From 6fb3ded2ba3bbc284477b1c2d78bf2a4bf201538 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 13 Oct 2021 14:19:13 +0200 Subject: [PATCH] =?UTF-8?q?[=F0=9F=A6=84=20=F0=9F=A6=84=20Uniswap=20Merge]?= =?UTF-8?q?=204.13.0=20(#1413)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ignore state/data/generated.ts * fix: run graphql:codegen on yarn build (#1933) * run graphql codegen on build * yarn.lock * remove ms.macro * chore(i18n): synchronize translations from crowdin [skip ci] * fix: error messages from walletconnect provider (#1943) * fix(position page): link to the token page of the explorer instead of the address page from the position page * feat: handle chain id in subgraph api (#1938) * build dynamic subgraph url based on chain id * reset api state (query cache) on chain id change * removed dependency on rtk-query/graphql * add error message * feat: add fee tier distribution badge (#1862) * integrate with The Graph and auto-select fee tier * restored * addressed some design feedback * add pulsing animation on feeAmount change * simplify fee tier title * adjust button radios * addressed some design feedback * log ReactGA events * ignore data while fetching * update to use new generated queries * remove deleted file * add usefeetierdistribution hook * invalidate cache and standardize the outlined card * added react ga * fix show options logic * address design feedback * show % select in minified view * updated merge error * chore(i18n): synchronize translations from crowdin [skip ci] * feat(pools): fetch pool tick data from uniswap v3 subgraph (#1932) * add hooks to fecth ticks data and active liquidity * cleanup * add polling interval * moved ms.macro types to dev deps * generate graphql schema on build * added @types/ms.macro * use clone deep * refactor: logs hook (#1941) * feat(logs): add an infrastructure for fetching logs in a declarative way * use the logs hook in the vote page, first pass * fix comment * bit of cleanup * unused imports * improve loading indicator on vote page * some testnet behavior improvements * fix loader state * loading state nit * show correct indexes * remove the unnecessary retry code * first pass at the slice * no throws * loading indicator should go away if not connected * use the logs slice for the logs hook state * style changes per cal's request * chore(i18n): synchronize translations from crowdin [skip ci] * various edits * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * fix: reduce call retries by specifying a block tag in multicall * update defaults (#1955) * fix: use a multicall that allows limiting gas of the individual calls (#1953) * fix: use a multicall that allows limiting gas of the individual calls * use the latest multicall addresses * fix: use an arbitrum multicall that returns a better block number * remove loading indicator from fee tier selection (#1954) * fix: invalidate subgraph cache with provideTags (#1962) * invalidate queries using tags * enhance generated api with provide tags * clean up * chore(i18n): synchronize translations from crowdin [skip ci] * fix: do not reverse the proposals list in place * no more `borderRadius` errors! (#1968) * fix(position image): increase the gas required constraint for the token URI * remove a lot of dead code (#1970) * refactor(devx): show a warning when a call fails due to insufficient gas allowed * chore: update yarn.lock * chore(i18n): synchronize translations from crowdin [skip ci] * feat(L2): network alert cards (optimism and arbitrum) (#1957) * design review for arbitrum UI * add optimism L2 UI * better gradients, remove v2 links/routes, some minor padding and margin fixes * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * fix l2 routing bug * fix last commit * fix(L2): routing fixes (#1980) * Revert "fix last commit" This reverts commit 9f5764aab31b7653f4046be00eae707460513d4f. * Revert "fix l2 routing bug" This reverts commit f6dea47907016a8ec820cf1b9b4a95e9f8e15be5. * drops routing changes for L2 * pool v2 switch networks message * chore(i18n): synchronize translations from crowdin [skip ci] * stabilize fee distribution in hook (#1979) * fix: enforce styled-components/macro usage and upgrade react-scripts (#1981) * upgrade react-scripts and enforce macro rule * Fix code style issues with ESLint Co-authored-by: Lint Action * chore(i18n): synchronize translations from crowdin [skip ci] * improve liquidity depth hooks and data fetching (#1989) * feat(pools): add liquidity chart range input primitives (#1990) * first iteration of liquidity chart primitives * add tickprocessed type * clean up * chore(i18n): synchronize translations from crowdin [skip ci] * chore: update the contribution guidelines (#1987) * chore: update the contribution guidelines * move * note about non-deterministic steps * little bit of separation * language nits, add accessibility * add reasoning * chore(i18n): synchronize translations from crowdin [skip ci] * Updating two public-facing URLs to docs (#1992) * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * feat: Add Optimism (#1923) * preliminary support * fix multicall abi add explorer support * Fix code style issues with ESLint * fix gas estimation * fix todo tests * fix gas estimation comments * anonymize links to kovan etherscan and poll more frequently on optimistic kovan * separate pool creation * remove supported chain id * remove the blocktag * do not use the blocktag on optimism only * give more gas to tokenURI for optimism * update sdk * temp fix to the block tag thing (do not update the block number from the fetch block number) * remove unused import * gasRequired -> multi-network * bump quoter gas limit to 6m on optimism * move the gas required parameter by chain id one level up * missed a hook * retry fetching receipts for optimism as many times as arbitrum * fix duplicate enum, add optimism as well to retry options config * fix: state not getting updated after a transaction confirmation * bump sdk version * update for mainnet optimism (#1998) * fix for calculateGasMargin on optimism Co-authored-by: Lint Action Co-authored-by: Moody Salem * chore(i18n): synchronize translations from crowdin [skip ci] * fix: simplify liqudity depth hooks and improve chart primitives (#1999) * updated hooks and chart primitives * add lodash.inrange to package.json * feat(pool): add liquidity distribution range slider and update add liquidity layout (#1829) * first iteration of useTicks and useActiveLiquidity * feat(pools): add liquidity depth chart (#1835) * cleanup * use area chart instead of bar chart * check for undefined rather than falsy * feat: range buttons based on fee amount (#1870) * range buttons based on fee amount * hardcode percentages as ticks * increase blocksperfect * feat: optimize add liquidity charts (#1880) * ignore syncing state * remove surrounding ticks * avoid processing price1 as it is unused * cleanup * feat: add zoom buttons to liquidity depth chart (#1882) * ignore syncing state * remove surrounding ticks * avoid processing price1 as it is unused * cleanup * first pass at +/- zoom buttons * remove console.log, cleanup * use real price for price line * updated brush handles to latest spec * added % labels to handles * round tick to nearest usable tick * first pass at brushable area chart with d3 * first pass at brushable area chart with d3 * rework * address PR comments * add brush handles * address PR comments * further improvements * feat(pools): improve full range support + capital efficiency warning (#1903) * handle min and max prices in add liquidity * cleaned up * use flag to denote full range * reset inputs on fee select * fixed merge conflict * handle full range in positions preview * fixed invalid range when tokens are reversed * use formatTickData * updated layout * cleaned up layout * fixed address * avoid re-rendering deposit amounts * added zoom behavior and more styling * renamed chart * renamed main chart file; * add brush tooltips * remove chart title * added accents to brush handles * more work * moved to file * modularize chart components * fix maximum depth * added brush labels * cleanup * cleanup * set up zoom * added new components * improved brush and zoom integration * cleaned up clip path * fixed clip paths * integrated with the graph changes * adjust fee selector * fix data error * add bar chart * polish * merged * clean up error * cleaned up after merge * visual improvements * moved +/- buttons to the right/left * removed margin bottom * removed unsused * fix brush labels % change * use d3.axisBottom * updated labels * improve brush range * fix one brush change only * adjust zoom and clippath * use bars * use area * adjust axis bottom to mocks; * improved bars * show bar colors * better handle full range * adjust colors for light mode * updated to mocks * adjusted handles for visibility * switch to area * add react ga events * adjusted to mocks * memo brush domain to avoid re-renders * fix inputstepcounter color * adjust handles * rely on the graph sorting tickidx * use curvestepafter * updated polish * merged main * add clamping and other fixes * highlight selected area using a mask * use price instead of % for labels * delete unused * refine ux * relayout * improve hooks * adjust layout for mobile * fixed card color * adjust padding * preent tick overflow * flip handles sooner * delete bars.tsx * chore(i18n): synchronize translations from crowdin [skip ci] * update multicall address * fix: min/max price inverted in positions page (#2002) * fix param ordering in inverter hook * use object param * fix: retry calls that return header not found errors (#2004) * fix: make liquidity chart and input step counter responsive (#2005) * make chart responsive * make input step counter text responsive * clean up chart * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(token lists): bump the token list spec version * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * support optimism and arbitrum subgraphs (#2001) * fix(L2): polish (#1988) * polish move getINfoLinkByChainId to chain constants pr review - translations optimism token list, typo, text color for L1 switch use instead of t undo unintentional tokenlist order change use {'Optimism'} instead of {name} switch deadline implementation remove unused TYPE import switch to generalized CHAIN_INFO instead of L2_INFO so we don't have to check in components add target chain id param to optimism bridge fix a minor breakpoint issue reduce sigfigs for header balance update network card dropdown text for optimism remove list code refactor SupportedChainId * SupportedL1ChainId | SupportedL2ChainId -> SupportedChainId * fix: price tokens on optimism mainnet using DAI * fix: remove dollar sign from translation of eth amount fixes https://github.com/Uniswap/uniswap-interface/issues/1986 * fix: add liquidity flow polish (#2017) * addressed feedback * set initial, min and max zoom levels * better handle 0 * avoid formatting range selector * polish `not created` state * remove unused import * fix: update CMC priority within lists (#2019) * fix(routing): updates routing constants for layer 2 networks to use ETH (#2018) * fix: properly set upper/lower base/quote on manual inversion (#2023) * load pool data for each tier to determine state (#2026) * compare with undefined rather than falsy (#2027) * fix: add liquidity ux polish (#2024) * zoom out on intiial * adjust initial zoom ranges * remove unneeded reactga event * adjust full range warning copy * update zoom * adjust zoom ranges and label around current price * fix(L2): use 10 bips slippage for swaps by default on L2 (#2035) * use 10bip slippage on L2 * update deps Co-authored-by: Jordan Frankfurt * add optional chaining for stakingInfo (#1950) * fix(L2): enable optimism token list when switched to optimism (#2036) * add title text and opacity variability to token lists * add optimism token list * show tokens from active lists * sort up token lists with tokens on the current chain * fix up some type issues prune out chainId changes * clean up leftover any * refactor token count mechanism * handle plurals in title text string * new combineMaps implementation * remove custom plural * address a couple nits * show the number of tokens on current chain Co-authored-by: Jordan Frankfurt Co-authored-by: Justin Domingue * fix overflow on mobile (#2037) * fix(L2): add downtime warning to add liquidity and adjust styles of network notification (#2034) * add liquidity warning and network notification * remove unneeded width * use externalLink in LinkOutToBridge component Co-authored-by: Moody Salem * fix: dismiss txn confirmation modal if on L2 (#2022) * dismiss txn confirmation modal if on l2 * line up far right margin; * remove submitted view on txns if on L2 * remove uneeded chainid * wrap dismissal in useEffect * update variable names * fix(L2): Design tweaks for L2 (Optimism) (#2038) Style tweaks to header layout, network dropdown and pool pages Co-authored-by: Jordan Frankfurt * fix: support for isolated pool creation (#2029) * revert to remove ui polish changes * left align blurb and fix bp issue * fix translation wrapper * remove preview screen for create, hide submitted txn UI for create, copy updates * small fixes * copy updates for create blurb * add set price range title * add translation * fix some translations Co-authored-by: Justin Domingue * update links (#2040) * chore: support stubbing subgraph in integration tests (#2039) * upgrade to 7.0 * first iteration of stubbing subgraph in integration tests * fix: remove x-axis clamping behavior in liquidity chart (#2042) * remove x-axis clamping behavior * remove extra curly braces around errorMessage * fix: revert remove x-axis clamping behavior in liquidity chart (#2045) * Revert "fix: remove x-axis clamping behavior in liquidity chart (#2042)" This reverts commit f86db00464448fe2c0e387aa01855f1710cd4fbd. * removed extra curly braces around errorMessage * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * fix(arbitrum): use infura endpoints * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * refactor: clean up some constants * fix: add DAI and WBTC and USDT to routing for Optimism fixes https://github.com/Uniswap/uniswap-interface/issues/2016 * fix(block number): link to the correct page in etherscan for optimism block number links * fix: update balance check in derived swap state (#2054) * update balance check in derived swap state * replace balance check with correct version of trade * remove duplicate `set price range` title when there is liquidity (#2058) * fix: add language selector in top-level menu (#2057) * initial language selector * set > icon opacity for consistency * always reset menu to main on close * always reset menu on menu open * fix: all supported chain ids should not include the string keys of the enum * chore: add one more unit test for ALL_SUPPORTED_CHAIN_IDS * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * fix: remove chart clamping and show indicator when handle is off screen (#2064) * initial off screen indicator * adjust offscreen indicator * add off screen handle indicator * hide reset until we get a better behavior * add svg.tsx * fix: change panning cursor to col-resize (#2071) * change panning cursor to col-resize * revert to grabby, and support grabbing * fix: set language using url param in language dropdown (#2063) * set language using url param in language dropdown * refactor common code into a hook * add hook * address pr feedback * chore(i18n): synchronize translations from crowdin [skip ci] * feat: only block on app url, mobile UI tweaks (#2073) * start iframe ui updates * replace hook with constant * add origin check for blocked lists * remove origin check for local unsupported list * remove redundant retun * remove iframe check * undo local change * remove unused dependencies (#2074) * increase zoom out range (#2076) * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * fix: invert selected range on RateToggle instead of clearing inputs (#2078) * Update WalletLink and @web3-react/walletlink-connector (#2079) * fix(swap): double max gas for quoting a v3 swap for large trades * set 5 min deadline on swaps and hide UI on L2 (#2081) * chore: add blurb about release process (#2061) * chore: add blurb about release process * additional standard * how to collect feedback * feat: routing api integration (#2080) * initial routing api integration * add routing api slice * display route in dialog * addressed pr feedback * switch to `get` * renamed useRouterTradeExactIn to useRouter * moving few files to later iteration * removed unnecessary `as` * switch to polling * add todo for blocknumber freshness * remove account-slippage-deadline * chore: copy edit on CONTRIBUTING.md * perf: list to token map function was doing too many reduces (#2095) * perf: remove extra spread operators from the combine maps function * feat: support connecting to a gnosis safe (#2096) * feat: support connecting to a gnosis safe * add the manifest.json attributes required by gnosis safe * remove background fill * copy * only try injected provider if not active after trying safe * add UniH to unsupported list (#2097) * fix: better support decimal overflow (#2075) * better support decimal overflow * improve tests * explicitly define num digits in toSignificant * add tests for 0 decimals * undo change to tryParseAmount and instead compute price without relying on parse * remove console log and add test * removed comment * addressed pr feedback * improve parsing logic * addressed pr feedback * update unsupported and broken lists (#2100) * fix: remove html ids and unnecessary keys from the menu * fix: prevent error if user selects ETH and WETH in add liquidity (#2112) * fix: prevent error if user selects ETH and WETH in add liquidity fixes https://github.com/Uniswap/uniswap-interface/issues/1763 * linting error * revert changes to Tokens.ts * test: set up component snapshot testing (#2102) * set up snapshot testing * improvements * add tests for TextInput as an example * Fix code style issues with ESLint * add comment to custom-test-env file * only set up needed providers * include style rules in snapshots * disable redux storage warning * added setupTests to avoid boilerplate Co-authored-by: Lint Action * Revert "feat(routing): support mirror protocol routing as additional bases (#1375)" This reverts commit 3198129af2eff07048eab2a9c041522495927e04. * Revert "feat(uma): uma call option routing (#1385)" This reverts commit 89d484d882e73ba6fe51a2cf66a893210cb0ce79. * feat: use modal for txn confirmation on l2 (#2055) * use modal for txn confirmation on l2 * update with error state * remove 1 translation * dedicated L2 component * update styling on to be less jumpy * add success animation to modal * revert regular submitted content * remove useless hook in popups * remove import Co-authored-by: Ian Lapham * chore(i18n): synchronize translations from crowdin [skip ci] * fix: toggle liquidity position when toggling rates (#2117) If field A and field B are already populated, chances are the user wants them to remain the same and only wants to invert the price range selector. The conversion isn't exact, but should be preferable to the previous behavior. * chore(i18n): synchronize translations from crowdin [skip ci] * fix: upgrade walletconnect to 1.5.1 (#2120) * upgrade walletconnect to 1.5.0 * bump again * document `useQueryCacheInvalidator` explains why `chainId` is pulled directly from the store. * Fix code style issues with ESLint * fix(L2): update downtime warning copy (#2114) * add link (#2113) * chore(i18n): synchronize translations from crowdin [skip ci] * fix: add support for full range positions in add liquidity (#2090) * remove arbitrary range buttons and move full range button * better align full range to deposit * support dragging range when in full range * hack to support full range brushing * restore zoom levels * adjusted for mocks * fix styling * simplify type * restore rate toggle change * fix lower bound by looking at isSorted * better align bottoms * add reset button for full range positions * only flip when not at limit * fix +/- buttons in range selector * add help link * chore(i18n): synchronize translations from crowdin [skip ci] * fix: upgrade ethers to allow constructing transaction data compatible with eip-1559 (#2121) * fix: upgrade ethers to allow constructing transaction data compatible with eip-1559 * bump the experimental provider * chore(i18n): synchronize translations from crowdin [skip ci] * fix: update mobile header to fix overflow (#2125) Co-authored-by: Matthew Quinn * fix: center import pool text V2 (#2122) * fix: center import pool text V2 * fix: updated based on feedback Co-authored-by: Matthew Quinn * update full range documentation link * Fix code style issues with ESLint * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * fix: optimism link and some copy changes (#2126) * fix: optimism link * fix: (un)planned downtime copy changes * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(deps): bump tar from 6.1.0 to 6.1.4 (#2132) Bumps [tar](https://github.com/npm/node-tar) from 6.1.0 to 6.1.4. - [Release notes](https://github.com/npm/node-tar/releases) - [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-tar/compare/v6.1.0...v6.1.4) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * upgrade to walletconnect 1.5.2 (#2134) * fix: rename weth9 to weth (#2133) * update @uniswap/sdk-core and @uniswap/v3-sdk * reorder * update unsupported list (#2143) Co-authored-by: Ian Lapham * chore(i18n): synchronize translations from crowdin [skip ci] * fix: avoid running BestV3Trade when tokens are the same (#2172) * avoid running BestV3Trade when tokens are the same * flipped condition * add ReactGA event for full range (#2181) * update chinese simplified and traditional display names (#2180) * fix: proposal descriptions should be pulled from the right governor (#2176) * fix: `#isTradeBetter` should check that output currencies are equal (#2177) * fix: isTradeBetter should check that input/output are equal * fix the fix * update blocked address list (#2182) Co-authored-by: Ian Lapham * fix: do not construct routes with the same pool twice (#2196) * chore: set `default_bump` to false per https://github.com/mathieudutour/github-tag-action/issues/85 * fix: use `@walletconnect/ethereum-provider` instead of `@walletconnect/web3-provider` (#2175) * use @walletconnect/ethereum-provider * bump to support client-side disconnect * chore(i18n): touch en-US to prevent lingui from adding a creation date (#2210) Fixes #2198. Touching en-US.po before extracting messages prevents lingui from adding a creation date, maintaining deterministic builds and IPFS hashes. * chore(i18n): do not compile i18n messages (#2211) * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore: fix SEO and accessibility nits for Lighthouse (#2212) * refactor: index.html * chore: preconnect to GA * chore(SEO): add meta content * chore(SEO): add alt to ethereum logo * chore(accessibility): add aria-labels to menus * chore(accessibility): mark AppBody as main * chore(accessibility): update nav link ids * chore(accessibility): set html.lang to match i18n locale * chore(refactor): mv html.lang to useEffect * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * chore(i18n): synchronize translations from crowdin [skip ci] * fix: update walletconnect for deep links and scalability (#2215) * chore: change language icon to globe (#2222) * chore: change language icon to globe * chore(accessibility): increase contrast for connections (#2223) * fix(governance): hot fix for `parseLog` crash * fix: proposal formatting * chore: rm unused styled-system (#2234) * chore: update multicodec usage (#2233) * chore: remove unused cross-env (#2232) * chore: rm redux-devtools-extension (#2230) redux-devtools-extension integration is already provided through @reduxjs/toolkit. * fix: walletconnect on L2 issues with Rainbow (#2242) * fix: remove the walletconnect bridge url * fix: use the latest ethereum provider which fixes the optimism rainbow issue * fix: turn service workers back on (#1944) reconfigure service workers to only cache used assets (excluding .po language files and non-.var.woff2 font files) * fix: turn service workers back on * chore: configure service worker caches * chore: add newline * Fix code style issues with ESLint * chore: limit service-worker caching Co-authored-by: Zach Pomerantz Co-authored-by: Lint Action * test: support stubbing subgraph data in cypress (#2259) * upgrade to 7.0 * first iteration of stubbing subgraph in integration tests * added fixtures * add tests for fee tier distribution and liquidity chart * remove unused test utils * update yarn.lock * fixed merge artifacts * update token list (#2270) * fix(governance): special case bravo parsing (#2247) * fix(governance): special case bravo parsing * fix(governance): explicitly parse U+2018, U+2026 * chore: upgrade ethers to ^5.4.6 Updates parseLog to delay parsing nested properties, so that they will only fail when accessed. * fix(vote): recover from invalid UTF-8 in proposal descriptions * fix(vote): special case bravo proposal newlines * chore: rm dead reference * add arbitrum token list (#2268) * Revert "test: support stubbing subgraph data in cypress (#2259)" (#2269) This reverts commit 00403a481034ad4a23fc454a818b5687237c246d. * fix: disable serviceworker in integration tests (#2274) * update explorer links for arbitrum (#2279) * chore(deps): bump tar from 6.1.4 to 6.1.11 (#2280) Bumps [tar](https://github.com/npm/node-tar) from 6.1.4 to 6.1.11. - [Release notes](https://github.com/npm/node-tar/releases) - [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-tar/compare/v6.1.4...v6.1.11) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * update optimism subgraph url (#2282) * fix: older browser suppport (#2228) * fix: Object.fromEntries polyfill * fix: update polyfills * fix: update polyfills * chore: upgrade type system (#2276) * chore: upgrade type system Upgrade types, typescript, and formatters. * fix: ListsState tests * use network-specific help center link (#2285) * style: enforce object-shorthand (#2286) * fix: make non-interactive header elements unselectable (#2288) * fix: always paint StyledPollingNumber (#2254) Adds a en-space to StyledPollingNumber so it is always painted. Fixes a layout shift when rendering the first block number. StyledPollingNumber is taller than its container, so waiting to paint it causes a layout shift when rendering. * chore: paint currency selector on initialization (#2245) Avoids a flash of "Select a token" as the App initializes. * feat: governor bravo support (#2271) * first pass * Fix code style issues with ESLint * address comments Co-authored-by: Lint Action * chore: use native flatMap (#2231) * chore(i18n): synchronize translations from crowdin [skip ci] * Squashed commit of the following: commit 5f1d1af62bcd47286aafacc18788f4e7e22dd7c0 Author: Aseem Sood Date: Fri Sep 3 12:12:39 2021 -0400 update readme tweak commit 80a5b95c0e0ae8934b5591c982077eaa813db747 Author: Noah Zinsmeister Date: Fri Sep 3 11:52:56 2021 -0400 consolidate and standardize unsupported tokens * yarn lock * merge type/style/conflict fixes * fix supported networks support * fix settings * test snapshots * don't ignore generated * move workflows to uni * path fix * path fix / walletmodal * Fix code style issues with Prettier * small fixes post merge * Fix code style issues with Prettier * pr comments 1. imports/exports * import path * pr comments * pr comments * cypress fix * remove projectId * fix overflow in managelist * style fix * style fixes * ci fix * remove CI: false from workflow * add new multicall xdai address (#1528) * set xdai polling to 1ms (#1529) * [Uni merge 🦄 ] replace uniswap text with cowswap (#1526) * replace uniswap text with cowswap * only show unsupported token tag * comment unused types/imports * [Uni merge 🦄 ] Switch network adaptation (#1501) * fix supported networks * lock file * style fixes * edit menu/header to better responsive feel * dead code in baseTheme * expand mediaWidths in cowswapTheme * remove styled base theme * responsive header for network switcher * Fix code style issues with Prettier * fix network connect on no connection * network switching UI/logic fix * pr comment fix chainId * fix disabled check * styling * style pt.2 - highlight in list selected network * yarn update * pr comments: - network card style * pr comments: - text change * pr comments - move menu items into menu at medium * pr comments - deal better with unsupported networks * non-connected - connect and switch Co-authored-by: Lint Action * WIP Uni Merge - styles fixes. (#1565) * Menu styles/icon fixes. * Fix code style issues with Prettier * Menu styles/icon fixes. * Fix code style issues with Prettier * Menu style fixes. * iOS menu fix. Co-authored-by: Lint Action Co-authored-by: Justin Domingue Co-authored-by: Crowdin Bot Co-authored-by: Moody Salem Co-authored-by: Moody Salem Co-authored-by: Ian Lapham Co-authored-by: Noah Zinsmeister Co-authored-by: Jordan Frankfurt Co-authored-by: Lint Action Co-authored-by: ChrisFox.eth <39571193+foxaweber@users.noreply.github.com> Co-authored-by: Luke Donato Co-authored-by: Callil Capuozzo Co-authored-by: Brendan Weinstein <65564422+brendanww@users.noreply.github.com> Co-authored-by: Ian Lapham Co-authored-by: Jeffrey Lin Co-authored-by: Matthew Quinn Co-authored-by: Matthew Quinn Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ian Lapham Co-authored-by: Zach Pomerantz Co-authored-by: circlehotarux Co-authored-by: Michel <31534717+biocom@users.noreply.github.com> --- .eslintrc.json | 16 +- .../workflows/integration-tests.yaml | 2 + .../uniswap-original/workflows/release.yaml | 1 + .gitignore | 4 +- .prettierignore | 1 + CONTRIBUTING.md | 7 +- README.md | 2 +- codegen.yml | 11 + custom-test-env.js | 16 + cypress-custom/integration/fee.test.ts | 8 +- cypress/fixtures/feeTierDistribution.json | 25 + cypress/integration/add-liquidity.test.ts | 37 + cypress/integration/landing.test.ts | 1 + cypress/plugins/index.js | 22 + cypress/support/commands.js | 2 +- cypress/utils/graphql-test-utils.ts | 12 + package.json | 39 +- public/images/256x256_App_Icon_Pink.svg | 11 + public/index.html | 7 +- src/abis/governor-bravo.json | 1046 ++++ src/assets/styles/styled.ts | 2 +- src/assets/svg/arbitrum_logo.svg | 181 + src/assets/svg/optimism_logo.svg | 5 + src/components/AccountDetails/Copy.tsx | 48 + src/components/AccountDetails/Transaction.tsx | 5 +- src/components/AccountDetails/index.tsx | 7 +- src/components/AddressInputPanel/index.tsx | 21 +- src/components/Badge/RangeBadge.tsx | 9 - src/components/Badge/index.tsx | 4 +- src/components/Blocklist/index.tsx | 3 +- src/components/Button/index.tsx | 109 +- src/components/Card/index.tsx | 14 +- src/components/Confetti/index.tsx | 1 - .../CurrencyInputPanel/FiatValue.tsx | 2 +- src/components/CurrencyInputPanel/index.tsx | 12 +- src/components/CurrencyLogo/index.tsx | 8 +- src/components/DoubleLogo/index.tsx | 1 - src/components/ErrorBoundary/index.tsx | 4 +- src/components/FeeSelector/index.tsx | 251 +- .../FormattedCurrencyAmount/index.tsx | 1 - src/components/Header/NetworkCard.tsx | 235 + src/components/Header/Polling.tsx | 9 +- src/components/Header/URLWarning.tsx | 3 +- src/components/Header/UniBalanceContent.tsx | 17 +- src/components/Header/index.tsx | 181 +- src/components/HoverInlineText/index.tsx | 4 +- src/components/Identicon/index.tsx | 2 +- .../InputStepCounter/InputStepCounter.tsx | 103 +- src/components/LineChart/index.tsx | 6 +- .../LiquidityChartRangeInput/Area.tsx | 44 + .../LiquidityChartRangeInput/AxisBottom.tsx | 43 + .../LiquidityChartRangeInput/Brush.tsx | 265 + .../LiquidityChartRangeInput/Chart.tsx | 146 + .../LiquidityChartRangeInput/Line.tsx | 24 + .../LiquidityChartRangeInput/Zoom.tsx | 130 + .../LiquidityChartRangeInput/hooks.ts | 56 + .../LiquidityChartRangeInput/index.tsx | 208 + .../LiquidityChartRangeInput/svg.tsx | 61 + .../LiquidityChartRangeInput/types.ts | 61 + src/components/ListLogo/index.tsx | 1 - src/components/Loader/index.tsx | 4 +- src/components/Logo/index.tsx | 2 +- src/components/Menu/index.tsx | 224 +- src/components/Modal/index.tsx | 7 +- src/components/ModalViews/index.tsx | 4 +- src/components/NavigationTabs/index.tsx | 46 +- .../NetworkAlert/AddLiquidityNetworkAlert.tsx | 134 + .../NetworkAlert/MinimalNetworkAlert.tsx | 134 + src/components/NetworkAlert/NetworkAlert.tsx | 169 + src/components/NetworkAlert/styles.ts | 7 + src/components/NumericalInput/index.tsx | 6 +- .../OptimismDowntimeWarning/index.tsx | 63 + src/components/Popover/index.tsx | 2 +- src/components/Popups/ClaimPopup.tsx | 6 +- src/components/Popups/ListUpdatePopup.tsx | 2 +- src/components/Popups/PopupItem.tsx | 4 +- src/components/Popups/TransactionPopup.tsx | 4 +- src/components/Popups/index.tsx | 22 +- src/components/PositionCard/Sushi.tsx | 3 +- src/components/PositionCard/V2.tsx | 8 +- src/components/PositionCard/index.tsx | 23 +- src/components/PositionList/index.tsx | 6 +- src/components/PositionListItem/index.tsx | 51 +- src/components/PositionPreview/index.tsx | 20 +- src/components/ProgressSteps/index.tsx | 4 +- src/components/QuestionHelper/index.tsx | 2 +- .../RangeSelector/PresetsButtons.tsx | 34 + src/components/RangeSelector/index.tsx | 63 +- src/components/RateToggle/index.tsx | 13 +- src/components/SearchModal/CommonBases.tsx | 11 +- src/components/SearchModal/CurrencyList.tsx | 29 +- src/components/SearchModal/CurrencySearch.tsx | 11 +- .../SearchModal/CurrencySearchModal.tsx | 10 +- src/components/SearchModal/ImportList.tsx | 4 +- src/components/SearchModal/ImportRow.tsx | 2 +- src/components/SearchModal/ImportToken.tsx | 8 +- src/components/SearchModal/Manage.tsx | 2 +- src/components/SearchModal/ManageLists.tsx | 102 +- src/components/SearchModal/ManageTokens.tsx | 2 +- src/components/SearchModal/SortButton.tsx | 1 - src/components/SearchModal/styleds.tsx | 43 +- src/components/Settings/index.tsx | 6 +- src/components/Slider/index.tsx | 2 +- src/components/SwitchLocaleLink/index.tsx | 64 +- .../__snapshots__/index.test.tsx.snap | 130 + src/components/TextInput/index.test.tsx | 68 + src/components/TextInput/index.tsx | 146 + src/components/ThemeColorPalette/index.tsx | 2 +- src/components/Toggle/ListToggle.tsx | 1 - src/components/Toggle/index.tsx | 47 +- src/components/TokenWarningModal/index.tsx | 1 - src/components/Tooltip/index.tsx | 2 +- .../AnimatedConfirmation.tsx | 70 + .../TransactionConfirmationModal/index.tsx | 131 +- src/components/TransactionSettings/index.tsx | 73 +- src/components/WalletModal/Option.tsx | 1 - src/components/WalletModal/PendingView.tsx | 5 +- src/components/WalletModal/index.tsx | 6 +- src/components/Web3ReactManager/index.tsx | 2 +- src/components/Web3Status/index.tsx | 13 +- src/components/claim/AddressClaimModal.tsx | 4 +- src/components/claim/ClaimModal.tsx | 4 +- src/components/earn/ClaimRewardModal.tsx | 2 +- src/components/earn/PoolCard.tsx | 5 +- src/components/earn/StakingModal.tsx | 2 +- src/components/earn/UnstakingModal.tsx | 2 +- src/components/earn/styled.ts | 11 - src/components/swap/AdvancedSwapDetails.tsx | 6 +- .../swap/AdvancedSwapDetailsDropdown.tsx | 12 +- src/components/swap/BetterTradeLink.tsx | 2 +- src/components/swap/ConfirmSwapModal.tsx | 2 +- src/components/swap/FormattedPriceImpact.tsx | 11 +- src/components/swap/SwapHeader.tsx | 1 - src/components/swap/SwapModalFooter.tsx | 2 +- src/components/swap/SwapModalHeader.tsx | 10 +- src/components/swap/SwapRoute.tsx | 4 +- src/components/swap/TradePrice.tsx | 6 +- .../swap/UnsupportedCurrencyFooter.tsx | 4 +- src/components/swap/styleds.tsx | 75 +- src/components/vote/DelegateModal.tsx | 4 +- src/components/vote/ProposalEmptyState.tsx | 60 + src/components/vote/VoteModal.tsx | 37 +- src/connectors/index.ts | 40 +- src/constants/addresses.ts | 100 +- src/constants/chains.test.ts | 30 + src/constants/chains.ts | 126 +- src/constants/governance.ts | 22 +- src/constants/lists.ts | 39 +- src/constants/locales.ts | 7 +- src/constants/misc.ts | 9 +- src/constants/proposals/index.ts | 1 + src/constants/routing.ts | 100 +- .../tokenLists/broken.tokenlist.json | 22 + .../uniswap-v2-unsupported.tokenlist.json | 30 - .../tokenLists/unsupported.tokenlist.json | 1078 ++++ src/constants/tokens.ts | 159 +- src/custom/api/matcha-0x/index.ts | 2 - src/custom/api/paraswap/index.ts | 1 - src/custom/assets/cow-swap/discord.svg | 3 + src/custom/assets/cow-swap/twitter.svg | 4 +- .../AccountDetails/AccountDetailsMod.tsx | 2 +- .../AccountDetails/Transaction/index.tsx | 2 +- .../AccountDetails/Transaction/styled.ts | 2 +- .../AccountDetails/TransactionMod.tsx | 3 +- .../components/AccountDetails/index.tsx | 2 +- .../components/AccountDetails/styled.ts | 45 +- src/custom/components/AppziButton/index.tsx | 3 +- .../components/ArrowWrapperLoader/index.tsx | 3 +- src/custom/components/Button/ButtonMod.tsx | 113 +- src/custom/components/Button/index.tsx | 12 +- src/custom/components/ClickWrap/index.tsx | 3 +- src/custom/components/Copy/CopyMod.tsx | 2 - .../CurrencyInputPanelMod.tsx | 14 +- .../FiatValue/FiatValueMod.tsx | 2 +- .../components/CurrencyInputPanel/index.tsx | 4 +- .../CurrencyLogo/CurrencyLogoMod.tsx | 6 +- .../ErrorBoundary/ErrorBoundaryMod.tsx | 4 +- src/custom/components/ExplorerLink/index.tsx | 1 - src/custom/components/Footer/index.tsx | 3 +- src/custom/components/Header/HeaderMod.tsx | 224 +- .../Header/NetworkCard/NetworkCardMod.tsx | 340 ++ .../components/Header/NetworkCard/index.ts | 4 + .../components/Header/Polling/PollingMod.tsx | 40 +- .../components/Header/Polling/index.tsx | 3 +- .../Header/URLWarning/URLWarningMod.tsx | 3 +- .../components/Header/URLWarning/index.tsx | 4 +- src/custom/components/Header/index.tsx | 201 +- .../components/HighFeeWarning/index.tsx | 2 +- src/custom/components/Link/index.tsx | 1 - src/custom/components/Markdown/index.tsx | 4 +- src/custom/components/Markdown/renderers.tsx | 2 +- src/custom/components/Menu/MenuMod.tsx | 201 +- src/custom/components/Menu/index.tsx | 172 +- src/custom/components/Modal/index.ts | 2 +- .../components/NotificationBanner/index.tsx | 2 +- src/custom/components/OrdersPanel/index.tsx | 2 +- src/custom/components/Page/index.tsx | 4 +- src/custom/components/Pill/index.tsx | 2 +- src/custom/components/Popover/PopoverMod.tsx | 2 +- src/custom/components/Popover/index.tsx | 1 - .../components/Popups/ListUpdatePopup.tsx | 4 +- .../components/Popups/ListUpdatePopupMod.tsx | 2 +- src/custom/components/Popups/PopupItem.tsx | 3 +- src/custom/components/Popups/PopupItemMod.tsx | 4 +- src/custom/components/Popups/PopupsMod.tsx | 22 +- .../components/Popups/TransactionPopupMod.tsx | 8 +- .../ProgressSteps/ProgressStepsMod.tsx | 76 - src/custom/components/ProgressSteps/index.tsx | 12 - .../QuestionHelper/QuestionHelperMod.tsx | 2 +- .../components/QuestionHelper/index.tsx | 1 - src/custom/components/ScrollToTop/index.tsx | 4 +- .../CommonBases/CommonBasesMod.tsx | 8 +- .../SearchModal/CommonBases/index.ts | 6 + .../CurrencyList/CurrencyListMod.tsx | 20 +- .../SearchModal/CurrencyList/index.tsx | 13 +- .../CurrencySearch/CurrencySearchMod.tsx | 6 +- .../SearchModal/CurrencySearch/index.tsx | 3 +- .../SearchModal/ImportList/ImportListMod.tsx | 8 +- .../SearchModal/ImportList/index.tsx | 2 +- .../SearchModal/ImportRow/ImportRowMod.tsx | 2 +- .../SearchModal/ImportRow/index.tsx | 2 +- .../ImportToken/ImportTokenMod.tsx | 6 +- .../SearchModal/ImportToken/index.tsx | 5 +- .../SearchModal/Manage/ManageMod.tsx | 2 +- .../components/SearchModal/Manage/index.tsx | 2 +- .../ManageLists/ManageListsMod.tsx | 99 +- .../SearchModal/ManageLists/index.tsx | 4 +- .../ManageTokens/ManageTokensMod.tsx | 2 +- .../SearchModal/ManageTokens/index.tsx | 1 - .../components/Settings/SettingsMod.tsx | 18 +- src/custom/components/Settings/index.tsx | 4 +- .../SwitchLocaleLink/SwitchLocaleLinkMod.tsx | 50 + .../components/SwitchLocaleLink/index.tsx | 5 + src/custom/components/Toggle/index.tsx | 3 +- src/custom/components/Tooltip/TooltipMod.tsx | 2 +- .../TransactionConfirmationModalMod.tsx | 156 +- .../TransactionConfirmationModal/index.tsx | 4 +- .../TransactionSettingsMod.tsx | 78 +- .../components/TransactionSettings/index.tsx | 3 +- src/custom/components/Version/index.tsx | 3 +- .../WalletModal/Option/OptionMod.tsx | 1 - .../components/WalletModal/Option/index.tsx | 3 +- .../components/WalletModal/WalletModalMod.tsx | 2 +- src/custom/components/WalletModal/index.tsx | 3 +- .../components/Web3Status/Web3StatusMod.tsx | 5 +- src/custom/components/Web3Status/index.tsx | 35 +- .../AdvancedSwapDetailsMod.tsx | 2 +- .../AdvancedSwapDetailsDropdownMod.tsx | 1 - .../ConfirmSwapModal/ConfirmSwapModalMod.tsx | 2 +- .../swap/ConfirmSwapModal/index.tsx | 1 - .../components/swap/EthWethWrap/WrapCard.tsx | 3 +- .../EthWethWrap/WrappingVisualisation.tsx | 1 - .../components/swap/EthWethWrap/index.tsx | 4 +- .../components/swap/FeeInformationTooltip.tsx | 4 +- .../SwapModalFooter/SwapModalFooterMod.tsx | 3 +- .../components/swap/SwapModalFooter/index.tsx | 9 +- .../SwapModalHeader/SwapModalHeaderMod.tsx | 5 +- .../components/swap/SwapModalHeader/index.tsx | 3 +- .../swap/TradePrice/TradePriceMod.tsx | 4 +- .../components/swap/TradePrice/index.tsx | 2 +- .../components/swap/TradeSummary/RowFee.tsx | 6 +- .../TradeSummary/RowReceivedAfterSlippage.tsx | 4 +- .../swap/TradeSummary/RowSlippage.tsx | 4 +- .../components/swap/TradeSummary/index.tsx | 3 +- .../UnsupportedCurrencyFooterMod.tsx | 41 +- .../swap/UnsupportedCurrencyFooter/index.tsx | 1 - src/custom/connectors/index.ts | 2 + src/custom/constants/addresses.ts | 6 +- src/custom/constants/chains/chainsMod.ts | 100 +- src/custom/constants/index.ts | 13 +- src/custom/constants/lists.ts | 38 +- src/custom/constants/routing/routingMod.ts | 123 +- src/custom/constants/tokens/tokensMod.ts | 122 +- src/custom/hooks/useApproveCallback.ts | 2 +- src/custom/hooks/useContract.ts | 4 +- src/custom/hooks/useLast/index.ts | 15 + src/custom/hooks/useUSDCPrice/index.ts | 50 +- src/custom/i18n.tsx | 2 +- src/custom/pages/About/index.tsx | 3 +- src/custom/pages/App/AppMod.tsx | 158 +- src/custom/pages/App/index.tsx | 3 +- src/custom/pages/AppBody/index.tsx | 1 - src/custom/pages/CookiePolicy/index.tsx | 3 +- src/custom/pages/CowGame/index.tsx | 3 +- src/custom/pages/Faq/index.tsx | 4 +- src/custom/pages/NotFound/index.tsx | 3 +- src/custom/pages/PrivacyPolicy/index.tsx | 3 +- src/custom/pages/Profile/index.tsx | 1 - src/custom/pages/Profile/styled.tsx | 2 +- src/custom/pages/Rewards/index.tsx | 3 +- src/custom/pages/Swap/SwapMod.tsx | 87 +- src/custom/pages/Swap/index.tsx | 16 +- src/custom/pages/Swap/styleds.tsx | 2 +- src/custom/pages/TermsAndConditions/index.tsx | 1 - src/custom/state/index.ts | 33 +- src/custom/state/lists/hooks/hooksMod.ts | 168 +- .../state/lists/reducer/reducerMod.test.ts | 15 - src/custom/state/lists/reducer/reducerMod.ts | 30 +- src/custom/state/lists/updater/updaterMod.ts | 31 +- src/custom/state/orders/helpers.tsx | 3 +- src/custom/state/price/hooks.ts | 7 +- src/custom/state/swap/hooks.ts | 56 +- src/custom/state/user/hooks/index.ts | 10 + src/custom/theme/baseTheme.tsx | 24 +- src/custom/theme/cowSwapTheme.tsx | 31 +- src/custom/theme/styled.d.ts | 9 +- src/custom/utils/getLibrary.ts | 31 + src/custom/utils/index.ts | 6 +- src/hooks/Tokens.ts | 8 +- src/hooks/useAllCurrencyCombinations.ts | 5 +- src/hooks/useAllV3Routes.ts | 14 +- src/hooks/useApproveCallback.ts | 17 +- src/hooks/useBestV3Trade.ts | 34 +- src/hooks/useColor.ts | 2 +- src/hooks/useContract.ts | 61 +- src/hooks/useERC20Permit.ts | 8 +- src/hooks/useFeeTierDistribution.ts | 170 + src/hooks/useIsSwapUnsupported.ts | 2 +- src/hooks/useIsTickAtLimit.ts | 23 + src/hooks/useLast.ts | 12 - src/hooks/useLocationLinkProps.ts | 37 + src/hooks/usePoolTickData.ts | 135 + src/hooks/usePools.ts | 2 +- src/hooks/usePositionTokenURI.ts | 5 +- src/hooks/useRouter.ts | 30 + src/hooks/useSocksBalance.ts | 25 +- src/hooks/useSwapCallback.ts | 14 +- src/hooks/useSwapSlippageTolerance.ts | 10 +- src/hooks/useTheme.ts | 2 +- src/hooks/useTokenAllowance.ts | 3 +- src/hooks/useTransactionDeadline.ts | 7 +- src/hooks/useUSDCPrice.ts | 6 +- src/hooks/useV2Pairs.ts | 2 +- src/hooks/useWrapCallback.ts | 10 +- src/hooks/web3.ts | 35 +- src/i18n.tsx | 5 +- src/index.tsx | 59 +- src/locales/af-ZA.po | 437 +- src/locales/ar-SA.po | 437 +- src/locales/ca-ES.po | 437 +- src/locales/cs-CZ.po | 437 +- src/locales/da-DK.po | 437 +- src/locales/de-DE.po | 639 ++- src/locales/el-GR.po | 437 +- src/locales/es-ES.po | 437 +- src/locales/fi-FI.po | 437 +- src/locales/fr-FR.po | 449 +- src/locales/he-IL.po | 437 +- src/locales/hu-HU.po | 437 +- src/locales/id-ID.po | 437 +- src/locales/it-IT.po | 437 +- src/locales/ja-JP.po | 495 +- src/locales/ko-KR.po | 437 +- src/locales/nl-NL.po | 437 +- src/locales/no-NO.po | 437 +- src/locales/pl-PL.po | 471 +- src/locales/pt-BR.po | 437 +- src/locales/pt-PT.po | 437 +- src/locales/ro-RO.po | 440 +- src/locales/ru-RU.po | 445 +- src/locales/sr-SP.po | 437 +- src/locales/sv-SE.po | 437 +- src/locales/tr-TR.po | 437 +- src/locales/uk-UA.po | 437 +- src/locales/vi-VN.po | 437 +- src/locales/zh-CN.po | 499 +- src/locales/zh-TW.po | 461 +- src/pages/AddLiquidity/PoolPriceBar.tsx | 52 - src/pages/AddLiquidity/Review.tsx | 26 +- src/pages/AddLiquidity/index.tsx | 954 ++-- src/pages/AddLiquidity/redirects.tsx | 3 +- src/pages/AddLiquidity/styled.tsx | 105 +- .../AddLiquidityV2/ConfirmAddModalBottom.tsx | 3 +- src/pages/AddLiquidityV2/PoolPriceBar.tsx | 4 +- src/pages/AddLiquidityV2/index.tsx | 14 +- src/pages/AddLiquidityV2/redirects.tsx | 1 - src/pages/App.tsx | 54 +- src/pages/AppBody.tsx | 5 +- .../CreateProposal/ProposalActionDetail.tsx | 83 + .../CreateProposal/ProposalActionSelector.tsx | 129 + src/pages/CreateProposal/ProposalEditor.tsx | 63 + .../ProposalSubmissionModal.tsx | 57 + src/pages/CreateProposal/index.tsx | 286 ++ src/pages/Earn/Countdown.tsx | 2 +- src/pages/Earn/Manage.tsx | 14 +- src/pages/Earn/index.tsx | 1 - src/pages/MigrateV2/MigrateV2Pair.tsx | 23 +- src/pages/MigrateV2/index.tsx | 6 +- src/pages/Pool/CTACards.tsx | 118 +- src/pages/Pool/PositionPage.tsx | 151 +- src/pages/Pool/index.tsx | 223 +- src/pages/Pool/styleds.tsx | 2 +- src/pages/Pool/v2.tsx | 201 +- src/pages/PoolFinder/index.tsx | 4 +- src/pages/RemoveLiquidity/V3.tsx | 28 +- src/pages/RemoveLiquidity/index.tsx | 16 +- src/pages/Swap/index.tsx | 25 +- src/pages/Swap/redirects.tsx | 2 +- src/pages/Vote/VotePage.tsx | 37 +- src/pages/Vote/index.tsx | 160 +- src/pages/Vote/styled.tsx | 52 +- src/pages/styled.tsx | 11 + src/polyfills.ts | 1 + src/service-worker.ts | 95 +- src/setupTests.ts | 5 + src/state/application/actions.ts | 2 + src/state/application/hooks.ts | 10 - src/state/application/reducer.test.ts | 20 +- src/state/application/reducer.ts | 8 + src/state/application/updater.ts | 29 +- src/state/burn/reducer.ts | 2 +- src/state/burn/v3/reducer.ts | 2 +- src/state/claim/hooks.ts | 4 +- src/state/data/enhanced.ts | 19 + src/state/data/generated.ts | 4317 +++++++++++++++++ src/state/data/slice.ts | 110 + src/state/governance/hooks.ts | 448 +- src/state/index.ts | 16 +- src/state/lists/actions.ts | 3 +- src/state/lists/hooks.ts | 75 +- src/state/lists/reducer.ts | 18 +- src/state/lists/updater.ts | 22 +- src/state/logs/hooks.ts | 76 + src/state/logs/slice.ts | 87 + src/state/logs/updater.ts | 66 + src/state/logs/utils.ts | 38 + src/state/mint/v3/actions.ts | 1 + src/state/mint/v3/hooks.ts | 66 +- src/state/mint/v3/reducer.ts | 18 +- src/state/mint/v3/utils.test.ts | 39 + src/state/mint/v3/utils.ts | 34 +- src/state/multicall/actions.test.ts | 24 +- src/state/multicall/actions.ts | 44 +- src/state/multicall/hooks.ts | 48 +- src/state/multicall/reducer.test.ts | 4 + src/state/multicall/reducer.ts | 47 +- src/state/multicall/updater.tsx | 84 +- src/state/multicall/utils.ts | 28 + src/state/routing/slice.ts | 54 + src/state/stake/hooks.ts | 39 +- src/state/swap/hooks.ts | 16 +- src/state/swap/reducer.ts | 14 +- src/state/transactions/hooks.tsx | 18 + src/state/transactions/updater.tsx | 6 +- src/state/user/actions.ts | 5 +- src/state/user/hooks.tsx | 46 +- src/state/user/reducer.ts | 8 + src/state/user/updater.tsx | 1 - src/state/wallet/hooks.ts | 19 +- src/test-utils.tsx | 19 + src/theme/RadialGradientByChainUpdater.ts | 56 + src/theme/components.tsx | 65 +- src/theme/index.tsx | 10 +- src/theme/{styled.ts => styled.d.ts} | 15 +- src/utils/anonymizeLink.test.ts | 41 + src/utils/anonymizeLink.ts | 32 + src/utils/calculateGasMargin.test.ts | 9 +- src/utils/calculateGasMargin.ts | 9 +- src/utils/computeSurroundingTicks.test.ts | 56 + src/utils/computeSurroundingTicks.ts | 59 + src/utils/constructSameAddressMap.ts | 26 +- src/utils/contenthashToUri.ts | 14 +- src/utils/formatTickPrice.ts | 20 + src/utils/getExplorerLink.ts | 27 +- src/utils/getLibrary.ts | 17 +- src/utils/isTradeBetter.ts | 2 +- src/utils/switchToNetwork.ts | 21 + src/utils/unwrappedToken.ts | 2 +- yarn.lock | 3589 +++++++++++--- 469 files changed, 31318 insertions(+), 7209 deletions(-) create mode 100644 codegen.yml create mode 100644 custom-test-env.js create mode 100644 cypress/fixtures/feeTierDistribution.json create mode 100644 cypress/plugins/index.js create mode 100644 cypress/utils/graphql-test-utils.ts create mode 100644 public/images/256x256_App_Icon_Pink.svg create mode 100644 src/abis/governor-bravo.json create mode 100644 src/assets/svg/arbitrum_logo.svg create mode 100644 src/assets/svg/optimism_logo.svg create mode 100644 src/components/AccountDetails/Copy.tsx create mode 100644 src/components/Header/NetworkCard.tsx create mode 100644 src/components/LiquidityChartRangeInput/Area.tsx create mode 100644 src/components/LiquidityChartRangeInput/AxisBottom.tsx create mode 100644 src/components/LiquidityChartRangeInput/Brush.tsx create mode 100644 src/components/LiquidityChartRangeInput/Chart.tsx create mode 100644 src/components/LiquidityChartRangeInput/Line.tsx create mode 100644 src/components/LiquidityChartRangeInput/Zoom.tsx create mode 100644 src/components/LiquidityChartRangeInput/hooks.ts create mode 100644 src/components/LiquidityChartRangeInput/index.tsx create mode 100644 src/components/LiquidityChartRangeInput/svg.tsx create mode 100644 src/components/LiquidityChartRangeInput/types.ts create mode 100644 src/components/NetworkAlert/AddLiquidityNetworkAlert.tsx create mode 100644 src/components/NetworkAlert/MinimalNetworkAlert.tsx create mode 100644 src/components/NetworkAlert/NetworkAlert.tsx create mode 100644 src/components/NetworkAlert/styles.ts create mode 100644 src/components/OptimismDowntimeWarning/index.tsx create mode 100644 src/components/RangeSelector/PresetsButtons.tsx create mode 100644 src/components/TextInput/__snapshots__/index.test.tsx.snap create mode 100644 src/components/TextInput/index.test.tsx create mode 100644 src/components/TextInput/index.tsx create mode 100644 src/components/TransactionConfirmationModal/AnimatedConfirmation.tsx create mode 100644 src/components/vote/ProposalEmptyState.tsx create mode 100644 src/constants/chains.test.ts create mode 100644 src/constants/tokenLists/broken.tokenlist.json delete mode 100644 src/constants/tokenLists/uniswap-v2-unsupported.tokenlist.json create mode 100644 src/constants/tokenLists/unsupported.tokenlist.json create mode 100644 src/custom/assets/cow-swap/discord.svg create mode 100644 src/custom/components/Header/NetworkCard/NetworkCardMod.tsx create mode 100644 src/custom/components/Header/NetworkCard/index.ts delete mode 100644 src/custom/components/ProgressSteps/ProgressStepsMod.tsx delete mode 100644 src/custom/components/ProgressSteps/index.tsx create mode 100644 src/custom/components/SwitchLocaleLink/SwitchLocaleLinkMod.tsx create mode 100644 src/custom/components/SwitchLocaleLink/index.tsx create mode 100644 src/custom/hooks/useLast/index.ts create mode 100644 src/custom/state/user/hooks/index.ts create mode 100644 src/custom/utils/getLibrary.ts create mode 100644 src/hooks/useFeeTierDistribution.ts create mode 100644 src/hooks/useIsTickAtLimit.ts create mode 100644 src/hooks/useLocationLinkProps.ts create mode 100644 src/hooks/usePoolTickData.ts create mode 100644 src/hooks/useRouter.ts delete mode 100644 src/pages/AddLiquidity/PoolPriceBar.tsx create mode 100644 src/pages/CreateProposal/ProposalActionDetail.tsx create mode 100644 src/pages/CreateProposal/ProposalActionSelector.tsx create mode 100644 src/pages/CreateProposal/ProposalEditor.tsx create mode 100644 src/pages/CreateProposal/ProposalSubmissionModal.tsx create mode 100644 src/pages/CreateProposal/index.tsx create mode 100644 src/polyfills.ts create mode 100644 src/setupTests.ts create mode 100644 src/state/data/enhanced.ts create mode 100644 src/state/data/generated.ts create mode 100644 src/state/data/slice.ts create mode 100644 src/state/logs/hooks.ts create mode 100644 src/state/logs/slice.ts create mode 100644 src/state/logs/updater.ts create mode 100644 src/state/logs/utils.ts create mode 100644 src/state/mint/v3/utils.test.ts create mode 100644 src/state/multicall/utils.ts create mode 100644 src/state/routing/slice.ts create mode 100644 src/test-utils.tsx create mode 100644 src/theme/RadialGradientByChainUpdater.ts rename src/theme/{styled.ts => styled.d.ts} (86%) create mode 100644 src/utils/anonymizeLink.test.ts create mode 100644 src/utils/anonymizeLink.ts create mode 100644 src/utils/computeSurroundingTicks.test.ts create mode 100644 src/utils/computeSurroundingTicks.ts create mode 100644 src/utils/formatTickPrice.ts create mode 100644 src/utils/switchToNetwork.ts diff --git a/.eslintrc.json b/.eslintrc.json index aca554345..59e0c42eb 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -28,6 +28,20 @@ "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-ignore": "off", - "@typescript-eslint/explicit-module-boundary-types": "off" + "@typescript-eslint/explicit-module-boundary-types": "off", + "react/react-in-jsx-scope": "off", + "object-shorthand": ["error", "always"], + "no-restricted-imports": [ + "error", + { + "paths": [ + { + "name": "styled-components", + "message": "Please import from styled-components/macro." + } + ], + "patterns": ["!styled-components/macro"] + } + ] } } diff --git a/.github/uniswap-original/workflows/integration-tests.yaml b/.github/uniswap-original/workflows/integration-tests.yaml index 85c3e89a8..5a02fdb51 100644 --- a/.github/uniswap-original/workflows/integration-tests.yaml +++ b/.github/uniswap-original/workflows/integration-tests.yaml @@ -42,9 +42,11 @@ jobs: env: CI: false REACT_APP_NETWORK_URL: "https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847" + REACT_APP_SERVICE_WORKER: false - run: yarn integration-test env: CYPRESS_INTEGRATION_TEST_PRIVATE_KEY: ${{ secrets.CYPRESS_INTEGRATION_TEST_PRIVATE_KEY }} + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} diff --git a/.github/uniswap-original/workflows/release.yaml b/.github/uniswap-original/workflows/release.yaml index 403bf40f6..e31ff9ceb 100644 --- a/.github/uniswap-original/workflows/release.yaml +++ b/.github/uniswap-original/workflows/release.yaml @@ -23,6 +23,7 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} release_branches: .* + default_bump: false create_release: name: Create Release diff --git a/.gitignore b/.gitignore index 94898b4af..22d76ca02 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ /src/locales/**/*.ts /src/locales/**/*.json /src/locales/**/en-US.po +# UNI ignores this but we may need it for CI +# /src/state/data/generated.ts # dependencies /node_modules @@ -45,4 +47,4 @@ cypress-custom/screenshots cypress-custom/fixtures/example.json .yalc* -yalc.lock \ No newline at end of file +yalc.lock diff --git a/.prettierignore b/.prettierignore index 0088871b4..33362b9c2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ # generated stuff +/src/state/data/generated.ts build .git .github diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba854ec80..a6e0d58d5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,8 +40,7 @@ CowSwap uses [Crowdin](https://crowdin.com/project/cowswap) for managing translations. Whenever a new string is added to the project, it gets uploaded to Crowdin for translation by [this workflow](./.github/workflows/crowdin.yaml). -Every hour, translations are synced from Crowdin in [this other workflow](./.github/workflows/crowdin-sync.yaml). +You can contribute by joining Crowdin to proofread existing translations [here](https://crowdin.com/project/uniswap-interface/invite?d=93i5n413q403t4g473p443o4c3t2g3s21343u2c3n403l4b3v2735353i4g4k4l4g453j4g4o4j4e4k4b323l4a3h463s4g453q443m4e3t2b303s2a35353l403o443v293e303k4g4n4r4g483i4g4r4j4e4o473i5n4a3t463t4o4) -You can contribute by joining Crowdin to proofread existing translations [here](TODO: enter crowdin url) - -Or, ask to join us as a translator in the Discord! --> +Or, ask to join us as a translator in the Discord! +--> diff --git a/README.md b/README.md index 93a7dc726..e96154e5c 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Please see the: - [Gnosis Protocol: Smart contracts](https://github.com/gnosis/gp-v2-contracts) - [Gnosis Protocol: Services](https://github.com/gnosis/gp-v2-services) -## Development +You can block an entire list of tokens by passing in a tokenlist like [here](./src/constants/lists.ts) or you can block specific tokens by adding them to [unsupported.tokenlist.json](./src/constants/tokenLists/unsupported.tokenlist.json). ### Install Dependencies diff --git a/codegen.yml b/codegen.yml new file mode 100644 index 000000000..67fc86b9f --- /dev/null +++ b/codegen.yml @@ -0,0 +1,11 @@ +overrideExisting: true +schema: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3' +documents: 'src/**/!(*.d).{ts,tsx}' +generates: + ./src/state/data/generated.ts: + plugins: + - typescript + - typescript-operations + - typescript-rtk-query: + importBaseApiFrom: './slice' + exportHooks: true diff --git a/custom-test-env.js b/custom-test-env.js new file mode 100644 index 000000000..3cbb28b21 --- /dev/null +++ b/custom-test-env.js @@ -0,0 +1,16 @@ +// Custom test environment to provide `TextEncoder`/`TextDecoder` + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const Environment = require('jest-environment-jsdom') + +module.exports = class CustomTestEnvironment extends Environment { + async setup() { + await super.setup() + if (typeof this.global.TextEncoder === 'undefined') { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { TextEncoder, TextDecoder } = require('util') + this.global.TextEncoder = TextEncoder + this.global.TextDecoder = TextDecoder + } + } +} diff --git a/cypress-custom/integration/fee.test.ts b/cypress-custom/integration/fee.test.ts index 3806814f6..522df23c6 100644 --- a/cypress-custom/integration/fee.test.ts +++ b/cypress-custom/integration/fee.test.ts @@ -1,7 +1,7 @@ import { SupportedChainId as ChainId } from '../../src/custom/constants/chains' import { WETH9 as WETH } from '@uniswap/sdk-core' import { OrderKind } from '@gnosis.pm/gp-v2-contracts' -import { FeeQuoteParams, FeeInformation } from '../../src/custom/priceApi/price' +import { FeeQuoteParams, FeeInformation } from '../../src/custom/utils/price' import { parseUnits } from 'ethers/lib/utils' const DAI = '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735' @@ -60,6 +60,8 @@ describe('Fee endpoint', () => { buyToken: DAI, amount: parseUnits('0.1', DEFAULT_SELL_TOKEN.decimals).toString(), kind: OrderKind.SELL, + fromDecimals: DEFAULT_SELL_TOKEN.decimals, + toDecimals: 6, }) // GIVEN: - @@ -78,6 +80,8 @@ describe('Fee: Complex fetch and persist fee', () => { buyToken: DAI, amount: parseUnits(INPUT_AMOUNT, DEFAULT_SELL_TOKEN.decimals).toString(), kind: OrderKind.SELL, + fromDecimals: DEFAULT_SELL_TOKEN.decimals, + toDecimals: 6, }) // Needs to run first to pass because of Cypress async issues between tests @@ -128,6 +132,8 @@ describe('Fee: simple checks it exists', () => { buyToken: DAI, amount: parseUnits(INPUT_AMOUNT, DEFAULT_SELL_TOKEN.decimals).toString(), kind: OrderKind.SELL, + fromDecimals: DEFAULT_SELL_TOKEN.decimals, + toDecimals: 6, }) const FEE_RESP = { // 1 min in future diff --git a/cypress/fixtures/feeTierDistribution.json b/cypress/fixtures/feeTierDistribution.json new file mode 100644 index 000000000..20d8f28a7 --- /dev/null +++ b/cypress/fixtures/feeTierDistribution.json @@ -0,0 +1,25 @@ +{ + "_meta": { + "block": { + "number": 99999999 + } + }, + "asToken0": [ + { + "feeTier": "500", + "totalValueLockedToken0": "0", + "totalValueLockedToken1": "1" + }, + { + "feeTier": "3000", + "totalValueLockedToken0": "0", + "totalValueLockedToken1": "7" + }, + { + "feeTier": "10000", + "totalValueLockedToken0": "0", + "totalValueLockedToken1": "2" + } + ], + "asToken1": [] +} diff --git a/cypress/integration/add-liquidity.test.ts b/cypress/integration/add-liquidity.test.ts index 372d90dfb..0266739f6 100644 --- a/cypress/integration/add-liquidity.test.ts +++ b/cypress/integration/add-liquidity.test.ts @@ -1,4 +1,13 @@ +import { CyHttpMessages } from 'cypress/types/net-stubbing' +import { aliasQuery, hasQuery } from '../utils/graphql-test-utils' + describe('Add Liquidity', () => { + beforeEach(() => { + cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req) => { + aliasQuery(req, 'feeTierDistribution') + }) + }) + it('loads the two correct tokens', () => { cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab/500') cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR') @@ -23,4 +32,32 @@ describe('Add Liquidity', () => { cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85') cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR') }) + + it('loads fee tier distribution', () => { + cy.fixture('feeTierDistribution.json').then((feeTierDistribution) => { + cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req: CyHttpMessages.IncomingHttpRequest) => { + if (hasQuery(req, 'feeTierDistribution')) { + req.alias = 'feeTierDistributionQuery' + + req.reply({ + body: { + data: { + ...feeTierDistribution, + }, + }, + headers: { + 'access-control-allow-origin': '*', + }, + }) + } + }) + + cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab') + + cy.wait('@feeTierDistributionQuery') + + cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.3% fee tier') + cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '70%') + }) + }) }) diff --git a/cypress/integration/landing.test.ts b/cypress/integration/landing.test.ts index f96878623..1c5070c1a 100644 --- a/cypress/integration/landing.test.ts +++ b/cypress/integration/landing.test.ts @@ -4,6 +4,7 @@ describe('Landing Page', () => { beforeEach(() => cy.visit('/')) it('loads swap page', () => { cy.get('#swap-page') + cy.screenshot() }) it('redirects to url /swap', () => { diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 000000000..59b2bab6e --- /dev/null +++ b/cypress/plugins/index.js @@ -0,0 +1,22 @@ +/// +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +/** + * @type {Cypress.PluginConfig} + */ +// eslint-disable-next-line no-unused-vars +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +} diff --git a/cypress/support/commands.js b/cypress/support/commands.js index b8e4db87f..2c0ffdf29 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -80,7 +80,7 @@ Cypress.Commands.overwrite('visit', (original, url, options) => { onBeforeLoad(win) { options && options.onBeforeLoad && options.onBeforeLoad(win) win.localStorage.clear() - const provider = new JsonRpcProvider('https://rinkeby.infura.io/v3/1221fd11e90849509afafd330ec7acc6', 4) + const provider = new JsonRpcProvider('https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4) const signer = new Wallet(TEST_PRIVATE_KEY, provider) win.ethereum = new CustomizedBridge(signer, provider) }, diff --git a/cypress/utils/graphql-test-utils.ts b/cypress/utils/graphql-test-utils.ts new file mode 100644 index 000000000..f6c3ed637 --- /dev/null +++ b/cypress/utils/graphql-test-utils.ts @@ -0,0 +1,12 @@ +// Utility to match GraphQL mutation based on the query name +export const hasQuery = (req: any, queryName: string) => { + const { body } = req + return body.hasOwnProperty('query') && body.query.includes(queryName) +} + +// Alias query if queryName matches +export const aliasQuery = (req: any, queryName: string) => { + if (hasQuery(req, queryName)) { + req.alias = `${queryName}Query` + } +} diff --git a/package.json b/package.json index d4e54f7da..ea56eacbe 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,12 @@ "devDependencies": { "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", "@craco/craco": "^5.7.0", - "@ethersproject/experimental": "^5.2.0", + "@ethersproject/experimental": "^5.4.0", + "@gnosis.pm/safe-apps-web3-react": "^0.6.0", + "@graphql-codegen/cli": "1.21.5", + "@graphql-codegen/typescript": "1.22.3", + "@graphql-codegen/typescript-operations": "^1.18.2", + "@graphql-codegen/typescript-rtk-query": "^1.1.1", "@lingui/cli": "^3.9.0", "@lingui/loader": "^3.9.0", "@lingui/macro": "^3.9.0", @@ -20,15 +25,19 @@ "@reach/dialog": "^0.10.3", "@reach/portal": "^0.10.3", "@react-hook/window-scroll": "^1.3.0", - "@reduxjs/toolkit": "^1.5.0", + "@reduxjs/toolkit": "^1.6.1", "@sentry/webpack-plugin": "^1.17.1", + "@testing-library/jest-dom": "^5.14.1", + "@testing-library/react": "^12.0.0", "@typechain/ethers-v5": "^7.0.0", + "@types/d3": "^7.0.0", "@types/jest": "^25.2.1", "@types/lingui__core": "^2.7.1", "@types/lingui__macro": "^2.7.4", "@types/lingui__react": "^2.8.3", "@types/lodash.flatmap": "^4.5.6", "@types/luxon": "^1.24.4", + "@types/ms.macro": "^2.0.0", "@types/multicodec": "^1.0.0", "@types/node": "^13.13.5", "@types/qs": "^6.9.2", @@ -63,29 +72,34 @@ "@web3-react/fortmatic-connector": "^6.0.9", "@web3-react/injected-connector": "^6.0.7", "@web3-react/portis-connector": "^6.0.9", - "@web3-react/walletconnect-connector": "^6.2.0", - "@web3-react/walletlink-connector": "^6.2.0", + "@web3-react/walletconnect-connector": "^7.0.2-alpha.0", + "@web3-react/walletlink-connector": "^6.2.3", "ajv": "^6.12.3", "cids": "^1.0.0", "copy-to-clipboard": "^3.2.0", - "cross-env": "^7.0.2", - "cypress": "6.7.1", + "jest-styled-components": "^7.0.5", + "cypress": "^7.7.0", + "d3": "^7.0.0", "eslint": "^7.11.0", "eslint-config-prettier": "^6.11.0", "eslint-plugin-prettier": "^3.1.3", "eslint-plugin-react": "^7.19.0", "eslint-plugin-react-hooks": "^4.0.0", - "ethers": "^5.2.0", + "ethers": "^5.4.6", + "graphql": "^15.5.0", + "graphql-request": "^3.4.0", "inter-ui": "^3.13.1", "ipfs-deploy": "^8.0.1", "jazzicon": "^1.5.0", "lightweight-charts": "^3.3.0", "lodash.flatmap": "^4.5.0", "luxon": "^1.25.0", + "ms.macro": "^2.0.0", "multicodec": "^3.0.1", "multihashes": "^4.0.2", "node-vibrant": "^3.1.5", "polished": "^3.3.2", + "polyfill-object.fromentries": "^1.0.1", "prettier": "^2.2.1", "qs": "^6.9.4", "react": "^17.0.1", @@ -95,6 +109,7 @@ "react-feather": "^2.0.8", "react-ga": "^2.5.7", "react-i18next": "^10.7.0", + "react-is": "^17.0.2", "react-markdown": "^5.0.3", "react-popper": "^2.2.3", "react-redux": "^7.2.2", @@ -108,7 +123,7 @@ "redux-localstorage-simple": "^2.3.1", "serve": "^11.3.2", "start-server-and-test": "^1.11.0", - "styled-components": "^4.2.0", + "styled-components": "^5.3.0", "styled-system": "^5.1.5", "typechain": "^5.0.0", "typescript": "^4.4.2", @@ -116,10 +131,8 @@ "use-count-up": "^2.2.5", "wcag-contrast": "^3.0.0", "workbox-core": "^6.1.0", - "workbox-expiration": "^6.1.0", "workbox-precaching": "^6.1.0", - "workbox-routing": "^6.1.0", - "workbox-strategies": "^6.1.0" + "workbox-routing": "^6.1.0" }, "resolutions": { "@walletconnect/ethereum-provider": "1.6.4" @@ -143,9 +156,11 @@ "compile-external-abi-types": "typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'", "compile-v3-contract-types": "typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'", "compile-all-types": "yarn compile-contract-types && yarn compile-v3-contract-types", + "graphql:generate": "graphql-codegen --config codegen.yml", "postinstall": "yarn compile-contract-types", "i18n:extract": "lingui extract --locale en-US", - "i18n:compile": "lingui compile" + "prei18n:extract": "touch src/locales/en-US.po", + "prestart": "yarn graphql:generate && yarn prei18n:extract" }, "jest": { "moduleNameMapper": { diff --git a/public/images/256x256_App_Icon_Pink.svg b/public/images/256x256_App_Icon_Pink.svg new file mode 100644 index 000000000..8c8286838 --- /dev/null +++ b/public/images/256x256_App_Icon_Pink.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/index.html b/public/index.html index 4b8f7a61e..4328194cb 100644 --- a/public/index.html +++ b/public/index.html @@ -20,11 +20,8 @@ It will be replaced with the URL of the `public` folder during the build. Only files inside the `public` folder can be referenced from the HTML. - Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will - work correctly both with client-side routing and a non-root public URL. - Learn how to configure a non-root public URL by running `npm run build`. - --> - + + --> CowSwap diff --git a/src/abis/governor-bravo.json b/src/abis/governor-bravo.json new file mode 100644 index 000000000..ecd3ff188 --- /dev/null +++ b/src/abis/governor-bravo.json @@ -0,0 +1,1046 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "NewAdmin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldImplementation", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "NewImplementation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldPendingAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newPendingAdmin", + "type": "address" + } + ], + "name": "NewPendingAdmin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "ProposalCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "proposer", + "type": "address" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "targets", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "string[]", + "name": "signatures", + "type": "string[]" + }, + { + "indexed": false, + "internalType": "bytes[]", + "name": "calldatas", + "type": "bytes[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startBlock", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "endBlock", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "name": "ProposalCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "ProposalExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "eta", + "type": "uint256" + } + ], + "name": "ProposalQueued", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldProposalThreshold", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newProposalThreshold", + "type": "uint256" + } + ], + "name": "ProposalThresholdSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "voter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "support", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "votes", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "VoteCast", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldVotingDelay", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newVotingDelay", + "type": "uint256" + } + ], + "name": "VotingDelaySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldVotingPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newVotingPeriod", + "type": "uint256" + } + ], + "name": "VotingPeriodSet", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "BALLOT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "DOMAIN_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_PROPOSAL_THRESHOLD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_VOTING_DELAY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_VOTING_PERIOD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MIN_PROPOSAL_THRESHOLD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MIN_VOTING_DELAY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MIN_VOTING_PERIOD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "_acceptAdmin", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalCount", + "type": "uint256" + } + ], + "name": "_initiate", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newPendingAdmin", + "type": "address" + } + ], + "name": "_setPendingAdmin", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newProposalThreshold", + "type": "uint256" + } + ], + "name": "_setProposalThreshold", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newVotingDelay", + "type": "uint256" + } + ], + "name": "_setVotingDelay", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newVotingPeriod", + "type": "uint256" + } + ], + "name": "_setVotingPeriod", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "cancel", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "support", + "type": "uint8" + } + ], + "name": "castVote", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "support", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "castVoteBySig", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "support", + "type": "uint8" + }, + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "castVoteWithReason", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "execute", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "getActions", + "outputs": [ + { + "internalType": "address[]", + "name": "targets", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + }, + { + "internalType": "string[]", + "name": "signatures", + "type": "string[]" + }, + { + "internalType": "bytes[]", + "name": "calldatas", + "type": "bytes[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "voter", + "type": "address" + } + ], + "name": "getReceipt", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "hasVoted", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "support", + "type": "uint8" + }, + { + "internalType": "uint96", + "name": "votes", + "type": "uint96" + } + ], + "internalType": "struct GovernorBravoDelegateStorageV1.Receipt", + "name": "", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialProposalId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "timelock_", + "type": "address" + }, + { + "internalType": "address", + "name": "uni_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "votingPeriod_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votingDelay_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalThreshold_", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "latestProposalIds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "pendingAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "proposalCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "proposalMaxOperations", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "proposalThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "proposals", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "proposer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "eta", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "startBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "forVotes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "againstVotes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "abstainVotes", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "canceled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "executed", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address[]", + "name": "targets", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + }, + { + "internalType": "string[]", + "name": "signatures", + "type": "string[]" + }, + { + "internalType": "bytes[]", + "name": "calldatas", + "type": "bytes[]" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "name": "propose", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "queue", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "quorumVotes", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "state", + "outputs": [ + { + "internalType": "enum GovernorBravoDelegateStorageV1.ProposalState", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "timelock", + "outputs": [ + { + "internalType": "contract TimelockInterface", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "uni", + "outputs": [ + { + "internalType": "contract UniInterface", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "votingDelay", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "votingPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/assets/styles/styled.ts b/src/assets/styles/styled.ts index f0039d8b4..6b97295e9 100644 --- a/src/assets/styles/styled.ts +++ b/src/assets/styles/styled.ts @@ -1,4 +1,4 @@ -import styled from 'styled-components' +import styled from 'styled-components/macro' import * as CSS from 'csstype' // with font size convert to rems diff --git a/src/assets/svg/arbitrum_logo.svg b/src/assets/svg/arbitrum_logo.svg new file mode 100644 index 000000000..47fe30800 --- /dev/null +++ b/src/assets/svg/arbitrum_logo.svg @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svg/optimism_logo.svg b/src/assets/svg/optimism_logo.svg new file mode 100644 index 000000000..2e5ce3103 --- /dev/null +++ b/src/assets/svg/optimism_logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/AccountDetails/Copy.tsx b/src/components/AccountDetails/Copy.tsx new file mode 100644 index 000000000..6aa48dfe5 --- /dev/null +++ b/src/components/AccountDetails/Copy.tsx @@ -0,0 +1,48 @@ +import styled from 'styled-components/macro' +import useCopyClipboard from '../../hooks/useCopyClipboard' + +import { LinkStyledButton } from '../../theme' +import { CheckCircle, Copy } from 'react-feather' +import { Trans } from '@lingui/macro' + +const CopyIcon = styled(LinkStyledButton)` + color: ${({ theme }) => theme.text3}; + flex-shrink: 0; + display: flex; + text-decoration: none; + font-size: 0.825rem; + :hover, + :active, + :focus { + text-decoration: none; + color: ${({ theme }) => theme.text2}; + } +` +const TransactionStatusText = styled.span` + margin-left: 0.25rem; + font-size: 0.825rem; + ${({ theme }) => theme.flexRowNoWrap}; + align-items: center; +` + +export default function CopyHelper(props: { toCopy: string; children?: React.ReactNode }) { + const [isCopied, setCopied] = useCopyClipboard() + + return ( + setCopied(props.toCopy)}> + {isCopied ? ( + + + + Copied + + + ) : ( + + + + )} + {isCopied ? '' : props.children} + + ) +} diff --git a/src/components/AccountDetails/Transaction.tsx b/src/components/AccountDetails/Transaction.tsx index 814ff8f2b..4adb10e51 100644 --- a/src/components/AccountDetails/Transaction.tsx +++ b/src/components/AccountDetails/Transaction.tsx @@ -1,9 +1,8 @@ -import React from 'react' import styled from 'styled-components/macro' import { CheckCircle, Triangle } from 'react-feather' -import { ExternalLink } from 'theme' -import { useActiveWeb3React } from 'hooks/web3' +import { useActiveWeb3React } from '../../hooks/web3' +import { ExternalLink } from '../../theme' import { useAllTransactions } from '../../state/transactions/hooks' import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' import { RowFixed } from '../Row' diff --git a/src/components/AccountDetails/index.tsx b/src/components/AccountDetails/index.tsx index 36eb45a53..6c4289278 100644 --- a/src/components/AccountDetails/index.tsx +++ b/src/components/AccountDetails/index.tsx @@ -1,5 +1,6 @@ -import React, { useCallback, useContext } from 'react' -import styled, { ThemeContext } from 'styled-components' +import { useCallback, useContext } from 'react' +import styled, { ThemeContext } from 'styled-components/macro' +import { SUPPORTED_WALLETS } from 'constants/wallet' import { useActiveWeb3React } from '../../hooks/web3' import { clearAllTransactions } from '../../state/transactions/actions' import { shortenAddress } from 'utils' @@ -8,9 +9,7 @@ import { AutoRow } from '../Row' import Copy from 'components/Copy' import Transaction from 'components/AccountDetails/Transaction' -import { SUPPORTED_WALLETS } from 'constants/index' import { ReactComponent as Close } from '../../assets/images/x.svg' -// import { getEtherscanLink } from 'utils' import { injected, walletconnect, walletlink, fortmatic, portis } from 'connectors' import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg' import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg' diff --git a/src/components/AddressInputPanel/index.tsx b/src/components/AddressInputPanel/index.tsx index 238fb6691..1323523f8 100644 --- a/src/components/AddressInputPanel/index.tsx +++ b/src/components/AddressInputPanel/index.tsx @@ -1,5 +1,6 @@ -import React, { useContext, useCallback } from 'react' -import styled, { ThemeContext } from 'styled-components' +import { t, Trans } from '@lingui/macro' +import { useContext, useCallback, ReactNode } from 'react' +import styled, { ThemeContext } from 'styled-components/macro' import useENS from '../../hooks/useENS' import { useActiveWeb3React } from '../../hooks/web3' import { ExternalLink, TYPE } from 'theme' @@ -40,7 +41,7 @@ const Input = styled.input<{ error?: boolean }>` width: 0; background-color: ${({ theme }) => theme.bg1}; transition: color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')}; - color: ${({ error, theme }) => (error ? theme.red1 : theme.primary1)}; + color: ${({ error, theme }) => (error ? theme.red1 : theme.text1)}; overflow: hidden; text-overflow: ellipsis; font-weight: 500; @@ -67,10 +68,16 @@ const Input = styled.input<{ error?: boolean }>` export default function AddressInputPanel({ id, + className = 'recipient-address-input', + label, + placeholder, value, onChange, }: { id?: string + className?: string + label?: ReactNode + placeholder?: string // the typed string value value: string // triggers whenever the typed value changes @@ -99,25 +106,25 @@ export default function AddressInputPanel({ - Recipient + {label ?? Recipient} {address && chainId && ( - (View on Explorer) + (View on Explorer) )} theme.bg0}; - padding: 4px 6px; -` - export default function RangeBadge({ removed, inRange, diff --git a/src/components/Badge/index.tsx b/src/components/Badge/index.tsx index a2ad343d7..5e4b0aed7 100644 --- a/src/components/Badge/index.tsx +++ b/src/components/Badge/index.tsx @@ -1,7 +1,7 @@ import { readableColor } from 'polished' import { PropsWithChildren } from 'react' -import styled, { DefaultTheme } from 'styled-components' -import { Color } from 'theme/styled' +import styled, { DefaultTheme } from 'styled-components/macro' +import { Color } from '@src/theme/styled' export enum BadgeVariant { DEFAULT = 'DEFAULT', diff --git a/src/components/Blocklist/index.tsx b/src/components/Blocklist/index.tsx index c8fc31919..3e0e954cb 100644 --- a/src/components/Blocklist/index.tsx +++ b/src/components/Blocklist/index.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useMemo } from 'react' +import { ReactNode, useMemo } from 'react' import { useActiveWeb3React } from '../../hooks/web3' import { Trans } from '@lingui/macro' @@ -9,6 +9,7 @@ const BLOCKED_ADDRESSES: string[] = [ '0x901bb9583b24D97e995513C6778dc6888AB6870e', '0xA7e5d5A720f06526557c513402f2e6B5fA20b008', '0x8576aCC5C05D6Ce88f4e49bf65BdF0C62F91353C', + '0xC8a65Fadf0e0dDAf421F28FEAb69Bf6E2E589963', ] export default function Blocklist({ children }: { children: ReactNode }) { diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 4957aa417..3111fd31b 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import styled from 'styled-components/macro' import { darken } from 'polished' @@ -13,16 +12,15 @@ const Base = styled(RebassButton)< { padding?: string width?: string - borderRadius?: string + $borderRadius?: string altDisabledStyle?: boolean } & ButtonProps >` - padding: ${({ padding }) => (padding ? padding : '16px')}; - width: ${({ width }) => (width ? width : '100%')}; + padding: ${({ padding }) => padding ?? '16px'}; + width: ${({ width }) => width ?? '100%'}; font-weight: 500; text-align: center; - border-radius: 20px; - border-radius: ${({ borderRadius }) => borderRadius && borderRadius}; + border-radius: ${({ $borderRadius }) => $borderRadius ?? '20px'}; outline: none; border: 1px solid transparent; color: white; @@ -43,10 +41,6 @@ const Base = styled(RebassButton)< transition: transform 450ms ease; transform: perspective(1px) translateZ(0); - &:hover { - transform: scale(0.99); - } - > * { user-select: none; } @@ -114,9 +108,6 @@ export const ButtonGray = styled(Base)` color: ${({ theme }) => theme.text2}; font-size: 16px; font-weight: 500; - &:focus { - background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)}; - } &:hover { background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)}; } @@ -153,63 +144,41 @@ export const ButtonSecondary = styled(Base)` } ` -export const ButtonPink = styled(Base)` - background-color: ${({ theme }) => theme.primary1}; - color: white; - +export const ButtonOutlined = styled(Base)` + border: 1px solid ${({ theme }) => theme.bg2}; + background-color: transparent; + color: ${({ theme }) => theme.text1}; &:focus { - box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.primary1)}; - background-color: ${({ theme }) => darken(0.05, theme.primary1)}; + box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; } &:hover { - background-color: ${({ theme }) => darken(0.05, theme.primary1)}; + box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; } &:active { - box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.primary1)}; - background-color: ${({ theme }) => darken(0.1, theme.primary1)}; + box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; } &:disabled { - background-color: ${({ theme }) => theme.primary1}; opacity: 50%; cursor: auto; } ` -export const ButtonUNIGradient = styled(ButtonPrimary)` +export const ButtonYellow = styled(Base)` + background-color: ${({ theme }) => theme.yellow3}; color: white; - padding: 4px 8px; - height: 36px; - font-weight: 500; - background-color: ${({ theme }) => theme.bg3}; - background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2; - width: fit-content; - position: relative; - cursor: pointer; - border: none; - white-space: no-wrap; - :hover { - opacity: 0.8; - } - :active { - opacity: 0.9; - } -` - -export const ButtonOutlined = styled(Base)` - border: 1px solid ${({ theme }) => theme.bg2}; - background-color: transparent; - color: ${({ theme }) => theme.text1}; - &:focus { - box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; + box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.yellow3)}; + background-color: ${({ theme }) => darken(0.05, theme.yellow3)}; } &:hover { - box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; + background-color: ${({ theme }) => darken(0.05, theme.yellow3)}; } &:active { - box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; + box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.yellow3)}; + background-color: ${({ theme }) => darken(0.1, theme.yellow3)}; } &:disabled { + background-color: ${({ theme }) => theme.yellow3}; opacity: 50%; cursor: auto; } @@ -259,27 +228,6 @@ export const ButtonText = styled(Base)` } ` -export const ButtonWhite = styled(Base)` - border: 1px solid #edeef2; - background-color: ${({ theme }) => theme.bg1}; - color: black; - - &:focus { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - box-shadow: 0 0 0 1pt ${darken(0.05, '#edeef2')}; - } - &:hover { - box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')}; - } - &:active { - box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')}; - } - &:disabled { - opacity: 50%; - cursor: auto; - } -` - const ButtonConfirmedStyle = styled(Base)` background-color: ${({ theme }) => theme.bg3}; color: ${({ theme }) => theme.text1}; @@ -348,17 +296,6 @@ export function ButtonDropdown({ disabled = false, children, ...rest }: { disabl ) } -export function ButtonDropdownGrey({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) { - return ( - - -
{children}
- -
-
- ) -} - export function ButtonDropdownLight({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) { return ( @@ -370,14 +307,6 @@ export function ButtonDropdownLight({ disabled = false, children, ...rest }: { d ) } -export function ButtonRadio({ active, ...rest }: { active?: boolean } & ButtonProps) { - if (!active) { - return - } else { - return - } -} - const ActiveOutlined = styled(ButtonOutlined)` border: 1px solid; border-color: ${({ theme }) => theme.primary1}; diff --git a/src/components/Card/index.tsx b/src/components/Card/index.tsx index f584934fb..b1c9d3ebf 100644 --- a/src/components/Card/index.tsx +++ b/src/components/Card/index.tsx @@ -1,13 +1,11 @@ import styled from 'styled-components/macro' import { Box } from 'rebass/styled-components' -const Card = styled(Box)<{ width?: string; padding?: string; border?: string; borderRadius?: string }>` +const Card = styled(Box)<{ width?: string; padding?: string; border?: string; $borderRadius?: string }>` width: ${({ width }) => width ?? '100%'}; - border-radius: 16px; - padding: 1rem; - padding: ${({ padding }) => padding}; + padding: ${({ padding }) => padding ?? '1rem'}; + border-radius: ${({ $borderRadius }) => $borderRadius ?? '16px'}; border: ${({ border }) => border}; - border-radius: ${({ borderRadius }) => borderRadius}; ` export default Card @@ -42,12 +40,6 @@ export const YellowCard = styled(Card)` font-weight: 500; ` -export const PinkCard = styled(Card)` - background-color: rgba(255, 0, 122, 0.03); - color: ${({ theme }) => theme.primary1}; - font-weight: 500; -` - export const BlueCard = styled(Card)` background-color: ${({ theme }) => theme.primary5}; color: ${({ theme }) => theme.blue2}; diff --git a/src/components/Confetti/index.tsx b/src/components/Confetti/index.tsx index 5a9f22cd9..a59992e8d 100644 --- a/src/components/Confetti/index.tsx +++ b/src/components/Confetti/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import ReactConfetti from 'react-confetti' import { useWindowSize } from '../../hooks/useWindowSize' diff --git a/src/components/CurrencyInputPanel/FiatValue.tsx b/src/components/CurrencyInputPanel/FiatValue.tsx index 1768ed816..91770cda1 100644 --- a/src/components/CurrencyInputPanel/FiatValue.tsx +++ b/src/components/CurrencyInputPanel/FiatValue.tsx @@ -1,5 +1,5 @@ import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' -import React, { useMemo } from 'react' +import { useMemo } from 'react' import useTheme from '../../hooks/useTheme' import { TYPE } from '../../theme' import { warningSeverity } from '../../utils/prices' diff --git a/src/components/CurrencyInputPanel/index.tsx b/src/components/CurrencyInputPanel/index.tsx index b76e1e599..3e7e2ce82 100644 --- a/src/components/CurrencyInputPanel/index.tsx +++ b/src/components/CurrencyInputPanel/index.tsx @@ -1,6 +1,6 @@ import { Pair } from '@uniswap/v2-sdk' import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core' -import React, { useState, useCallback, ReactNode } from 'react' +import { useState, useCallback, ReactNode } from 'react' import styled from 'styled-components/macro' import { darken } from 'polished' import { useCurrencyBalance } from '../../state/wallet/hooks' @@ -53,7 +53,8 @@ const Container = styled.div<{ hideInput: boolean }>` } ` -const CurrencySelect = styled(ButtonGray)<{ selected: boolean; hideInput?: boolean }>` +const CurrencySelect = styled(ButtonGray)<{ visible: boolean; selected: boolean; hideInput?: boolean }>` + visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')}; align-items: center; font-size: 24px; font-weight: 500; @@ -160,6 +161,8 @@ interface CurrencyInputPanelProps { priceImpact?: Percent id: string showCommonBases?: boolean + showCurrencyAmount?: boolean + disableNonToken?: boolean renderBalance?: (amount: CurrencyAmount) => ReactNode locked?: boolean } @@ -174,6 +177,8 @@ export default function CurrencyInputPanel({ otherCurrency, id, showCommonBases, + showCurrencyAmount, + disableNonToken, renderBalance, fiatValue, priceImpact, @@ -207,6 +212,7 @@ export default function CurrencyInputPanel({ )} diff --git a/src/components/CurrencyLogo/index.tsx b/src/components/CurrencyLogo/index.tsx index 39c2e0f85..96053f571 100644 --- a/src/components/CurrencyLogo/index.tsx +++ b/src/components/CurrencyLogo/index.tsx @@ -1,5 +1,5 @@ import { Currency } from '@uniswap/sdk-core' -import React, { useMemo } from 'react' +import { useMemo } from 'react' import styled from 'styled-components/macro' import EthereumLogo from '../../assets/images/ethereum-logo.png' import useHttpLocations from '../../hooks/useHttpLocations' @@ -7,7 +7,7 @@ import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo' import Logo from '../Logo' export const getTokenLogoURL = (address: string) => - `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${address}/logo.png` + `https://raw.githubusercontent.com/uniswap/assets/master/blockchains/ethereum/assets/${address}/logo.png` const StyledEthereumLogo = styled.img<{ size: string }>` width: ${({ size }) => size}; @@ -30,7 +30,7 @@ export default function CurrencyLogo({ style, ...rest }: { - currency?: Currency + currency?: Currency | null size?: string style?: React.CSSProperties }) { @@ -50,7 +50,7 @@ export default function CurrencyLogo({ }, [currency, uriLocations]) if (currency?.isNative) { - return + return } return diff --git a/src/components/DoubleLogo/index.tsx b/src/components/DoubleLogo/index.tsx index 15923b64b..722975d62 100644 --- a/src/components/DoubleLogo/index.tsx +++ b/src/components/DoubleLogo/index.tsx @@ -1,5 +1,4 @@ import { Currency } from '@uniswap/sdk-core' -import React from 'react' import styled from 'styled-components/macro' import CurrencyLogo from 'components/CurrencyLogo' diff --git a/src/components/ErrorBoundary/index.tsx b/src/components/ErrorBoundary/index.tsx index 2f327a4e2..c3af844dc 100644 --- a/src/components/ErrorBoundary/index.tsx +++ b/src/components/ErrorBoundary/index.tsx @@ -1,5 +1,5 @@ import { Trans } from '@lingui/macro' -import React, { ErrorInfo } from 'react' +import { Component, ErrorInfo } from 'react' import store, { AppState } from '../../state' import { ExternalLink, TYPE } from '../../theme' import { AutoColumn } from '../Column' @@ -46,7 +46,7 @@ type ErrorBoundaryState = { error: Error | null } -export default class ErrorBoundary extends React.Component { +export default class ErrorBoundary extends Component { constructor(props: unknown) { super(props) this.state = { error: null } diff --git a/src/components/FeeSelector/index.tsx b/src/components/FeeSelector/index.tsx index f49c67901..12ffc9ae8 100644 --- a/src/components/FeeSelector/index.tsx +++ b/src/components/FeeSelector/index.tsx @@ -1,81 +1,228 @@ -import React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { FeeAmount } from '@uniswap/v3-sdk' +import { Currency } from '@uniswap/sdk-core' import { Trans } from '@lingui/macro' import { AutoColumn } from 'components/Column' import { DynamicSection } from 'pages/AddLiquidity/styled' import { TYPE } from 'theme' import { RowBetween } from 'components/Row' -import { ButtonRadioChecked } from '@src/components/Button' -import styled from 'styled-components/macro' +import { ButtonGray, ButtonRadioChecked } from 'components/Button' +import styled, { keyframes } from 'styled-components/macro' +import Badge from 'components/Badge' +import Card from 'components/Card' +import usePrevious from 'hooks/usePrevious' +import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution' +import ReactGA from 'react-ga' +import { Box } from 'rebass' + +const pulse = (color: string) => keyframes` + 0% { + box-shadow: 0 0 0 0 ${color}; + } + + 70% { + box-shadow: 0 0 0 2px ${color}; + } + + 100% { + box-shadow: 0 0 0 0 ${color}; + } +` const ResponsiveText = styled(TYPE.label)` + line-height: 16px; + ${({ theme }) => theme.mediaWidth.upToSmall` font-size: 12px; + line-height: 12px; `}; ` +const FocusedOutlineCard = styled(Card)<{ pulsing: boolean }>` + border: 1px solid ${({ theme }) => theme.bg2}; + animation: ${({ pulsing, theme }) => pulsing && pulse(theme.primary1)} 0.6s linear; +` + +const FeeAmountLabel = { + [FeeAmount.LOW]: { + label: '0.05', + description: Best for stable pairs., + }, + [FeeAmount.MEDIUM]: { + label: '0.3', + description: Best for most pairs., + }, + [FeeAmount.HIGH]: { + label: '1', + description: Best for exotic pairs., + }, +} + +const FeeTierPercentageBadge = ({ percentage }: { percentage: number | undefined }) => { + return ( + + + {percentage !== undefined ? {percentage?.toFixed(0)}% select : Not created} + + + ) +} + export default function FeeSelector({ disabled = false, feeAmount, handleFeePoolSelect, + currencyA, + currencyB, }: { disabled?: boolean feeAmount?: FeeAmount handleFeePoolSelect: (feeAmount: FeeAmount) => void + currencyA?: Currency | undefined + currencyB?: Currency | undefined }) { + const { isLoading, isError, largestUsageFeeTier, distributions } = useFeeTierDistribution(currencyA, currencyB) + + const [showOptions, setShowOptions] = useState(false) + const [pulsing, setPulsing] = useState(false) + + const previousFeeAmount = usePrevious(feeAmount) + + const recommended = useRef(false) + + const handleFeePoolSelectWithEvent = useCallback( + (fee) => { + ReactGA.event({ + category: 'FeePoolSelect', + action: 'Manual', + }) + handleFeePoolSelect(fee) + }, + [handleFeePoolSelect] + ) + + useEffect(() => { + if (feeAmount || isLoading || isError) { + return + } + + if (!largestUsageFeeTier) { + // cannot recommend, open options + setShowOptions(true) + } else { + setShowOptions(false) + + recommended.current = true + ReactGA.event({ + category: 'FeePoolSelect', + action: ' Recommended', + }) + + handleFeePoolSelect(largestUsageFeeTier) + } + }, [feeAmount, isLoading, isError, largestUsageFeeTier, handleFeePoolSelect]) + + useEffect(() => { + setShowOptions(isError) + }, [isError]) + + useEffect(() => { + if (feeAmount && previousFeeAmount !== feeAmount) { + setPulsing(true) + } + }, [previousFeeAmount, feeAmount]) + return ( - - Select Pool - - - Select a pool type based on your preferred liquidity provider fee. - - - handleFeePoolSelect(FeeAmount.LOW)} - > - - - 0.05% fee - - - Best for stable pairs. - - - - handleFeePoolSelect(FeeAmount.MEDIUM)} - > - - - 0.3% fee - - - Best for most pairs. - + setPulsing(false)}> + + + {!feeAmount ? ( + <> + + Fee tier + + + The % you will earn in fees. + + + ) : ( + <> + + {FeeAmountLabel[feeAmount].label}% fee tier + + + {distributions && feeAmount && } + + + )} - - handleFeePoolSelect(FeeAmount.HIGH)} - > - - - 1% fee - - - Best for exotic pairs. - - - - + + setShowOptions(!showOptions)} width="auto" padding="4px" $borderRadius="6px"> + {showOptions ? Hide : Edit} + + + + + {showOptions && ( + + handleFeePoolSelectWithEvent(FeeAmount.LOW)} + > + + + + 0.05% fee + + + Best for stable pairs. + + + + {distributions && } + + + handleFeePoolSelectWithEvent(FeeAmount.MEDIUM)} + > + + + + 0.3% fee + + + Best for most pairs. + + + + {distributions && } + + + handleFeePoolSelectWithEvent(FeeAmount.HIGH)} + > + + + + 1% fee + + + Best for exotic pairs. + + + + {distributions && } + + + + )} ) diff --git a/src/components/FormattedCurrencyAmount/index.tsx b/src/components/FormattedCurrencyAmount/index.tsx index 5421ad22e..81d380d6f 100644 --- a/src/components/FormattedCurrencyAmount/index.tsx +++ b/src/components/FormattedCurrencyAmount/index.tsx @@ -1,5 +1,4 @@ import JSBI from 'jsbi' -import React from 'react' import { Currency, CurrencyAmount, Fraction } from '@uniswap/sdk-core' const CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000)) diff --git a/src/components/Header/NetworkCard.tsx b/src/components/Header/NetworkCard.tsx new file mode 100644 index 000000000..88b0f5995 --- /dev/null +++ b/src/components/Header/NetworkCard.tsx @@ -0,0 +1,235 @@ +import { Trans } from '@lingui/macro' +import { YellowCard } from 'components/Card' +import { useOnClickOutside } from 'hooks/useOnClickOutside' +import { useActiveWeb3React } from 'hooks/web3' +import { useEffect, useRef, useState } from 'react' +import { ArrowDownCircle, ChevronDown, ToggleLeft } from 'react-feather' +import { ApplicationModal } from 'state/application/actions' +import { useModalOpen, useToggleModal } from 'state/application/hooks' +import styled, { css } from 'styled-components/macro' +import { ExternalLink } from 'theme' +import { switchToNetwork } from 'utils/switchToNetwork' +import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from '@src/constants/chains' + +const BaseWrapper = css` + position: relative; + margin-right: 8px; + ${({ theme }) => theme.mediaWidth.upToMedium` + justify-self: end; + `}; + + ${({ theme }) => theme.mediaWidth.upToSmall` + margin: 0 0.5rem 0 0; + width: initial; + text-overflow: ellipsis; + flex-shrink: 1; + `}; +` +const L2Wrapper = styled.div` + ${BaseWrapper} +` +const BaseMenuItem = css` + align-items: center; + background-color: transparent; + border-radius: 12px; + color: ${({ theme }) => theme.text2}; + cursor: pointer; + display: flex; + flex: 1; + flex-direction: row; + font-size: 16px; + font-weight: 400; + justify-content: space-between; + :hover { + color: ${({ theme }) => theme.text1}; + text-decoration: none; + } +` +const DisabledMenuItem = styled.div` + ${BaseMenuItem} + align-items: center; + background-color: ${({ theme }) => theme.bg2}; + cursor: auto; + display: flex; + font-size: 10px; + font-style: italic; + justify-content: center; + :hover, + :active, + :focus { + color: ${({ theme }) => theme.text2}; + } +` +const FallbackWrapper = styled(YellowCard)` + ${BaseWrapper} + width: auto; + border-radius: 12px; + padding: 8px 12px; + width: 100%; + user-select: none; +` +const Icon = styled.img` + width: 16px; + margin-right: 2px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + margin-right: 4px; + + `}; +` + +const MenuFlyout = styled.span` + background-color: ${({ theme }) => theme.bg1}; + border: 1px solid ${({ theme }) => theme.bg0}; + + box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), + 0px 24px 32px rgba(0, 0, 0, 0.01); + border-radius: 12px; + padding: 1rem; + display: flex; + flex-direction: column; + font-size: 1rem; + position: absolute; + left: 0rem; + top: 3rem; + z-index: 100; + width: 237px; + ${({ theme }) => theme.mediaWidth.upToMedium` + + bottom: unset; + top: 4.5em + right: 0; + + `}; + > { + padding: 12px; + } + > :not(:first-child) { + margin-top: 8px; + } + > :not(:last-child) { + margin-bottom: 8px; + } +` +const LinkOutCircle = styled(ArrowDownCircle)` + transform: rotate(230deg); + width: 16px; + height: 16px; + opacity: 0.6; +` +const MenuItem = styled(ExternalLink)` + ${BaseMenuItem} +` +const ButtonMenuItem = styled.button` + ${BaseMenuItem} + border: none; + box-shadow: none; + color: ${({ theme }) => theme.text2}; + outline: none; + padding: 0; +` +const NetworkInfo = styled.button<{ chainId: SupportedChainId }>` + align-items: center; + background-color: ${({ theme }) => theme.bg0}; + border-radius: 12px; + border: 1px solid ${({ theme }) => theme.bg0}; + color: ${({ theme }) => theme.text1}; + display: flex; + flex-direction: row; + font-weight: 500; + font-size: 12px; + height: 100%; + margin: 0; + height: 38px; + padding: 0.7rem; + + :hover, + :focus { + cursor: pointer; + outline: none; + border: 1px solid ${({ theme }) => theme.bg3}; + } +` +const NetworkName = styled.div<{ chainId: SupportedChainId }>` + border-radius: 6px; + font-size: 16px; + font-weight: 500; + padding: 0 2px 0.5px 4px; + margin: 0 2px; + white-space: pre; + ${({ theme }) => theme.mediaWidth.upToSmall` + display: none; + `}; +` + +export default function NetworkCard() { + const { chainId, library } = useActiveWeb3React() + const node = useRef(null) + const open = useModalOpen(ApplicationModal.ARBITRUM_OPTIONS) + const toggle = useToggleModal(ApplicationModal.ARBITRUM_OPTIONS) + useOnClickOutside(node, open ? toggle : undefined) + + const [implements3085, setImplements3085] = useState(false) + useEffect(() => { + // metamask is currently the only known implementer of this EIP + // here we proceed w/ a noop feature check to ensure the user's version of metamask supports network switching + // if not, we hide the UI + if (!library?.provider?.request || !chainId || !library?.provider?.isMetaMask) { + return + } + switchToNetwork({ library, chainId }) + .then((x) => x ?? setImplements3085(true)) + .catch(() => setImplements3085(false)) + }, [library, chainId]) + + const info = chainId ? CHAIN_INFO[chainId] : undefined + if (!chainId || chainId === SupportedChainId.MAINNET || !info || !library) { + return null + } + + if (L2_CHAIN_IDS.includes(chainId)) { + const info = CHAIN_INFO[chainId as SupportedL2ChainId] + const isArbitrum = [SupportedChainId.ARBITRUM_ONE, SupportedChainId.ARBITRUM_RINKEBY].includes(chainId) + return ( + + + + {info.label} + + + {open && ( + + +
{isArbitrum ? {info.label} Bridge : Optimistic L2 Gateway}
+ +
+ + {isArbitrum ? {info.label} Explorer : Optimistic Etherscan} + + + +
+ Learn more +
+ +
+ {implements3085 ? ( + switchToNetwork({ library, chainId: 1 })}> +
+ Switch to L1 (Mainnet) +
+ +
+ ) : ( + + Change your network to go back to L1 + + )} +
+ )} +
+ ) + } + + return {info.label} +} diff --git a/src/components/Header/Polling.tsx b/src/components/Header/Polling.tsx index d95534f74..04bac065a 100644 --- a/src/components/Header/Polling.tsx +++ b/src/components/Header/Polling.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useState } from 'react' -import styled, { keyframes } from 'styled-components' +import { useEffect, useState } from 'react' +import styled, { keyframes } from 'styled-components/macro' import { useActiveWeb3React } from '../../hooks/web3' import { useBlockNumber } from '../../state/application/hooks' @@ -26,12 +26,11 @@ const StyledPollingNumber = styled(TYPE.small)<{ breathe: boolean; hovering: boo opacity: 1; } ` -const StyledPollingDot = styled.div` +export const StyledPollingDot = styled.div` width: 8px; height: 8px; min-height: 8px; min-width: 8px; - margin-left: 0.5rem; border-radius: 50%; position: relative; background-color: ${({ theme }) => theme.green1}; @@ -96,7 +95,7 @@ export default function Polling() { > setIsHover(true)} onMouseLeave={() => setIsHover(false)}> - {blockNumber} + {blockNumber}  {isMounting && } diff --git a/src/components/Header/URLWarning.tsx b/src/components/Header/URLWarning.tsx index 4c31b2744..99d791224 100644 --- a/src/components/Header/URLWarning.tsx +++ b/src/components/Header/URLWarning.tsx @@ -1,8 +1,7 @@ -import React from 'react' import styled from 'styled-components/macro' import { AlertTriangle, X } from 'react-feather' -import { useURLWarningToggle, useURLWarningVisible } from '../../state/user/hooks' +import { useURLWarningToggle, useURLWarningVisible } from 'state/user/hooks' import { isMobile } from 'react-device-detect' import { Trans } from '@lingui/macro' diff --git a/src/components/Header/UniBalanceContent.tsx b/src/components/Header/UniBalanceContent.tsx index 3cd312081..eec13ca68 100644 --- a/src/components/Header/UniBalanceContent.tsx +++ b/src/components/Header/UniBalanceContent.tsx @@ -1,22 +1,23 @@ +import { Trans } from '@lingui/macro' import { CurrencyAmount, Token } from '@uniswap/sdk-core' -import React, { useMemo } from 'react' +import { CHAIN_INFO, SupportedChainId } from '@src/constants/chains' +import { useMemo } from 'react' import { X } from 'react-feather' import styled from 'styled-components/macro' import tokenLogo from '../../assets/images/token-logo.png' import { UNI } from 'constants/tokens' -import { useTotalSupply } from '../../hooks/useTotalSupply' -import { useActiveWeb3React } from '../../hooks/web3' import { useMerkleDistributorContract } from '../../hooks/useContract' import useCurrentBlockTimestamp from '../../hooks/useCurrentBlockTimestamp' +import { useTotalSupply } from '../../hooks/useTotalSupply' +import useUSDCPrice from '../../hooks/useUSDCPrice' +import { useActiveWeb3React } from '../../hooks/web3' import { useTotalUniEarned } from '../../state/stake/hooks' import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks' import { ExternalLink, StyledInternalLink, TYPE, UniTokenAnimated } from 'theme' import { computeUniCirculation } from '../../utils/computeUniCirculation' -import useUSDCPrice from '../../hooks/useUSDCPrice' import { AutoColumn } from '../Column' -import { RowBetween } from '../Row' import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled' -import { Trans } from '@lingui/macro' +import { RowBetween } from '../Row' const ContentWrapper = styled(AutoColumn)` width: 100%; @@ -59,6 +60,8 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU [blockTimestamp, chainId, totalSupply, unclaimedUni, uni] ) + const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET] + return ( @@ -128,7 +131,7 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU {totalSupply?.toFixed(0, { groupSeparator: ',' })} {uni && uni.chainId === 1 ? ( - + View UNI Analytics ) : null} diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 6eefc1eb1..c691888af 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -1,8 +1,8 @@ import { Trans } from '@lingui/macro' import useScrollPosition from '@react-hook/window-scroll' +import { CHAIN_INFO, SupportedChainId } from '@src/constants/chains' import { darken } from 'polished' -import React, { useState } from 'react' -import { Moon, Sun } from 'react-feather' +import { useState } from 'react' import { NavLink } from 'react-router-dom' import { Text } from 'rebass' import { useShowClaimPopup, useToggleSelfClaimModal } from 'state/application/hooks' @@ -13,17 +13,16 @@ import { useETHBalances } from 'state/wallet/hooks' import styled from 'styled-components/macro' import Logo from '../../assets/svg/logo.svg' import LogoDark from '../../assets/svg/logo_white.svg' -import { NETWORK_LABELS, SupportedChainId } from '../../constants/chains' import { useActiveWeb3React } from '../../hooks/web3' import { ExternalLink, TYPE } from '../../theme' -import { YellowCard } from '../Card' import ClaimModal from '../claim/ClaimModal' import { CardNoise } from '../earn/styled' import Menu from '../Menu' import Modal from '../Modal' -import Row, { RowFixed } from '../Row' +import Row from '../Row' import { Dots } from '../swap/styleds' import Web3Status from '../Web3Status' +import NetworkCard from './NetworkCard' import UniBalanceContent from './UniBalanceContent' const HeaderFrame = styled.div<{ showBackground: boolean }>` @@ -39,22 +38,27 @@ const HeaderFrame = styled.div<{ showBackground: boolean }>` padding: 1rem; z-index: 21; position: relative; - /* Background slide effect on scroll. */ - background-image: ${({ theme }) => `linear-gradient(to bottom, transparent 50%, ${theme.bg0} 50% )}}`} + background-image: ${({ theme }) => `linear-gradient(to bottom, transparent 50%, ${theme.bg0} 50% )}}`}; background-position: ${({ showBackground }) => (showBackground ? '0 -100%' : '0 0')}; background-size: 100% 200%; box-shadow: 0px 0px 0px 1px ${({ theme, showBackground }) => (showBackground ? theme.bg2 : 'transparent;')}; - transition: background-position .1s, box-shadow .1s; + transition: background-position 0.1s, box-shadow 0.1s; + background-blend-mode: hard-light; + + ${({ theme }) => theme.mediaWidth.upToLarge` + grid-template-columns: 48px 1fr 1fr; + `}; ${({ theme }) => theme.mediaWidth.upToMedium` padding: 1rem; - grid-template-columns: auto 1fr; + grid-template-columns: 1fr 1fr; `}; - ${({ theme }) => theme.mediaWidth.upToExtraSmall` - padding: 1rem; - `} + ${({ theme }) => theme.mediaWidth.upToSmall` + padding: 1rem; + grid-template-columns: 36px 1fr; + `}; ` const HeaderControls = styled.div` @@ -62,23 +66,6 @@ const HeaderControls = styled.div` flex-direction: row; align-items: center; justify-self: flex-end; - - ${({ theme }) => theme.mediaWidth.upToMedium` - flex-direction: row; - justify-content: space-between; - justify-self: center; - width: 100%; - max-width: 960px; - padding: 1rem; - position: fixed; - bottom: 0px; - left: 0px; - width: 100%; - z-index: 99; - height: 72px; - border-radius: 12px 12px 0 0; - background-color: ${({ theme }) => theme.bg1}; - `}; ` const HeaderElement = styled.div` @@ -91,22 +78,10 @@ const HeaderElement = styled.div` } ${({ theme }) => theme.mediaWidth.upToMedium` - flex-direction: row-reverse; align-items: center; `}; ` -const HeaderElementWrap = styled.div` - display: flex; - align-items: center; -` - -const HeaderRow = styled(RowFixed)` - ${({ theme }) => theme.mediaWidth.upToMedium` - width: 100%; - `}; -` - const HeaderLinks = styled(Row)` justify-self: center; background-color: ${({ theme }) => theme.bg0}; @@ -117,8 +92,25 @@ const HeaderLinks = styled(Row)` grid-auto-flow: column; grid-gap: 10px; overflow: auto; + align-items: center; + ${({ theme }) => theme.mediaWidth.upToLarge` + justify-self: start; + `}; ${({ theme }) => theme.mediaWidth.upToMedium` - justify-self: flex-end; + justify-self: center; + `}; + ${({ theme }) => theme.mediaWidth.upToMedium` + flex-direction: row; + justify-content: space-between; + justify-self: center; + z-index: 99; + position: fixed; + bottom: 0; right: 50%; + transform: translate(50%,-50%); + margin: 0 auto; + background-color: ${({ theme }) => theme.bg0}; + border: 1px solid ${({ theme }) => theme.bg2}; + box-shadow: 0px 6px 10px rgb(0 0 0 / 2%); `}; ` @@ -126,11 +118,10 @@ const AccountElement = styled.div<{ active: boolean }>` display: flex; flex-direction: row; align-items: center; - background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg2)}; + background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg1)}; border-radius: 12px; white-space: nowrap; width: 100%; - cursor: pointer; :focus { border: 1px solid blue; @@ -160,25 +151,6 @@ const UNIWrapper = styled.span` } ` -const HideSmall = styled.span` - ${({ theme }) => theme.mediaWidth.upToSmall` - display: none; - `}; -` - -const NetworkCard = styled(YellowCard)` - border-radius: 12px; - padding: 8px 12px; - ${({ theme }) => theme.mediaWidth.upToSmall` - margin: 0; - margin-right: 0.5rem; - width: initial; - overflow: hidden; - text-overflow: ellipsis; - flex-shrink: 1; - `}; -` - const BalanceText = styled(Text)` ${({ theme }) => theme.mediaWidth.upToExtraSmall` display: none; @@ -219,14 +191,15 @@ const StyledNavLink = styled(NavLink).attrs({ text-decoration: none; color: ${({ theme }) => theme.text2}; font-size: 1rem; - width: fit-content; font-weight: 500; padding: 8px 12px; word-break: break-word; - + overflow: hidden; + white-space: nowrap; &.${activeClassName} { border-radius: 12px; font-weight: 600; + justify-content: center; color: ${({ theme }) => theme.text1}; background-color: ${({ theme }) => theme.bg2}; } @@ -263,47 +236,13 @@ const StyledExternalLink = styled(ExternalLink).attrs({ color: ${({ theme }) => darken(0.1, theme.text1)}; text-decoration: none; } - - ${({ theme }) => theme.mediaWidth.upToExtraSmall` - display: none; -`} -` - -export const StyledMenuButton = styled.button` - position: relative; - width: 100%; - height: 100%; - border: none; - background-color: transparent; - margin: 0; - padding: 0; - height: 35px; - background-color: ${({ theme }) => theme.bg2}; - margin-left: 8px; - padding: 0.15rem 0.5rem; - border-radius: 0.5rem; - - :hover, - :focus { - cursor: pointer; - outline: none; - background-color: ${({ theme }) => theme.bg4}; - } - - svg { - margin-top: 2px; - } - > * { - stroke: ${({ theme }) => theme.text1}; - } ` export default function Header() { const { account, chainId } = useActiveWeb3React() const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? ''] - // const [isDark] = useDarkModeManager() - const [darkMode, toggleDarkMode] = useDarkModeManager() + const [darkMode] = useDarkModeManager() const toggleClaimModal = useToggleSelfClaimModal() @@ -316,19 +255,18 @@ export default function Header() { const scrollY = useScrollPosition() + const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET] return ( 45}> setShowUniBalanceModal(false)}> - - - <UniIcon> - <img width={'24px'} src={darkMode ? LogoDark : Logo} alt="logo" /> - </UniIcon> - - + + <UniIcon> + <img width={'24px'} src={darkMode ? LogoDark : Logo} alt="logo" /> + </UniIcon> + Swap @@ -346,21 +284,19 @@ export default function Header() { > Pool - - Vote - - + {chainId && chainId === SupportedChainId.MAINNET && ( + + Vote + + )} + Charts + - - {chainId && chainId !== SupportedChainId.MAINNET && NETWORK_LABELS[chainId] && ( - {NETWORK_LABELS[chainId]} - )} - {availableClaim && !showClaimPopup && ( @@ -377,21 +313,16 @@ export default function Header() { )} - + {account && userEthBalance ? ( - - {userEthBalance?.toSignificant(4)} ETH + + {userEthBalance?.toSignificant(3)} ETH ) : null} - - - toggleDarkMode()}> - {darkMode ? : } - - + ) diff --git a/src/components/HoverInlineText/index.tsx b/src/components/HoverInlineText/index.tsx index d7565f595..0efbea817 100644 --- a/src/components/HoverInlineText/index.tsx +++ b/src/components/HoverInlineText/index.tsx @@ -1,5 +1,5 @@ -import Tooltip from '@src/components/Tooltip' -import React, { useState } from 'react' +import Tooltip from 'components/Tooltip' +import { useState } from 'react' import styled from 'styled-components/macro' export const TextWrapper = styled.span<{ margin: boolean; link?: boolean; fontSize?: string; adjustSize?: boolean }>` diff --git a/src/components/Identicon/index.tsx b/src/components/Identicon/index.tsx index a1e10d7eb..9b3d6154c 100644 --- a/src/components/Identicon/index.tsx +++ b/src/components/Identicon/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react' +import { useEffect, useRef } from 'react' import styled from 'styled-components/macro' diff --git a/src/components/InputStepCounter/InputStepCounter.tsx b/src/components/InputStepCounter/InputStepCounter.tsx index d7551bc0c..eec708318 100644 --- a/src/components/InputStepCounter/InputStepCounter.tsx +++ b/src/components/InputStepCounter/InputStepCounter.tsx @@ -1,14 +1,13 @@ -import React, { useState, useCallback, useEffect, ReactNode } from 'react' -import { LightCard } from 'components/Card' -import { RowBetween } from 'components/Row' +import { useState, useCallback, useEffect, ReactNode } from 'react' +import { OutlineCard } from 'components/Card' import { Input as NumericalInput } from '../NumericalInput' -import styled, { keyframes } from 'styled-components' +import styled, { keyframes } from 'styled-components/macro' import { TYPE } from 'theme' import { AutoColumn } from 'components/Column' -import { ButtonPrimary } from 'components/Button' +import { ButtonGray } from 'components/Button' import { FeeAmount } from '@uniswap/v3-sdk' -import { formattedFeeAmount } from 'utils' import { Trans } from '@lingui/macro' +import { Plus, Minus } from 'react-feather' const pulse = (color: string) => keyframes` 0% { @@ -24,25 +23,37 @@ const pulse = (color: string) => keyframes` } ` -const SmallButton = styled(ButtonPrimary)` - /* background-color: ${({ theme }) => theme.bg2}; */ +const InputRow = styled.div` + display: grid; + + grid-template-columns: 30px 1fr 30px; +` + +const SmallButton = styled(ButtonGray)` border-radius: 8px; - padding: 4px 6px; - width: 48%; + padding: 4px; ` -const FocusedOutlineCard = styled(LightCard)<{ active?: boolean; pulsing?: boolean }>` +const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boolean }>` border-color: ${({ active, theme }) => active && theme.blue1}; padding: 12px; animation: ${({ pulsing, theme }) => pulsing && pulse(theme.blue1)} 0.8s linear; ` const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>` - /* background-color: ${({ theme }) => theme.bg0}; */ + background-color: transparent; text-align: center; - margin-right: 12px; width: 100%; font-weight: 500; + padding: 0 10px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + font-size: 16px; + `}; + + ${({ theme }) => theme.mediaWidth.upToExtraSmall` + font-size: 12px; + `}; ` const InputTitle = styled(TYPE.small)` @@ -51,11 +62,17 @@ const InputTitle = styled(TYPE.small)` font-weight: 500; ` +const ButtonLabel = styled(TYPE.white)<{ disabled: boolean }>` + color: ${({ theme, disabled }) => (disabled ? theme.text2 : theme.text1)} !important; +` + interface StepCounterProps { value: string onUserInput: (value: string) => void decrement: () => string increment: () => string + decrementDisabled?: boolean + incrementDisabled?: boolean feeAmount?: FeeAmount label?: string width?: string @@ -69,7 +86,8 @@ const StepCounter = ({ value, decrement, increment, - feeAmount, + decrementDisabled = false, + incrementDisabled = false, width, locked, onUserInput, @@ -87,9 +105,6 @@ const StepCounter = ({ // animation if parent value updates local value const [pulsing, setPulsing] = useState(false) - // format fee amount - const feeAmountFormatted = feeAmount ? formattedFeeAmount(feeAmount * 2) : '' - const handleOnFocus = () => { setUseLocalValue(true) setActive(true) @@ -126,39 +141,45 @@ const StepCounter = ({ return ( - + {title} - { - setLocalValue(val) - }} - /> + + + {!locked && ( + + + + + + )} + + { + setLocalValue(val) + }} + /> + + {!locked && ( + + + + + + )} + + {tokenB} per {tokenA} - {!locked ? ( - - - - -{feeAmountFormatted}% - - - - - +{feeAmountFormatted}% - - - - ) : null} ) } diff --git a/src/components/LineChart/index.tsx b/src/components/LineChart/index.tsx index b32c0971c..eb2ac0d00 100644 --- a/src/components/LineChart/index.tsx +++ b/src/components/LineChart/index.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState, useEffect, useCallback, Dispatch, SetStateAction, ReactNode } from 'react' +import { useRef, useState, useEffect, useCallback, Dispatch, SetStateAction, ReactNode } from 'react' import { createChart, IChartApi } from 'lightweight-charts' import { darken } from 'polished' import { RowBetween } from 'components/Row' @@ -73,11 +73,11 @@ const LineChart = ({ useEffect(() => { if (!chartCreated && data && !!chartRef?.current?.parentElement) { const chart = createChart(chartRef.current, { - height: height, + height, width: chartRef.current.parentElement.clientWidth - 32, layout: { backgroundColor: 'transparent', - textColor: textColor, + textColor, fontFamily: 'Inter', }, rightPriceScale: { diff --git a/src/components/LiquidityChartRangeInput/Area.tsx b/src/components/LiquidityChartRangeInput/Area.tsx new file mode 100644 index 000000000..132276d85 --- /dev/null +++ b/src/components/LiquidityChartRangeInput/Area.tsx @@ -0,0 +1,44 @@ +import { useMemo } from 'react' +import { area, curveStepAfter, ScaleLinear } from 'd3' +import styled from 'styled-components/macro' +import { ChartEntry } from './types' +import inRange from 'lodash/inRange' + +const Path = styled.path<{ fill: string | undefined }>` + opacity: 0.5; + stroke: ${({ fill, theme }) => fill ?? theme.blue2}; + fill: ${({ fill, theme }) => fill ?? theme.blue2}; +` + +export const Area = ({ + series, + xScale, + yScale, + xValue, + yValue, + fill, +}: { + series: ChartEntry[] + xScale: ScaleLinear + yScale: ScaleLinear + xValue: (d: ChartEntry) => number + yValue: (d: ChartEntry) => number + fill?: string | undefined +}) => + useMemo( + () => ( + xScale(xValue(d as ChartEntry))) + .y1((d: unknown) => yScale(yValue(d as ChartEntry))) + .y0(yScale(0))( + series.filter((d) => inRange(xScale(xValue(d)), 0, innerWidth)) as Iterable<[number, number]> + ) ?? undefined + } + /> + ), + [fill, series, xScale, xValue, yScale, yValue] + ) diff --git a/src/components/LiquidityChartRangeInput/AxisBottom.tsx b/src/components/LiquidityChartRangeInput/AxisBottom.tsx new file mode 100644 index 000000000..5794249c5 --- /dev/null +++ b/src/components/LiquidityChartRangeInput/AxisBottom.tsx @@ -0,0 +1,43 @@ +import { useMemo } from 'react' +import { Axis as d3Axis, axisBottom, NumberValue, ScaleLinear, select } from 'd3' +import styled from 'styled-components/macro' + +const StyledGroup = styled.g` + line { + display: none; + } + + text { + color: ${({ theme }) => theme.text2}; + transform: translateY(5px); + } +` + +const Axis = ({ axisGenerator }: { axisGenerator: d3Axis }) => { + const axisRef = (axis: SVGGElement) => { + axis && + select(axis) + .call(axisGenerator) + .call((g) => g.select('.domain').remove()) + } + + return +} + +export const AxisBottom = ({ + xScale, + innerHeight, + offset = 0, +}: { + xScale: ScaleLinear + innerHeight: number + offset?: number +}) => + useMemo( + () => ( + + + + ), + [innerHeight, offset, xScale] + ) diff --git a/src/components/LiquidityChartRangeInput/Brush.tsx b/src/components/LiquidityChartRangeInput/Brush.tsx new file mode 100644 index 000000000..ae1354d00 --- /dev/null +++ b/src/components/LiquidityChartRangeInput/Brush.tsx @@ -0,0 +1,265 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { BrushBehavior, brushX, D3BrushEvent, ScaleLinear, select } from 'd3' +import styled from 'styled-components/macro' +import { brushHandleAccentPath, brushHandlePath, OffScreenHandle } from 'components/LiquidityChartRangeInput/svg' +import usePrevious from 'hooks/usePrevious' + +const Handle = styled.path<{ color: string }>` + cursor: ew-resize; + pointer-events: none; + + stroke-width: 3; + stroke: ${({ color }) => color}; + fill: ${({ color }) => color}; +` + +const HandleAccent = styled.path` + cursor: ew-resize; + pointer-events: none; + + stroke-width: 1.5; + stroke: ${({ theme }) => theme.white}; + opacity: 0.6; +` + +const LabelGroup = styled.g<{ visible: boolean }>` + opacity: ${({ visible }) => (visible ? '1' : '0')}; + transition: opacity 300ms; +` + +const TooltipBackground = styled.rect` + fill: ${({ theme }) => theme.bg2}; +` + +const Tooltip = styled.text` + text-anchor: middle; + font-size: 13px; + fill: ${({ theme }) => theme.text1}; +` + +// flips the handles draggers when close to the container edges +const FLIP_HANDLE_THRESHOLD_PX = 20 + +// margin to prevent tick snapping from putting the brush off screen +const BRUSH_EXTENT_MARGIN_PX = 2 + +const compare = (a1: [number, number], a2: [number, number], xScale: ScaleLinear): boolean => + xScale(a1[0]) !== xScale(a2[0]) || xScale(a1[1]) !== xScale(a2[1]) + +export const Brush = ({ + id, + xScale, + interactive, + brushLabelValue, + brushExtent, + setBrushExtent, + innerWidth, + innerHeight, + westHandleColor, + eastHandleColor, +}: { + id: string + xScale: ScaleLinear + interactive: boolean + brushLabelValue: (d: 'w' | 'e', x: number) => string + brushExtent: [number, number] + setBrushExtent: (extent: [number, number], mode: string | undefined) => void + innerWidth: number + innerHeight: number + westHandleColor: string + eastHandleColor: string +}) => { + const brushRef = useRef(null) + const brushBehavior = useRef | null>(null) + + // only used to drag the handles on brush for performance + const [localBrushExtent, setLocalBrushExtent] = useState<[number, number] | null>(brushExtent) + const [showLabels, setShowLabels] = useState(false) + const [hovering, setHovering] = useState(false) + + const previousBrushExtent = usePrevious(brushExtent) + + const brushed = useCallback( + (event: D3BrushEvent) => { + const { type, selection, mode } = event + + if (!selection) { + setLocalBrushExtent(null) + return + } + + const scaled = (selection as [number, number]).map(xScale.invert) as [number, number] + + // avoid infinite render loop by checking for change + if (type === 'end' && compare(brushExtent, scaled, xScale)) { + setBrushExtent(scaled, mode) + } + + setLocalBrushExtent(scaled) + }, + [xScale, brushExtent, setBrushExtent] + ) + + // keep local and external brush extent in sync + // i.e. snap to ticks on bruhs end + useEffect(() => { + setLocalBrushExtent(brushExtent) + }, [brushExtent]) + + // initialize the brush + useEffect(() => { + if (!brushRef.current) return + + brushBehavior.current = brushX() + .extent([ + [Math.max(0 + BRUSH_EXTENT_MARGIN_PX, xScale(0)), 0], + [innerWidth - BRUSH_EXTENT_MARGIN_PX, innerHeight], + ]) + .handleSize(30) + .filter(() => interactive) + .on('brush end', brushed) + + brushBehavior.current(select(brushRef.current)) + + if (previousBrushExtent && compare(brushExtent, previousBrushExtent, xScale)) { + select(brushRef.current) + .transition() + .call(brushBehavior.current.move as any, brushExtent.map(xScale)) + } + + // brush linear gradient + select(brushRef.current) + .selectAll('.selection') + .attr('stroke', 'none') + .attr('fill-opacity', '0.1') + .attr('fill', `url(#${id}-gradient-selection)`) + }, [brushExtent, brushed, id, innerHeight, innerWidth, interactive, previousBrushExtent, xScale]) + + // respond to xScale changes only + useEffect(() => { + if (!brushRef.current || !brushBehavior.current) return + + brushBehavior.current.move(select(brushRef.current) as any, brushExtent.map(xScale) as any) + }, [brushExtent, xScale]) + + // show labels when local brush changes + useEffect(() => { + setShowLabels(true) + const timeout = setTimeout(() => setShowLabels(false), 1500) + return () => clearTimeout(timeout) + }, [localBrushExtent]) + + // variables to help render the SVGs + const flipWestHandle = localBrushExtent && xScale(localBrushExtent[0]) > FLIP_HANDLE_THRESHOLD_PX + const flipEastHandle = localBrushExtent && xScale(localBrushExtent[1]) > innerWidth - FLIP_HANDLE_THRESHOLD_PX + + const showWestArrow = localBrushExtent && (xScale(localBrushExtent[0]) < 0 || xScale(localBrushExtent[1]) < 0) + const showEastArrow = + localBrushExtent && (xScale(localBrushExtent[0]) > innerWidth || xScale(localBrushExtent[1]) > innerWidth) + + const westHandleInView = + localBrushExtent && xScale(localBrushExtent[0]) >= 0 && xScale(localBrushExtent[0]) <= innerWidth + const eastHandleInView = + localBrushExtent && xScale(localBrushExtent[1]) >= 0 && xScale(localBrushExtent[1]) <= innerWidth + + return useMemo( + () => ( + <> + + + + + + + {/* clips at exactly the svg area */} + + + + + + {/* will host the d3 brush */} + setHovering(true)} + onMouseLeave={() => setHovering(false)} + /> + + {/* custom brush handles */} + {localBrushExtent && ( + <> + {/* west handle */} + {westHandleInView ? ( + + + + + + + + + + {brushLabelValue('w', localBrushExtent[0])} + + + + ) : null} + + {/* east handle */} + {eastHandleInView ? ( + + + + + + + + + + {brushLabelValue('e', localBrushExtent[1])} + + + + ) : null} + + {showWestArrow && } + + {showEastArrow && ( + + + + )} + + )} + + ), + [ + brushLabelValue, + eastHandleColor, + eastHandleInView, + flipEastHandle, + flipWestHandle, + hovering, + id, + innerHeight, + innerWidth, + localBrushExtent, + showEastArrow, + showLabels, + showWestArrow, + westHandleColor, + westHandleInView, + xScale, + ] + ) +} diff --git a/src/components/LiquidityChartRangeInput/Chart.tsx b/src/components/LiquidityChartRangeInput/Chart.tsx new file mode 100644 index 000000000..c0ea45799 --- /dev/null +++ b/src/components/LiquidityChartRangeInput/Chart.tsx @@ -0,0 +1,146 @@ +import { max, scaleLinear, ZoomTransform } from 'd3' +import { useEffect, useMemo, useRef, useState } from 'react' +import { Bound } from 'state/mint/v3/actions' +import { Area } from './Area' +import { AxisBottom } from './AxisBottom' +import { Brush } from './Brush' +import { Line } from './Line' +import { ChartEntry, LiquidityChartRangeInputProps } from './types' +import Zoom, { ZoomOverlay } from './Zoom' + +export const xAccessor = (d: ChartEntry) => d.price0 +export const yAccessor = (d: ChartEntry) => d.activeLiquidity + +export function Chart({ + id = 'liquidityChartRangeInput', + data: { series, current }, + ticksAtLimit, + styles, + dimensions: { width, height }, + margins, + interactive = true, + brushDomain, + brushLabels, + onBrushDomainChange, + zoomLevels, +}: LiquidityChartRangeInputProps) { + const zoomRef = useRef(null) + + const [zoom, setZoom] = useState(null) + + const [innerHeight, innerWidth] = useMemo( + () => [height - margins.top - margins.bottom, width - margins.left - margins.right], + [width, height, margins] + ) + + const { xScale, yScale } = useMemo(() => { + const scales = { + xScale: scaleLinear() + .domain([current * zoomLevels.initialMin, current * zoomLevels.initialMax] as number[]) + .range([0, innerWidth]), + yScale: scaleLinear() + .domain([0, max(series, yAccessor)] as number[]) + .range([innerHeight, 0]), + } + + if (zoom) { + const newXscale = zoom.rescaleX(scales.xScale) + scales.xScale.domain(newXscale.domain()) + } + + return scales + }, [current, zoomLevels.initialMin, zoomLevels.initialMax, innerWidth, series, innerHeight, zoom]) + + useEffect(() => { + // reset zoom as necessary + setZoom(null) + }, [zoomLevels]) + + useEffect(() => { + if (!brushDomain) { + onBrushDomainChange(xScale.domain() as [number, number], undefined) + } + }, [brushDomain, onBrushDomainChange, xScale]) + + return ( + <> + { + onBrushDomainChange( + [current * zoomLevels.initialMin, current * zoomLevels.initialMax] as [number, number], + 'reset' + ) + }} + showResetButton={Boolean(ticksAtLimit[Bound.LOWER] || ticksAtLimit[Bound.UPPER])} + zoomLevels={zoomLevels} + /> + + + + + + + {brushDomain && ( + // mask to highlight selected area + + + + )} + + + + + + + {brushDomain && ( + // duplicate area chart with mask for selected area + + + + )} + + + + + + + + + + + + + ) +} diff --git a/src/components/LiquidityChartRangeInput/Line.tsx b/src/components/LiquidityChartRangeInput/Line.tsx new file mode 100644 index 000000000..772d2fee3 --- /dev/null +++ b/src/components/LiquidityChartRangeInput/Line.tsx @@ -0,0 +1,24 @@ +import { useMemo } from 'react' +import { ScaleLinear } from 'd3' +import styled from 'styled-components/macro' + +const StyledLine = styled.line` + opacity: 0.5; + stroke-width: 2; + stroke: ${({ theme }) => theme.text1}; + fill: none; +` + +export const Line = ({ + value, + xScale, + innerHeight, +}: { + value: number + xScale: ScaleLinear + innerHeight: number +}) => + useMemo( + () => , + [value, xScale, innerHeight] + ) diff --git a/src/components/LiquidityChartRangeInput/Zoom.tsx b/src/components/LiquidityChartRangeInput/Zoom.tsx new file mode 100644 index 000000000..fa3ae8c1d --- /dev/null +++ b/src/components/LiquidityChartRangeInput/Zoom.tsx @@ -0,0 +1,130 @@ +import { useEffect, useMemo, useRef } from 'react' +import { ButtonGray } from 'components/Button' +import styled from 'styled-components/macro' +import { ScaleLinear, select, ZoomBehavior, zoom, ZoomTransform, zoomIdentity } from 'd3' +import { RefreshCcw, ZoomIn, ZoomOut } from 'react-feather' +import { ZoomLevels } from './types' + +const Wrapper = styled.div<{ count: number }>` + display: grid; + grid-template-columns: repeat(${({ count }) => count.toString()}, 1fr); + grid-gap: 6px; + + position: absolute; + top: -75px; + right: 0; +` + +const Button = styled(ButtonGray)` + &:hover { + background-color: ${({ theme }) => theme.bg2}; + color: ${({ theme }) => theme.text1}; + } + + width: 32px; + height: 32px; + padding: 4px; +` + +export const ZoomOverlay = styled.rect` + fill: transparent; + cursor: grab; + + &:active { + cursor: grabbing; + } +` + +export default function Zoom({ + svg, + xScale, + setZoom, + width, + height, + resetBrush, + showResetButton, + zoomLevels, +}: { + svg: SVGElement | null + xScale: ScaleLinear + setZoom: (transform: ZoomTransform) => void + width: number + height: number + resetBrush: () => void + showResetButton: boolean + zoomLevels: ZoomLevels +}) { + const zoomBehavior = useRef>() + + const [zoomIn, zoomOut, zoomInitial, zoomReset] = useMemo( + () => [ + () => + svg && + zoomBehavior.current && + select(svg as Element) + .transition() + .call(zoomBehavior.current.scaleBy, 2), + () => + svg && + zoomBehavior.current && + select(svg as Element) + .transition() + .call(zoomBehavior.current.scaleBy, 0.5), + () => + svg && + zoomBehavior.current && + select(svg as Element) + .transition() + .call(zoomBehavior.current.scaleTo, 0.5), + () => + svg && + zoomBehavior.current && + select(svg as Element) + .call(zoomBehavior.current.transform, zoomIdentity.translate(0, 0).scale(1)) + .transition() + .call(zoomBehavior.current.scaleTo, 0.5), + ], + [svg] + ) + + useEffect(() => { + if (!svg) return + + zoomBehavior.current = zoom() + .scaleExtent([zoomLevels.min, zoomLevels.max]) + .extent([ + [0, 0], + [width, height], + ]) + .on('zoom', ({ transform }: { transform: ZoomTransform }) => setZoom(transform)) + + select(svg as Element).call(zoomBehavior.current) + }, [height, width, setZoom, svg, xScale, zoomBehavior, zoomLevels, zoomLevels.max, zoomLevels.min]) + + useEffect(() => { + // reset zoom to initial on zoomLevel change + zoomInitial() + }, [zoomInitial, zoomLevels]) + + return ( + + {showResetButton && ( + + )} + + + + ) +} diff --git a/src/components/LiquidityChartRangeInput/hooks.ts b/src/components/LiquidityChartRangeInput/hooks.ts new file mode 100644 index 000000000..1e76ef61c --- /dev/null +++ b/src/components/LiquidityChartRangeInput/hooks.ts @@ -0,0 +1,56 @@ +import { useCallback, useMemo } from 'react' +import { Currency } from '@uniswap/sdk-core' +import { FeeAmount } from '@uniswap/v3-sdk' +import { usePoolActiveLiquidity } from 'hooks/usePoolTickData' +import { ChartEntry } from './types' +import JSBI from 'jsbi' + +export interface TickProcessed { + liquidityActive: JSBI + price0: string +} + +export function useDensityChartData({ + currencyA, + currencyB, + feeAmount, +}: { + currencyA: Currency | undefined + currencyB: Currency | undefined + feeAmount: FeeAmount | undefined +}) { + const { isLoading, isUninitialized, isError, error, data } = usePoolActiveLiquidity(currencyA, currencyB, feeAmount) + + const formatData = useCallback(() => { + if (!data?.length) { + return undefined + } + + const newData: ChartEntry[] = [] + + for (let i = 0; i < data.length; i++) { + const t: TickProcessed = data[i] + + const chartEntry = { + activeLiquidity: parseFloat(t.liquidityActive.toString()), + price0: parseFloat(t.price0), + } + + if (chartEntry.activeLiquidity > 0) { + newData.push(chartEntry) + } + } + + return newData + }, [data]) + + return useMemo(() => { + return { + isLoading, + isUninitialized, + isError, + error, + formattedData: !isLoading && !isUninitialized ? formatData() : undefined, + } + }, [isLoading, isUninitialized, isError, error, formatData]) +} diff --git a/src/components/LiquidityChartRangeInput/index.tsx b/src/components/LiquidityChartRangeInput/index.tsx new file mode 100644 index 000000000..36eb5edd0 --- /dev/null +++ b/src/components/LiquidityChartRangeInput/index.tsx @@ -0,0 +1,208 @@ +import { ReactNode, useCallback, useMemo } from 'react' +import { Trans } from '@lingui/macro' +import { Currency, Price, Token } from '@uniswap/sdk-core' +import { AutoColumn, ColumnCenter } from 'components/Column' +import Loader from 'components/Loader' +import { useColor } from 'hooks/useColor' +import useTheme from 'hooks/useTheme' +import { saturate } from 'polished' +import { BarChart2, Inbox, CloudOff } from 'react-feather' +import { batch } from 'react-redux' +import styled from 'styled-components/macro' +import { TYPE } from '../../theme' +import { Chart } from './Chart' +import { useDensityChartData } from './hooks' +import { format } from 'd3' +import { Bound } from 'state/mint/v3/actions' +import { FeeAmount } from '@uniswap/v3-sdk' +import ReactGA from 'react-ga' +import { ZoomLevels } from './types' + +const ZOOM_LEVELS: Record = { + [FeeAmount.LOW]: { + initialMin: 0.999, + initialMax: 1.001, + min: 0.00001, + max: 1.5, + }, + [FeeAmount.MEDIUM]: { + initialMin: 0.5, + initialMax: 2, + min: 0.00001, + max: 20, + }, + [FeeAmount.HIGH]: { + initialMin: 0.5, + initialMax: 2, + min: 0.00001, + max: 20, + }, +} + +const ChartWrapper = styled.div` + position: relative; + + justify-content: center; + align-content: center; +` + +function InfoBox({ message, icon }: { message?: ReactNode; icon: ReactNode }) { + return ( + + {icon} + {message && ( + + {message} + + )} + + ) +} + +export default function LiquidityChartRangeInput({ + currencyA, + currencyB, + feeAmount, + ticksAtLimit, + price, + priceLower, + priceUpper, + onLeftRangeInput, + onRightRangeInput, + interactive, +}: { + currencyA: Currency | undefined + currencyB: Currency | undefined + feeAmount?: FeeAmount + ticksAtLimit: { [bound in Bound]?: boolean | undefined } + price: number | undefined + priceLower?: Price + priceUpper?: Price + onLeftRangeInput: (typedValue: string) => void + onRightRangeInput: (typedValue: string) => void + interactive: boolean +}) { + const theme = useTheme() + + const tokenAColor = useColor(currencyA?.wrapped) + const tokenBColor = useColor(currencyB?.wrapped) + + const isSorted = currencyA && currencyB && currencyA?.wrapped.sortsBefore(currencyB?.wrapped) + + const { isLoading, isUninitialized, isError, error, formattedData } = useDensityChartData({ + currencyA, + currencyB, + feeAmount, + }) + + const onBrushDomainChangeEnded = useCallback( + (domain, mode) => { + let leftRangeValue = Number(domain[0]) + const rightRangeValue = Number(domain[1]) + + if (leftRangeValue <= 0) { + leftRangeValue = 1 / 10 ** 6 + } + + batch(() => { + // simulate user input for auto-formatting and other validations + if ( + (!ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER] || mode === 'handle' || mode === 'reset') && + leftRangeValue > 0 + ) { + onLeftRangeInput(leftRangeValue.toFixed(6)) + } + + if ((!ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER] || mode === 'reset') && rightRangeValue > 0) { + // todo: remove this check. Upper bound for large numbers + // sometimes fails to parse to tick. + if (rightRangeValue < 1e35) { + onRightRangeInput(rightRangeValue.toFixed(6)) + } + } + }) + }, + [isSorted, onLeftRangeInput, onRightRangeInput, ticksAtLimit] + ) + + interactive = interactive && Boolean(formattedData?.length) + + const brushDomain: [number, number] | undefined = useMemo(() => { + const leftPrice = isSorted ? priceLower : priceUpper?.invert() + const rightPrice = isSorted ? priceUpper : priceLower?.invert() + + return leftPrice && rightPrice + ? [parseFloat(leftPrice?.toSignificant(6)), parseFloat(rightPrice?.toSignificant(6))] + : undefined + }, [isSorted, priceLower, priceUpper]) + + const brushLabelValue = useCallback( + (d: 'w' | 'e', x: number) => { + if (!price) return '' + + if (d === 'w' && ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]) return '0' + if (d === 'e' && ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]) return '∞' + + const percent = (x < price ? -1 : 1) * ((Math.max(x, price) - Math.min(x, price)) / price) * 100 + + return price ? `${format(Math.abs(percent) > 1 ? '.2~s' : '.2~f')(percent)}%` : '' + }, + [isSorted, price, ticksAtLimit] + ) + + if (isError) { + ReactGA.exception({ + ...error, + category: 'Liquidity', + fatal: false, + }) + } + + return ( + + {isUninitialized ? ( + Your position will appear here.} + icon={} + /> + ) : isLoading ? ( + } /> + ) : isError ? ( + Liquidity data not available.} + icon={} + /> + ) : !formattedData || formattedData === [] || !price ? ( + There is no liquidity data.} + icon={} + /> + ) : ( + + + + )} + + ) +} diff --git a/src/components/LiquidityChartRangeInput/svg.tsx b/src/components/LiquidityChartRangeInput/svg.tsx new file mode 100644 index 000000000..fb064ab2b --- /dev/null +++ b/src/components/LiquidityChartRangeInput/svg.tsx @@ -0,0 +1,61 @@ +/* + * Generates an SVG path for the east brush handle. + * Apply `scale(-1, 1)` to generate west brush handle. + * + * |```````\ + * | | | | + * |______/ + * | + * | + * | + * | + * | + * + * https://medium.com/@dennismphil/one-side-rounded-rectangle-using-svg-fb31cf318d90 + */ +export const brushHandlePath = (height: number) => + [ + // handle + `M 0 0`, // move to origin + `v ${height}`, // vertical line + 'm 1 0', // move 1px to the right + `V 0`, // second vertical line + `M 0 1`, // move to origin + + // head + 'h 12', // horizontal line + 'q 2 0, 2 2', // rounded corner + 'v 22', // vertical line + 'q 0 2 -2 2', // rounded corner + 'h -12', // horizontal line + `z`, // close path + ].join(' ') + +export const brushHandleAccentPath = () => + [ + 'm 5 7', // move to first accent + 'v 14', // vertical line + 'M 0 0', // move to origin + 'm 9 7', // move to second accent + 'v 14', // vertical line + 'z', + ].join(' ') + +export const OffScreenHandle = ({ + color, + size = 10, + margin = 10, +}: { + color: string + size?: number + margin?: number +}) => ( + +) diff --git a/src/components/LiquidityChartRangeInput/types.ts b/src/components/LiquidityChartRangeInput/types.ts new file mode 100644 index 000000000..22a3e4d47 --- /dev/null +++ b/src/components/LiquidityChartRangeInput/types.ts @@ -0,0 +1,61 @@ +import { Bound } from 'state/mint/v3/actions' + +export interface ChartEntry { + activeLiquidity: number + price0: number +} + +export interface Dimensions { + width: number + height: number +} + +export interface Margins { + top: number + right: number + bottom: number + left: number +} + +export interface ZoomLevels { + initialMin: number + initialMax: number + min: number + max: number +} + +export interface LiquidityChartRangeInputProps { + // to distringuish between multiple charts in the DOM + id?: string + + data: { + series: ChartEntry[] + current: number + } + ticksAtLimit: { [bound in Bound]?: boolean | undefined } + + styles: { + area: { + // color of the ticks in range + selection: string + } + + brush: { + handle: { + west: string + east: string + } + } + } + + dimensions: Dimensions + margins: Margins + + interactive?: boolean + + brushLabels: (d: 'w' | 'e', x: number) => string + brushDomain: [number, number] | undefined + onBrushDomainChange: (domain: [number, number], mode: string | undefined) => void + + zoomLevels: ZoomLevels +} diff --git a/src/components/ListLogo/index.tsx b/src/components/ListLogo/index.tsx index 9b6e0c020..2b2b398e6 100644 --- a/src/components/ListLogo/index.tsx +++ b/src/components/ListLogo/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import styled from 'styled-components/macro' import useHttpLocations from '../../hooks/useHttpLocations' diff --git a/src/components/Loader/index.tsx b/src/components/Loader/index.tsx index 33c192fc3..e881d3674 100644 --- a/src/components/Loader/index.tsx +++ b/src/components/Loader/index.tsx @@ -1,6 +1,4 @@ -import React from 'react' - -import styled, { keyframes } from 'styled-components' +import styled, { keyframes } from 'styled-components/macro' const rotate = keyframes` from { diff --git a/src/components/Logo/index.tsx b/src/components/Logo/index.tsx index 2a2237222..bfb7fc3c9 100644 --- a/src/components/Logo/index.tsx +++ b/src/components/Logo/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import { Slash } from 'react-feather' import { ImageProps } from 'rebass' import useTheme from '../../hooks/useTheme' diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index e70d18b40..feb3e35a3 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -1,16 +1,22 @@ -import React, { useRef } from 'react' -import { BookOpen, Code, Info, MessageCircle, PieChart } from 'react-feather' +import { t } from '@lingui/macro' +import { useEffect, useRef, useState } from 'react' +import { BookOpen, Code, Info, MessageCircle, PieChart, Moon, Sun, Globe, ChevronLeft, Check } from 'react-feather' import { Link } from 'react-router-dom' -import styled, { css } from 'styled-components' +import styled, { css } from 'styled-components/macro' import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg' import { useActiveWeb3React } from '../../hooks/web3' import { useOnClickOutside } from '../../hooks/useOnClickOutside' import { ApplicationModal } from '../../state/application/actions' import { useModalOpen, useToggleModal } from '../../state/application/hooks' import { Trans } from '@lingui/macro' - import { ExternalLink } from 'theme' import { ButtonPrimary } from '../Button' +import { useDarkModeManager } from 'state/user/hooks' + +import { L2_CHAIN_IDS, CHAIN_INFO, SupportedChainId } from '@src/constants/chains' +import { LOCALE_LABEL, SupportedLocale, SUPPORTED_LOCALES } from 'constants/locales' +import { useLocationLinkProps } from 'hooks/useLocationLinkProps' +import { useActiveLocale } from 'hooks/useActiveLocale' export enum FlyoutAlignment { LEFT = 'LEFT', @@ -30,17 +36,18 @@ const StyledMenuButton = styled.button` background-color: transparent; margin: 0; padding: 0; - height: 35px; - background-color: ${({ theme }) => theme.bg2}; + height: 38px; + background-color: ${({ theme }) => theme.bg0}; + border: 1px solid ${({ theme }) => theme.bg0}; padding: 0.15rem 0.5rem; - border-radius: 0.5rem; + border-radius: 12px; :hover, :focus { cursor: pointer; outline: none; - background-color: ${({ theme }) => theme.bg3}; + border: 1px solid ${({ theme }) => theme.bg3}; } svg { @@ -65,15 +72,18 @@ const StyledMenu = styled.div` ` const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>` - min-width: 8.125rem; - background-color: ${({ theme }) => theme.bg2}; + min-width: 196px; + max-height: 350px; + overflow: auto; + background-color: ${({ theme }) => theme.bg1}; box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 24px 32px rgba(0, 0, 0, 0.01); + border: 1px solid ${({ theme }) => theme.bg0}; border-radius: 12px; padding: 0.5rem; display: flex; flex-direction: column; - font-size: 1rem; + font-size: 16px; position: absolute; top: 3rem; z-index: 100; @@ -86,7 +96,9 @@ const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>` left: 0rem; `}; ${({ theme }) => theme.mediaWidth.upToMedium` - top: -17.25rem; + bottom: unset; + right: 0; + left: unset; `}; ` @@ -96,15 +108,13 @@ const MenuItem = styled(ExternalLink)` flex-direction: row; align-items: center; padding: 0.5rem 0.5rem; + justify-content: space-between; color: ${({ theme }) => theme.text2}; :hover { color: ${({ theme }) => theme.text1}; cursor: pointer; text-decoration: none; } - > svg { - margin-right: 8px; - } ` const InternalMenuItem = styled(Link)` @@ -121,63 +131,161 @@ const InternalMenuItem = styled(Link)` } ` +const InternalLinkMenuItem = styled(InternalMenuItem)` + display: flex; + flex-direction: row; + align-items: center; + padding: 0.5rem 0.5rem; + justify-content: space-between; + text-decoration: none; + :hover { + color: ${({ theme }) => theme.text1}; + cursor: pointer; + text-decoration: none; + } +` + +const ToggleMenuItem = styled.button` + background-color: transparent; + margin: 0; + padding: 0; + border: none; + display: flex; + flex: 1; + flex-direction: row; + align-items: center; + padding: 0.5rem 0.5rem; + justify-content: space-between; + font-size: 1rem; + font-weight: 500; + color: ${({ theme }) => theme.text2}; + :hover { + color: ${({ theme }) => theme.text1}; + cursor: pointer; + text-decoration: none; + } +` + const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface' +function LanguageMenuItem({ locale, active, key }: { locale: SupportedLocale; active: boolean; key: string }) { + const { to, onClick } = useLocationLinkProps(locale) + + if (!to) return null + + return ( + +
{LOCALE_LABEL[locale]}
+ {active && } +
+ ) +} + +function LanguageMenu({ close }: { close: () => void }) { + const activeLocale = useActiveLocale() + + return ( + + + + + {SUPPORTED_LOCALES.map((locale) => ( + + ))} + + ) +} + export default function Menu() { - const { account } = useActiveWeb3React() + const { account, chainId } = useActiveWeb3React() const node = useRef() const open = useModalOpen(ApplicationModal.MENU) const toggle = useToggleModal(ApplicationModal.MENU) useOnClickOutside(node, open ? toggle : undefined) const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM) + const showUNIClaimOption = Boolean(!!account && !!chainId && !L2_CHAIN_IDS.includes(chainId)) + const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET] + + const [darkMode, toggleDarkMode] = useDarkModeManager() + + const [menu, setMenu] = useState<'main' | 'lang'>('main') + + useEffect(() => { + setMenu('main') + }, [open]) return ( // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451 - + - {open && ( - - - -
- About -
-
- - -
- Docs -
-
- - -
- Code -
-
- - -
- Discord -
-
- - -
- Analytics -
-
- {account && ( - - Claim UNI - - )} -
- )} + {open && + (() => { + switch (menu) { + case 'lang': + return setMenu('main')} /> + case 'main': + default: + return ( + + +
+ About +
+ +
+ +
+ Docs +
+ +
+ +
+ Code +
+ +
+ +
+ Discord +
+ +
+ +
+ Analytics +
+ +
+ setMenu('lang')}> +
+ Language +
+ +
+ toggleDarkMode()}> +
{darkMode ? Light Theme : Dark Theme}
+ {darkMode ? : } +
+ {showUNIClaimOption && ( + + Claim UNI + + )} +
+ ) + } + })()}
) } @@ -218,11 +326,11 @@ export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, men {menuItems.map(({ content, link, external }, i) => external ? ( - + {content} ) : ( - + {content} ) diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index bce0504e4..e2fa8b5b8 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled, { css } from 'styled-components' +import styled, { css } from 'styled-components/macro' import { animated, useTransition, useSpring } from 'react-spring' import { DialogOverlay, DialogContent } from '@reach/dialog' import { isMobile } from 'react-device-detect' @@ -30,7 +29,7 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r )).attrs({ 'aria-label': 'dialog', })` - overflow-y: ${({ mobile }) => (mobile ? 'scroll' : 'hidden')}; + overflow-y: auto; &[data-reach-dialog-content] { margin: 0 0 2rem 0; @@ -39,7 +38,7 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadow1)}; padding: 0px; width: 50vw; - overflow-y: ${({ mobile }) => (mobile ? 'scroll' : 'hidden')}; + overflow-y: auto; overflow-x: hidden; align-self: ${({ mobile }) => (mobile ? 'flex-end' : 'center')}; diff --git a/src/components/ModalViews/index.tsx b/src/components/ModalViews/index.tsx index 495372053..803bec467 100644 --- a/src/components/ModalViews/index.tsx +++ b/src/components/ModalViews/index.tsx @@ -1,9 +1,9 @@ -import React, { useContext } from 'react' +import { useContext } from 'react' import { useActiveWeb3React } from '../../hooks/web3' import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' import { AutoColumn, ColumnCenter } from '../Column' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { RowBetween } from '../Row' import { TYPE, CloseIcon, CustomLightSpinner } from 'theme' import { ArrowUpCircle } from 'react-feather' diff --git a/src/components/NavigationTabs/index.tsx b/src/components/NavigationTabs/index.tsx index 6d8e2a435..a7d948dac 100644 --- a/src/components/NavigationTabs/index.tsx +++ b/src/components/NavigationTabs/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import styled from 'styled-components/macro' import { darken } from 'polished' import { Trans } from '@lingui/macro' @@ -6,7 +5,7 @@ import { NavLink, Link as HistoryLink, useLocation } from 'react-router-dom' import { Percent } from '@uniswap/sdk-core' import { ArrowLeft } from 'react-feather' -import { RowBetween } from '../Row' +import Row, { RowBetween } from '../Row' import SettingsTab from '../Settings' import { useAppDispatch } from 'state/hooks' @@ -14,6 +13,8 @@ import { resetMintState } from 'state/mint/actions' import { resetMintState as resetMintV3State } from 'state/mint/v3/actions' import { TYPE } from 'theme' import useTheme from 'hooks/useTheme' +import { ReactNode } from 'react' +import { Box } from 'rebass' const Tabs = styled.div` ${({ theme }) => theme.flexRowNoWrap} @@ -50,6 +51,15 @@ const StyledNavLink = styled(NavLink).attrs({ } ` +const StyledHistoryLink = styled(HistoryLink)<{ flex: string | undefined }>` + flex: ${({ flex }) => flex ?? 'none'}; + + ${({ theme }) => theme.mediaWidth.upToMedium` + flex: none; + margin-right: 10px; + `}; +` + const ActiveText = styled.div` font-weight: 500; font-size: 20px; @@ -75,11 +85,11 @@ export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) { export function FindPoolTabs({ origin }: { origin: string }) { return ( - + - + Import V2 Pool @@ -92,11 +102,14 @@ export function AddRemoveTabs({ creating, defaultSlippage, positionID, + children, }: { adding: boolean creating: boolean defaultSlippage: Percent positionID?: string | undefined + showBackLink?: boolean + children?: ReactNode | undefined }) { const theme = useTheme() // reset states on back @@ -111,7 +124,7 @@ export function AddRemoveTabs({ return ( - { if (adding) { @@ -120,10 +133,15 @@ export function AddRemoveTabs({ dispatch(resetMintV3State()) } }} + flex={children ? '1' : undefined} > - - + + {creating ? ( Create a pair ) : adding ? ( @@ -132,8 +150,22 @@ export function AddRemoveTabs({ Remove Liquidity )} + {children} ) } + +export function CreateProposalTabs() { + return ( + + + + + + Create Proposal + + + ) +} diff --git a/src/components/NetworkAlert/AddLiquidityNetworkAlert.tsx b/src/components/NetworkAlert/AddLiquidityNetworkAlert.tsx new file mode 100644 index 000000000..02c3854fd --- /dev/null +++ b/src/components/NetworkAlert/AddLiquidityNetworkAlert.tsx @@ -0,0 +1,134 @@ +import { Trans } from '@lingui/macro' +import { + ArbitrumWrapperBackgroundDarkMode, + ArbitrumWrapperBackgroundLightMode, + OptimismWrapperBackgroundDarkMode, + OptimismWrapperBackgroundLightMode, +} from 'components/NetworkAlert/NetworkAlert' +import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from '@src/constants/chains' +import { useActiveWeb3React } from 'hooks/web3' +import { ArrowDownCircle } from 'react-feather' +import { useArbitrumAlphaAlert, useDarkModeManager } from 'state/user/hooks' +import styled from 'styled-components/macro' +import { ExternalLink, MEDIA_WIDTHS } from 'theme' +import { ReadMoreLink } from './styles' + +const L2Icon = styled.img` + display: none; + height: 40px; + margin: auto 20px auto 4px; + width: 40px; + @media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) { + display: block; + } +` +const DesktopTextBreak = styled.div` + display: none; + @media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) { + display: block; + } +` +const Wrapper = styled.div<{ chainId: SupportedL2ChainId; darkMode: boolean; logoUrl: string }>` + ${({ chainId, darkMode }) => + [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId) + ? darkMode + ? OptimismWrapperBackgroundDarkMode + : OptimismWrapperBackgroundLightMode + : darkMode + ? ArbitrumWrapperBackgroundDarkMode + : ArbitrumWrapperBackgroundLightMode}; + border-radius: 20px; + display: flex; + flex-direction: column; + overflow: hidden; + padding: 12px; + position: relative; + width: 100%; + + :before { + background-image: url(${({ logoUrl }) => logoUrl}); + background-repeat: no-repeat; + background-size: 300px; + content: ''; + height: 300px; + opacity: 0.1; + position: absolute; + transform: rotate(25deg) translate(-90px, -40px); + width: 300px; + z-index: -1; + } + @media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) { + flex-direction: row; + padding: 16px 20px; + } +` +const Body = styled.div` + font-size: 12px; + line-height: 143%; + margin: 12px; + @media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) { + flex: 1 1 auto; + margin: auto 0; + } +` +const LinkOutCircle = styled(ArrowDownCircle)` + transform: rotate(230deg); + width: 20px; + height: 20px; + margin-left: 12px; +` +const LinkOutToBridge = styled(ExternalLink)` + align-items: center; + background-color: black; + border-radius: 16px; + color: white; + display: flex; + font-size: 14px; + justify-content: space-between; + margin: 0; + max-height: 47px; + padding: 16px 12px; + text-decoration: none; + width: auto; + :hover, + :focus, + :active { + background-color: black; + } + @media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) { + margin: auto 0 auto auto; + padding: 14px 16px; + min-width: 226px; + } +` +export function AddLiquidityNetworkAlert() { + const { chainId } = useActiveWeb3React() + const [darkMode] = useDarkModeManager() + const [arbitrumAlphaAcknowledged] = useArbitrumAlphaAlert() + + if (!chainId || !L2_CHAIN_IDS.includes(chainId) || arbitrumAlphaAcknowledged) { + return null + } + const info = CHAIN_INFO[chainId as SupportedL2ChainId] + const isOptimism = [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId) + const depositUrl = isOptimism ? `${info.bridge}?chainId=1` : info.bridge + const readMoreLink = isOptimism + ? 'https://help.uniswap.org/en/articles/5392809-how-to-deposit-tokens-to-optimism' + : 'https://help.uniswap.org/en/articles/5538618-how-to-deposit-tokens-to-arbitrum' + return ( + + + + This is an alpha release of Uniswap on the {info.label} network. + You must bridge L1 assets to the network to use them.{' '} + + Read more + + + + Deposit to {info.label} + + + + ) +} diff --git a/src/components/NetworkAlert/MinimalNetworkAlert.tsx b/src/components/NetworkAlert/MinimalNetworkAlert.tsx new file mode 100644 index 000000000..443ff2a7c --- /dev/null +++ b/src/components/NetworkAlert/MinimalNetworkAlert.tsx @@ -0,0 +1,134 @@ +import { Trans } from '@lingui/macro' +import { + ArbitrumWrapperBackgroundDarkMode, + ArbitrumWrapperBackgroundLightMode, + OptimismWrapperBackgroundDarkMode, + OptimismWrapperBackgroundLightMode, +} from 'components/NetworkAlert/NetworkAlert' +import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from '@src/constants/chains' +import { useActiveWeb3React } from 'hooks/web3' +import { ArrowDownCircle } from 'react-feather' +import { useArbitrumAlphaAlert, useDarkModeManager } from 'state/user/hooks' +import styled from 'styled-components/macro' +import { ExternalLink, MEDIA_WIDTHS } from 'theme' +import { ReadMoreLink } from './styles' + +const L2Icon = styled.img` + display: none; + height: 40px; + margin: auto 20px auto 4px; + width: 40px; + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + display: block; + } +` +const DesktopTextBreak = styled.div` + display: none; + @media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) { + display: block; + } +` +const Wrapper = styled.div<{ chainId: SupportedL2ChainId; darkMode: boolean; logoUrl: string }>` + ${({ chainId, darkMode }) => + [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId) + ? darkMode + ? OptimismWrapperBackgroundDarkMode + : OptimismWrapperBackgroundLightMode + : darkMode + ? ArbitrumWrapperBackgroundDarkMode + : ArbitrumWrapperBackgroundLightMode}; + border-radius: 20px; + display: flex; + flex-direction: column; + overflow: hidden; + padding: 12px; + position: relative; + width: 100%; + + :before { + background-image: url(${({ logoUrl }) => logoUrl}); + background-repeat: no-repeat; + background-size: 300px; + content: ''; + height: 300px; + opacity: 0.1; + position: absolute; + transform: rotate(25deg) translate(-90px, -40px); + width: 300px; + z-index: -1; + } + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + flex-direction: row; + padding: 16px 20px; + } +` +const Body = styled.div` + font-size: 12px; + line-height: 143%; + margin: 12px; + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + flex: 1 1 auto; + margin: auto 0; + } +` +const LinkOutCircle = styled(ArrowDownCircle)` + transform: rotate(230deg); + width: 20px; + height: 20px; + margin-left: 12px; +` +const LinkOutToBridge = styled(ExternalLink)` + align-items: center; + background-color: black; + border-radius: 16px; + color: white; + display: flex; + font-size: 14px; + justify-content: space-between; + margin: 0; + max-height: 47px; + padding: 16px 8px; + text-decoration: none; + width: auto; + :hover, + :focus, + :active { + background-color: black; + } + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + margin: auto 0 auto auto; + padding: 14px 17px; + min-width: 226px; + } +` +export function MinimalNetworkAlert() { + const { chainId } = useActiveWeb3React() + const [darkMode] = useDarkModeManager() + const [arbitrumAlphaAcknowledged] = useArbitrumAlphaAlert() + + if (!chainId || !L2_CHAIN_IDS.includes(chainId) || arbitrumAlphaAcknowledged) { + return null + } + const info = CHAIN_INFO[chainId as SupportedL2ChainId] + const isOptimism = [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId) + const depositUrl = isOptimism ? `${info.bridge}?chainId=1` : info.bridge + const readMoreLink = isOptimism + ? 'https://help.uniswap.org/en/articles/5392809-how-to-deposit-tokens-to-optimism' + : 'https://help.uniswap.org/en/articles/5538618-how-to-deposit-tokens-to-arbitrum' + return ( + + + + This is an alpha release of Uniswap on the {info.label} network. + You must bridge L1 assets to the network to use them.{' '} + + Read more + + + + Deposit to {info.label} + + + + ) +} diff --git a/src/components/NetworkAlert/NetworkAlert.tsx b/src/components/NetworkAlert/NetworkAlert.tsx new file mode 100644 index 000000000..67dc91bf3 --- /dev/null +++ b/src/components/NetworkAlert/NetworkAlert.tsx @@ -0,0 +1,169 @@ +import { Trans } from '@lingui/macro' +import { L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from '@src/constants/chains' +import { useActiveWeb3React } from 'hooks/web3' +import { useCallback, useState } from 'react' +import { ArrowDownCircle, X } from 'react-feather' +import { useArbitrumAlphaAlert, useDarkModeManager } from 'state/user/hooks' +import { useETHBalances } from 'state/wallet/hooks' +import styled, { css } from 'styled-components/macro' +import { ExternalLink, MEDIA_WIDTHS } from 'theme' +import { CHAIN_INFO } from '../../constants/chains' +import { ReadMoreLink } from './styles' + +const L2Icon = styled.img` + width: 40px; + height: 40px; + justify-self: center; +` +const CloseIcon = styled(X)` + cursor: pointer; + position: absolute; + top: 16px; + right: 16px; +` +const ContentWrapper = styled.div` + align-items: center; + display: grid; + grid-gap: 4px; + grid-template-columns: 40px 4fr; + grid-template-rows: auto auto; + margin: 20px 16px; + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + grid-template-columns: 42px 4fr; + grid-gap: 8px; + } +` +export const ArbitrumWrapperBackgroundDarkMode = css` + background: radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%), + radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.3) 0%, rgba(33, 114, 229, 0.3) 100%), hsla(0, 0%, 100%, 0.1); +` +export const ArbitrumWrapperBackgroundLightMode = css` + background: radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%), + radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1); +` +export const OptimismWrapperBackgroundDarkMode = css` + background: radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.2) 0%, rgba(255, 255, 255, 0.1) 100%), + radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.5) 0%, rgba(235, 0, 255, 0.345) 96%); +` +export const OptimismWrapperBackgroundLightMode = css` + background: radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%), + radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.5); +` +const RootWrapper = styled.div<{ chainId: SupportedChainId; darkMode: boolean; logoUrl: string }>` + ${({ chainId, darkMode }) => + [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId) + ? darkMode + ? OptimismWrapperBackgroundDarkMode + : OptimismWrapperBackgroundLightMode + : darkMode + ? ArbitrumWrapperBackgroundDarkMode + : ArbitrumWrapperBackgroundLightMode}; + border-radius: 20px; + display: flex; + flex-direction: column; + max-width: 480px; + min-height: 174px; + overflow: hidden; + position: relative; + width: 100%; + + :before { + background-image: url(${({ logoUrl }) => logoUrl}); + background-repeat: no-repeat; + background-size: 300px; + content: ''; + height: 300px; + opacity: 0.1; + position: absolute; + transform: rotate(25deg) translate(-90px, -40px); + width: 300px; + z-index: -1; + } +` +const Header = styled.h2` + font-weight: 600; + font-size: 20px; + margin: 0; + padding-right: 30px; +` +const Body = styled.p` + font-size: 12px; + grid-column: 1 / 3; + line-height: 143%; + margin: 0; + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + grid-column: 2 / 3; + } +` +const LinkOutCircle = styled(ArrowDownCircle)` + transform: rotate(230deg); + width: 20px; + height: 20px; +` +const LinkOutToBridge = styled(ExternalLink)` + align-items: center; + background-color: black; + border-radius: 16px; + color: white; + display: flex; + font-size: 16px; + height: 44px; + justify-content: space-between; + margin: 0 20px 20px 20px; + padding: 12px 16px; + text-decoration: none; + width: auto; + :hover, + :focus, + :active { + background-color: black; + } +` +export function NetworkAlert() { + const { account, chainId } = useActiveWeb3React() + const [darkMode] = useDarkModeManager() + const [arbitrumAlphaAcknowledged, setArbitrumAlphaAcknowledged] = useArbitrumAlphaAlert() + const [locallyDismissed, setLocallyDimissed] = useState(false) + const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? ''] + + const dismiss = useCallback(() => { + if (userEthBalance?.greaterThan(0)) { + setArbitrumAlphaAcknowledged(true) + } else { + setLocallyDimissed(true) + } + }, [setArbitrumAlphaAcknowledged, userEthBalance]) + if (!chainId || !L2_CHAIN_IDS.includes(chainId) || arbitrumAlphaAcknowledged || locallyDismissed) { + return null + } + const info = CHAIN_INFO[chainId as SupportedL2ChainId] + const isOptimism = [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId) + const depositUrl = isOptimism ? `${info.bridge}?chainId=1` : info.bridge + const readMoreLink = isOptimism + ? 'https://help.uniswap.org/en/articles/5392809-how-to-deposit-tokens-to-optimism' + : 'https://help.uniswap.org/en/articles/5538618-how-to-deposit-tokens-to-arbitrum' + return ( + + + + +
+ Uniswap on {info.label} +
+ + + This is an alpha release of Uniswap on the {info.label} network. You must bridge L1 assets to the network to + swap them. + {' '} + + Read more + + +
+ + Deposit to {info.label} + + +
+ ) +} diff --git a/src/components/NetworkAlert/styles.ts b/src/components/NetworkAlert/styles.ts new file mode 100644 index 000000000..602183226 --- /dev/null +++ b/src/components/NetworkAlert/styles.ts @@ -0,0 +1,7 @@ +import styled from 'styled-components/macro' +import { ExternalLink } from 'theme' + +export const ReadMoreLink = styled(ExternalLink)` + color: ${({ theme }) => theme.text1}; + text-decoration: underline; +` diff --git a/src/components/NumericalInput/index.tsx b/src/components/NumericalInput/index.tsx index 632ebf4d5..f60ff1808 100644 --- a/src/components/NumericalInput/index.tsx +++ b/src/components/NumericalInput/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import { HTMLProps, memo } from 'react' import styled from 'styled-components/macro' import { escapeRegExp } from '../../utils' @@ -40,7 +40,7 @@ const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: s const inputRegex = RegExp(`^\\d*(?:\\\\[.])?\\d*$`) // match escaped "." characters via in a non-capturing group -export const Input = React.memo(function InnerInput({ +export const Input = memo(function InnerInput({ value, onUserInput, placeholder, @@ -53,7 +53,7 @@ export const Input = React.memo(function InnerInput({ fontSize?: string align?: 'right' | 'left' prependSymbol?: string | undefined -} & Omit, 'ref' | 'onChange' | 'as'>) { +} & Omit, 'ref' | 'onChange' | 'as'>) { const enforcer = (nextUserInput: string) => { if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) { onUserInput(nextUserInput) diff --git a/src/components/OptimismDowntimeWarning/index.tsx b/src/components/OptimismDowntimeWarning/index.tsx new file mode 100644 index 000000000..a537a5e79 --- /dev/null +++ b/src/components/OptimismDowntimeWarning/index.tsx @@ -0,0 +1,63 @@ +import { Trans } from '@lingui/macro' +import { SupportedChainId } from '@src/constants/chains' +import { useActiveWeb3React } from 'hooks/web3' +import { AlertOctagon } from 'react-feather' +import styled from 'styled-components/macro' +import { ExternalLink } from 'theme' + +const Root = styled.div` + background-color: ${({ theme }) => theme.yellow3}; + border-radius: 18px; + color: black; + margin-top: 16px; + padding: 16px; + width: 100%; + max-width: 880px; +` +const WarningIcon = styled(AlertOctagon)` + margin: 0 8px 0 0; +` +const TitleRow = styled.div` + align-items: center; + display: flex; + flex-direction: row; + justify-content: flex-start; + margin: 0; + font-size: 20px; + font-weight: 600; + line-height: 25px; +` +const Body = styled.div` + font-size: 12px; + line-height: 15px; + margin: 8px 0 0 0; +` +const ReadMoreLink = styled(ExternalLink)` + color: black; + text-decoration: underline; +` + +export default function OptimismDowntimeWarning() { + const { chainId } = useActiveWeb3React() + if (!chainId || ![SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)) { + return null + } + + return ( + + + + Optimism Planned Downtime + + + + Optimism expects planned downtime in the near future. Unplanned downtime may also occur. While the network is + down, fees will not be generated and you will be unable to remove liquidity.{' '} + + Read more. + + + + + ) +} diff --git a/src/components/Popover/index.tsx b/src/components/Popover/index.tsx index b78ce066b..f39ff4591 100644 --- a/src/components/Popover/index.tsx +++ b/src/components/Popover/index.tsx @@ -1,6 +1,6 @@ import { Placement } from '@popperjs/core' import { transparentize } from 'polished' -import React, { useCallback, useState } from 'react' +import { useCallback, useState } from 'react' import { usePopper } from 'react-popper' import styled from 'styled-components/macro' import useInterval from '../../hooks/useInterval' diff --git a/src/components/Popups/ClaimPopup.tsx b/src/components/Popups/ClaimPopup.tsx index f319ca725..165688473 100644 --- a/src/components/Popups/ClaimPopup.tsx +++ b/src/components/Popups/ClaimPopup.tsx @@ -1,9 +1,9 @@ import { Trans } from '@lingui/macro' import { CurrencyAmount, Token } from '@uniswap/sdk-core' -import React, { useCallback, useEffect } from 'react' +import { useCallback, useEffect } from 'react' import ReactGA from 'react-ga' import { Heart, X } from 'react-feather' -import styled, { keyframes } from 'styled-components' +import styled, { keyframes } from 'styled-components/macro' import tokenLogo from '../../assets/images/token-logo.png' import { ButtonPrimary } from '../../components/Button' import { useActiveWeb3React } from '../../hooks/web3' @@ -117,7 +117,7 @@ export default function ClaimPopup() { - + Claim your UNI tokens diff --git a/src/components/Popups/ListUpdatePopup.tsx b/src/components/Popups/ListUpdatePopup.tsx index 3f0064c5d..c8744d5b6 100644 --- a/src/components/Popups/ListUpdatePopup.tsx +++ b/src/components/Popups/ListUpdatePopup.tsx @@ -3,7 +3,7 @@ import React, { useCallback, useMemo } from 'react' import ReactGA from 'react-ga' import { useDispatch } from 'react-redux' import { Text } from 'rebass' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { AppDispatch } from '../../state' import { useRemovePopup } from '../../state/application/hooks' import { acceptListUpdate } from '@src/state/lists/actions' diff --git a/src/components/Popups/PopupItem.tsx b/src/components/Popups/PopupItem.tsx index 5a92461f3..2df544616 100644 --- a/src/components/Popups/PopupItem.tsx +++ b/src/components/Popups/PopupItem.tsx @@ -1,7 +1,7 @@ -import React, { useCallback, useContext, useEffect } from 'react' +import { useCallback, useContext, useEffect } from 'react' import { X } from 'react-feather' import { useSpring } from 'react-spring/web' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { animated } from 'react-spring' import { PopupContent } from 'state/application/actions' import { useRemovePopup } from '../../state/application/hooks' diff --git a/src/components/Popups/TransactionPopup.tsx b/src/components/Popups/TransactionPopup.tsx index 1e1fc2fd3..09f395c62 100644 --- a/src/components/Popups/TransactionPopup.tsx +++ b/src/components/Popups/TransactionPopup.tsx @@ -1,6 +1,6 @@ -import React, { useContext } from 'react' +import { useContext } from 'react' import { AlertCircle, CheckCircle } from 'react-feather' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { useActiveWeb3React } from '../../hooks/web3' import { TYPE } from '../../theme' import { ExternalLink } from '../../theme/components' diff --git a/src/components/Popups/index.tsx b/src/components/Popups/index.tsx index a0ba551e4..0e993a12c 100644 --- a/src/components/Popups/index.tsx +++ b/src/components/Popups/index.tsx @@ -1,10 +1,12 @@ -import React from 'react' import styled from 'styled-components/macro' import { useActivePopups } from '../../state/application/hooks' import { AutoColumn } from '../Column' import PopupItem from 'components/Popups/PopupItem' import ClaimPopup from './ClaimPopup' import { useURLWarningVisible } from '../../state/user/hooks' +import { useActiveWeb3React } from 'hooks/web3' +import { SupportedChainId } from 'constants/chains' +import { MEDIA_WIDTHS } from 'theme' const MobilePopupWrapper = styled.div<{ height: string | number }>` position: relative; @@ -31,9 +33,13 @@ const MobilePopupInner = styled.div` } ` -const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>` +const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium + 1}px) and (max-width: ${ + MEDIA_WIDTHS.upToMedium + 500 +}px)` + +const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean; xlPadding: boolean }>` position: fixed; - top: ${({ extraPadding }) => (extraPadding ? '80px' : '88px')}; + top: ${({ extraPadding }) => (extraPadding ? '64px' : '56px')}; right: 1rem; max-width: 355px !important; width: 100%; @@ -42,6 +48,10 @@ const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>` ${({ theme }) => theme.mediaWidth.upToSmall` display: none; `}; + + ${StopOverflowQuery} { + top: ${({ extraPadding, xlPadding }) => (xlPadding ? '64px' : extraPadding ? '64px' : '56px')}; + } ` export default function Popups() { @@ -50,9 +60,13 @@ export default function Popups() { const urlWarningActive = useURLWarningVisible() + // need extra padding if network is not L1 Ethereum + const { chainId } = useActiveWeb3React() + const isNotOnMainnet = Boolean(chainId && chainId !== SupportedChainId.MAINNET) + return ( <> - + {activePopups.map((item) => ( diff --git a/src/components/PositionCard/Sushi.tsx b/src/components/PositionCard/Sushi.tsx index 0e73867ee..1c1b4bc53 100644 --- a/src/components/PositionCard/Sushi.tsx +++ b/src/components/PositionCard/Sushi.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { Token } from '@uniswap/sdk-core' import { Link } from 'react-router-dom' import { Text } from 'rebass' @@ -63,7 +62,7 @@ export default function SushiPositionCard({ tokenA, tokenB, liquidityToken, bord setShowMore(!showMore)} > @@ -188,7 +188,7 @@ export default function V2PositionCard({ pair, border, stakedBalance }: Position darken(0.06, theme.bg2)}; - } -` const StyledPositionCard = styled(LightCard)<{ bgColor: any }>` border: none; background: ${({ theme, bgColor }) => @@ -219,7 +212,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi - setShowMore(!showMore)}> + setShowMore(!showMore)}> {showMore ? ( <> Manage @@ -306,7 +299,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi - +
- Price range + Status
diff --git a/src/components/PositionListItem/index.tsx b/src/components/PositionListItem/index.tsx index c0e35dca9..a743e87a5 100644 --- a/src/components/PositionListItem/index.tsx +++ b/src/components/PositionListItem/index.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react' +import { useMemo } from 'react' import { Position } from '@uniswap/v3-sdk' import Badge from 'components/Badge' import DoubleCurrencyLogo from 'components/DoubleLogo' @@ -6,17 +6,19 @@ import { usePool } from 'hooks/usePools' import { useToken } from 'hooks/Tokens' import { Link } from 'react-router-dom' import styled from 'styled-components/macro' -import { HideSmall, MEDIA_WIDTHS, SmallOnly } from '@src/theme' +import { HideSmall, MEDIA_WIDTHS, SmallOnly } from 'theme' import { PositionDetails } from 'types/position' import { Price, Token, Percent } from '@uniswap/sdk-core' -import { formatPrice } from 'utils/formatCurrencyAmount' +import { formatTickPrice } from 'utils/formatTickPrice' import Loader from 'components/Loader' import { unwrappedToken } from 'utils/unwrappedToken' import RangeBadge from 'components/Badge/RangeBadge' -import { RowFixed } from 'components/Row' +import { RowBetween } from 'components/Row' import HoverInlineText from 'components/HoverInlineText' import { DAI, USDC, USDT, WBTC, WETH9_EXTENDED } from 'constants/tokens' import { Trans } from '@lingui/macro' +import useIsTickAtLimit from 'hooks/useIsTickAtLimit' +import { Bound } from 'state/mint/v3/actions' const LinkRow = styled(Link)` align-items: center; @@ -24,6 +26,9 @@ const LinkRow = styled(Link)` display: flex; cursor: pointer; user-select: none; + display: flex; + flex-direction: column; + justify-content: space-between; color: ${({ theme }) => theme.text1}; margin: 8px 0; @@ -32,25 +37,23 @@ const LinkRow = styled(Link)` font-weight: 500; background-color: ${({ theme }) => theme.bg1}; - &:first-of-type { - margin: 0 0 8px 0; - } &:last-of-type { margin: 8px 0 0 0; } & > div:not(:first-child) { - text-align: right; + text-align: center; } :hover { background-color: ${({ theme }) => theme.bg2}; } + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { - flex-direction: row; + /* flex-direction: row; */ } ${({ theme }) => theme.mediaWidth.upToSmall` flex-direction: column; - row-gap: 24px; + row-gap: 12px; `}; ` @@ -70,11 +73,14 @@ const RangeLineItem = styled(DataLineItem)` display: flex; flex-direction: row; align-items: center; - justify-self: flex-end; + + margin-top: 4px; + width: 100%; ${({ theme }) => theme.mediaWidth.upToSmall` - flex-direction: column; - row-gap: 4px; + background-color: ${({ theme }) => theme.bg2}; + border-radius: 12px; + padding: 8px 0; `}; ` @@ -97,6 +103,9 @@ const ExtentsText = styled.span` color: ${({ theme }) => theme.text3}; font-size: 14px; margin-right: 4px; + ${({ theme }) => theme.mediaWidth.upToSmall` + display: none; + `}; ` const PrimaryPositionIdData = styled.div` @@ -201,6 +210,8 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr return undefined }, [liquidity, pool, tickLower, tickUpper]) + const tickAtLimit = useIsTickAtLimit(feeAmount, tickLower, tickUpper) + // prices const { priceLower, priceUpper, quote, base } = getPriceOrderingFromPositionForUI(position) @@ -216,7 +227,7 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr return ( - + @@ -230,7 +241,7 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr - +
{priceLower && priceUpper ? ( @@ -239,23 +250,23 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr Min: - {formatPrice(priceLower, 5)} per{' '} - + {formatTickPrice(priceLower, tickAtLimit, Bound.LOWER)} {' '} + per {' '} {' '} - {' '} + {' '} Max: - {formatPrice(priceUpper, 5)} per{' '} - + {formatTickPrice(priceUpper, tickAtLimit, Bound.UPPER)} {' '} + per diff --git a/src/components/PositionPreview/index.tsx b/src/components/PositionPreview/index.tsx index 84785f5b4..5bde526fa 100644 --- a/src/components/PositionPreview/index.tsx +++ b/src/components/PositionPreview/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useContext, ReactNode } from 'react' +import { useState, useCallback, useContext, ReactNode } from 'react' import { Position } from '@uniswap/v3-sdk' import { LightCard } from 'components/Card' import { AutoColumn } from 'components/Column' @@ -12,19 +12,23 @@ import { Currency } from '@uniswap/sdk-core' import RateToggle from 'components/RateToggle' import DoubleCurrencyLogo from 'components/DoubleLogo' import RangeBadge from 'components/Badge/RangeBadge' -import { ThemeContext } from 'styled-components' +import { ThemeContext } from 'styled-components/macro' import JSBI from 'jsbi' +import { Bound } from 'state/mint/v3/actions' +import { formatTickPrice } from 'utils/formatTickPrice' export const PositionPreview = ({ position, title, inRange, baseCurrencyDefault, + ticksAtLimit, }: { position: Position title?: ReactNode inRange: boolean baseCurrencyDefault?: Currency | undefined + ticksAtLimit: { [bound: string]: boolean | undefined } }) => { const theme = useContext(ThemeContext) @@ -121,7 +125,11 @@ export const PositionPreview = ({ Min Price - {`${priceLower.toSignificant(5)}`} + {`${formatTickPrice( + priceLower, + ticksAtLimit, + Bound.LOWER + )}`} {quoteCurrency.symbol} per {baseCurrency.symbol} @@ -138,7 +146,11 @@ export const PositionPreview = ({ Max Price - {`${priceUpper.toSignificant(5)}`} + {`${formatTickPrice( + priceUpper, + ticksAtLimit, + Bound.UPPER + )}`} {quoteCurrency.symbol} per {baseCurrency.symbol} diff --git a/src/components/ProgressSteps/index.tsx b/src/components/ProgressSteps/index.tsx index 9d0da9cd0..3867f0c41 100644 --- a/src/components/ProgressSteps/index.tsx +++ b/src/components/ProgressSteps/index.tsx @@ -1,7 +1,7 @@ -import React, { useContext } from 'react' +import { useContext } from 'react' import styled from 'styled-components/macro' import { AutoColumn } from '../Column' -import { ThemeContext } from 'styled-components' +import { ThemeContext } from 'styled-components/macro' import { TYPE } from '../../theme' const Wrapper = styled(AutoColumn)` diff --git a/src/components/QuestionHelper/index.tsx b/src/components/QuestionHelper/index.tsx index 2d2e836b1..17147a05b 100644 --- a/src/components/QuestionHelper/index.tsx +++ b/src/components/QuestionHelper/index.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useCallback, useState } from 'react' +import { ReactNode, useCallback, useState } from 'react' import styled from 'styled-components/macro' import Tooltip from '../Tooltip' diff --git a/src/components/RangeSelector/PresetsButtons.tsx b/src/components/RangeSelector/PresetsButtons.tsx new file mode 100644 index 000000000..cb83968a8 --- /dev/null +++ b/src/components/RangeSelector/PresetsButtons.tsx @@ -0,0 +1,34 @@ +import { ButtonOutlined } from 'components/Button' +import { AutoRow } from 'components/Row' +import { TYPE } from 'theme' +import styled from 'styled-components/macro' +import { Trans } from '@lingui/macro' +import ReactGA from 'react-ga' + +const Button = styled(ButtonOutlined).attrs(() => ({ + padding: '8px', + $borderRadius: '8px', +}))` + color: ${({ theme }) => theme.text1}; + flex: 1; +` + +export default function PresetsButtons({ setFullRange }: { setFullRange: () => void }) { + return ( + + + + ) +} diff --git a/src/components/RangeSelector/index.tsx b/src/components/RangeSelector/index.tsx index 3167c6ca2..81f474267 100644 --- a/src/components/RangeSelector/index.tsx +++ b/src/components/RangeSelector/index.tsx @@ -1,8 +1,9 @@ import { Trans } from '@lingui/macro' -import React from 'react' import { Currency, Price, Token } from '@uniswap/sdk-core' import StepCounter from 'components/InputStepCounter/InputStepCounter' import { RowBetween } from 'components/Row' +import { AutoColumn } from 'components/Column' +import { Bound } from 'state/mint/v3/actions' // currencyA is the base token export default function RangeSelector({ @@ -17,6 +18,7 @@ export default function RangeSelector({ currencyA, currencyB, feeAmount, + ticksAtLimit, }: { priceLower?: Price priceUpper?: Price @@ -29,6 +31,7 @@ export default function RangeSelector({ currencyA?: Currency | null currencyB?: Currency | null feeAmount?: number + ticksAtLimit: { [bound in Bound]?: boolean | undefined } }) { const tokenA = (currencyA ?? undefined)?.wrapped const tokenB = (currencyB ?? undefined)?.wrapped @@ -38,31 +41,37 @@ export default function RangeSelector({ const rightPrice = isSorted ? priceUpper : priceLower?.invert() return ( - - Min Price} - tokenA={currencyA?.symbol} - tokenB={currencyB?.symbol} - /> - Max Price} - /> -
+ + + Min Price} + tokenA={currencyA?.symbol} + tokenB={currencyB?.symbol} + /> + Max Price} + /> + + ) } diff --git a/src/components/RateToggle/index.tsx b/src/components/RateToggle/index.tsx index 5842cafb3..13c7b93c9 100644 --- a/src/components/RateToggle/index.tsx +++ b/src/components/RateToggle/index.tsx @@ -1,5 +1,4 @@ import { Trans } from '@lingui/macro' -import React from 'react' import { Currency } from '@uniswap/sdk-core' import { ToggleElement, ToggleWrapper } from 'components/Toggle/MultiToggle' @@ -20,19 +19,13 @@ export default function RateToggle({ const isSorted = tokenA && tokenB && tokenA.sortsBefore(tokenB) return tokenA && tokenB ? ( - // eslint-disable-next-line jsx-a11y/click-events-have-key-events -
+
- {isSorted ? currencyA.symbol : currencyB.symbol} price + {isSorted ? currencyA.symbol : currencyB.symbol} - {isSorted ? currencyB.symbol : currencyA.symbol} price + {isSorted ? currencyB.symbol : currencyA.symbol}
diff --git a/src/components/SearchModal/CommonBases.tsx b/src/components/SearchModal/CommonBases.tsx index 9f0b9559c..d86f5c886 100644 --- a/src/components/SearchModal/CommonBases.tsx +++ b/src/components/SearchModal/CommonBases.tsx @@ -1,5 +1,4 @@ import { Trans } from '@lingui/macro' -import React from 'react' import { Text } from 'rebass' import { Currency } from '@uniswap/sdk-core' import styled from 'styled-components/macro' @@ -11,6 +10,12 @@ import QuestionHelper from '../QuestionHelper' import { AutoRow } from '../Row' import CurrencyLogo from 'components/CurrencyLogo' +const MobileWrapper = styled(AutoColumn)` + ${({ theme }) => theme.mediaWidth.upToSmall` + display: none; + `}; +` + const BaseWrapper = styled.div<{ disable?: boolean }>` border: 1px solid ${({ theme, disable }) => (disable ? 'transparent' : theme.bg3)}; border-radius: 10px; @@ -40,7 +45,7 @@ export default function CommonBases({ const bases = typeof chainId !== 'undefined' ? COMMON_BASES[chainId] ?? [] : [] return bases.length > 0 ? ( - + Common bases @@ -64,6 +69,6 @@ export default function CommonBases({ ) })} - + ) : null } diff --git a/src/components/SearchModal/CurrencyList.tsx b/src/components/SearchModal/CurrencyList.tsx index aee999e40..6dd9a248e 100644 --- a/src/components/SearchModal/CurrencyList.tsx +++ b/src/components/SearchModal/CurrencyList.tsx @@ -1,6 +1,6 @@ import { Trans } from '@lingui/macro' import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' -import React, { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react' +import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react' import { FixedSizeList } from 'react-window' import { Text } from 'rebass' import styled from 'styled-components/macro' @@ -16,7 +16,7 @@ import CurrencyLogo from 'components/CurrencyLogo' import { MouseoverTooltip } from '../Tooltip' import { MenuItem } from './styleds' import Loader from '../Loader' -import { isTokenOnList } from 'utils' +import { isTokenOnList } from '../../utils' import ImportRow from 'components/SearchModal/ImportRow' import { LightGreyCard } from 'components/Card' import TokenListLogo from '../../assets/svg/tokenlist.svg' @@ -104,12 +104,14 @@ function CurrencyRow({ isSelected, otherSelected, style, + showCurrencyAmount, }: { currency: Currency onSelect: () => void isSelected: boolean otherSelected: boolean style: CSSProperties + showCurrencyAmount?: boolean }) { const { account } = useActiveWeb3React() const key = currencyKey(currency) @@ -141,9 +143,11 @@ function CurrencyRow({ - - {balance ? : account ? : null} - + {showCurrencyAmount && ( + + {balance ? : account ? : null} + + )} ) } @@ -158,7 +162,7 @@ function BreakLineComponent({ style }: { style: CSSProperties }) { const theme = useTheme() return ( - + @@ -189,6 +193,7 @@ export default function CurrencyList({ fixedListRef, showImportView, setImportToken, + showCurrencyAmount, }: { height: number currencies: Currency[] @@ -199,6 +204,7 @@ export default function CurrencyList({ fixedListRef?: MutableRefObject showImportView: () => void setImportToken: (token: Token) => void + showCurrencyAmount?: boolean }) { const itemData: (Currency | BreakLine)[] = useMemo(() => { if (otherListTokens && otherListTokens?.length > 0) { @@ -237,13 +243,22 @@ export default function CurrencyList({ isSelected={isSelected} onSelect={handleSelect} otherSelected={otherSelected} + showCurrencyAmount={showCurrencyAmount} /> ) } else { return null } }, - [currencies.length, onCurrencySelect, otherCurrency, selectedCurrency, setImportToken, showImportView] + [ + currencies.length, + onCurrencySelect, + otherCurrency, + selectedCurrency, + setImportToken, + showImportView, + showCurrencyAmount, + ] ) const itemKey = useCallback((index: number, data: typeof itemData) => { diff --git a/src/components/SearchModal/CurrencySearch.tsx b/src/components/SearchModal/CurrencySearch.tsx index b729203d3..231834a12 100644 --- a/src/components/SearchModal/CurrencySearch.tsx +++ b/src/components/SearchModal/CurrencySearch.tsx @@ -1,10 +1,10 @@ import { Currency, Token } from '@uniswap/sdk-core' -import React, { KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react' import ReactGA from 'react-ga' import { t, Trans } from '@lingui/macro' import { FixedSizeList } from 'react-window' import { Text } from 'rebass' -import { GpEther as ExtendedEther } from 'constants/tokens' +import { ExtendedEther } from 'constants/tokens' import { useActiveWeb3React } from '../../hooks/web3' import { useAllTokens, useToken, useIsUserAddedToken, useSearchInactiveTokenLists } from '../../hooks/Tokens' import { CloseIcon, TYPE, ButtonText, IconWrapper } from '../../theme' @@ -48,6 +48,8 @@ interface CurrencySearchProps { onCurrencySelect: (currency: Currency) => void otherSelectedCurrency?: Currency | null showCommonBases?: boolean + showCurrencyAmount?: boolean + disableNonToken?: boolean showManageView: () => void showImportView: () => void setImportToken: (token: Token) => void @@ -58,6 +60,8 @@ export function CurrencySearch({ onCurrencySelect, otherSelectedCurrency, showCommonBases, + showCurrencyAmount, + disableNonToken, onDismiss, isOpen, showManageView, @@ -203,7 +207,7 @@ export function CurrencySearch({ {({ height }) => ( )} diff --git a/src/components/SearchModal/CurrencySearchModal.tsx b/src/components/SearchModal/CurrencySearchModal.tsx index e1d3c2026..460e4488b 100644 --- a/src/components/SearchModal/CurrencySearchModal.tsx +++ b/src/components/SearchModal/CurrencySearchModal.tsx @@ -1,5 +1,5 @@ import { Currency, Token } from '@uniswap/sdk-core' -import React, { useCallback, useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import useLast from '../../hooks/useLast' import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo' import Modal from '../Modal' @@ -10,7 +10,7 @@ import Manage from 'components/SearchModal/Manage' import { TokenList } from '@uniswap/token-lists' import { ImportList } from 'components/SearchModal/ImportList' -interface CurrencySearchModalProps { +export interface CurrencySearchModalProps { isOpen: boolean onDismiss: () => void selectedCurrency?: Currency | null @@ -18,6 +18,8 @@ interface CurrencySearchModalProps { otherSelectedCurrency?: Currency | null showCommonBases?: boolean className?: string + showCurrencyAmount?: boolean + disableNonToken?: boolean } export enum CurrencyModalView { @@ -35,6 +37,8 @@ export default function CurrencySearchModal({ otherSelectedCurrency, showCommonBases = false, className, + showCurrencyAmount = true, + disableNonToken = false, }: CurrencySearchModalProps) { const [modalView, setModalView] = useState(CurrencyModalView.manage) const lastOpen = useLast(isOpen) @@ -76,6 +80,8 @@ export default function CurrencySearchModal({ selectedCurrency={selectedCurrency} otherSelectedCurrency={otherSelectedCurrency} showCommonBases={showCommonBases} + showCurrencyAmount={showCurrencyAmount} + disableNonToken={disableNonToken} showImportView={() => setModalView(CurrencyModalView.importToken)} setImportToken={setImportToken} showManageView={() => setModalView(CurrencyModalView.manage)} diff --git a/src/components/SearchModal/ImportList.tsx b/src/components/SearchModal/ImportList.tsx index 403f79564..834233b45 100644 --- a/src/components/SearchModal/ImportList.tsx +++ b/src/components/SearchModal/ImportList.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from 'react' +import { useState, useCallback } from 'react' import styled from 'styled-components/macro' import ReactGA from 'react-ga' import { TYPE, CloseIcon } from 'theme' @@ -149,7 +149,7 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro diff --git a/src/components/SearchModal/ImportRow.tsx b/src/components/SearchModal/ImportRow.tsx index 114e64bfd..ce81ed548 100644 --- a/src/components/SearchModal/ImportRow.tsx +++ b/src/components/SearchModal/ImportRow.tsx @@ -1,4 +1,4 @@ -import React, { CSSProperties } from 'react' +import { CSSProperties } from 'react' import { Token } from '@uniswap/sdk-core' import { AutoRow, RowFixed } from 'components/Row' import { AutoColumn } from 'components/Column' diff --git a/src/components/SearchModal/ImportToken.tsx b/src/components/SearchModal/ImportToken.tsx index 5a831a484..0e52d3872 100644 --- a/src/components/SearchModal/ImportToken.tsx +++ b/src/components/SearchModal/ImportToken.tsx @@ -1,5 +1,4 @@ import { TokenList } from '@uniswap/token-lists/dist/types' -import React from 'react' import { Token, Currency } from '@uniswap/sdk-core' import styled from 'styled-components/macro' import { TYPE, CloseIcon } from 'theme' @@ -34,10 +33,11 @@ const WarningWrapper = styled(Card)<{ highWarning: boolean }>` const AddressText = styled(TYPE.blue)` font-size: 12px; + word-break: break-all; ${({ theme }) => theme.mediaWidth.upToSmall` font-size: 10px; -`} + `} ` interface ImportProps { @@ -109,7 +109,7 @@ export function ImportToken({ tokens, list, onBack, onDismiss, handleCurrencySel ) : ( - + @@ -125,7 +125,7 @@ export function ImportToken({ tokens, list, onBack, onDismiss, handleCurrencySel { tokens.map((token) => addToken(token)) diff --git a/src/components/SearchModal/Manage.tsx b/src/components/SearchModal/Manage.tsx index 01e6b9c55..37388dce2 100644 --- a/src/components/SearchModal/Manage.tsx +++ b/src/components/SearchModal/Manage.tsx @@ -1,5 +1,5 @@ import { Trans } from '@lingui/macro' -import React, { useState } from 'react' +import { useState } from 'react' import { PaddedColumn, Separator } from './styleds' import { RowBetween } from 'components/Row' import { ArrowLeft } from 'react-feather' diff --git a/src/components/SearchModal/ManageLists.tsx b/src/components/SearchModal/ManageLists.tsx index 6d4805d90..0c9fe9ffd 100644 --- a/src/components/SearchModal/ManageLists.tsx +++ b/src/components/SearchModal/ManageLists.tsx @@ -1,36 +1,34 @@ -import React, { memo, useCallback, useMemo, useRef, useState, useEffect } from 'react' -import { Settings, CheckCircle } from 'react-feather' +import { t, Trans } from '@lingui/macro' +import { TokenList } from '@uniswap/token-lists' +import Card from 'components/Card' +import { UNSUPPORTED_LIST_URLS } from '@src/constants/lists' +import { useListColor } from 'hooks/useColor' +import { useActiveWeb3React } from 'hooks/web3' +import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { CheckCircle, Settings } from 'react-feather' import ReactGA from 'react-ga' import { useAppDispatch, useAppSelector } from '@src/state/hooks' import { usePopper } from 'react-popper' import styled from 'styled-components/macro' import { useFetchListCallback } from '../../hooks/useFetchListCallback' import { useOnClickOutside } from '../../hooks/useOnClickOutside' -import { TokenList } from '@uniswap/token-lists' -import { t, Trans } from '@lingui/macro' - +import useTheme from '../../hooks/useTheme' import useToggle from '../../hooks/useToggle' -import { acceptListUpdate, removeList, disableList, enableList } from '@src/state/lists/actions' -import { useIsListActive, useAllLists, useActiveListUrls } from 'state/lists/hooks' -import { ExternalLink, LinkStyledButton, TYPE, IconWrapper } from '../../theme' +import { acceptListUpdate, disableList, enableList, removeList } from '@src/state/lists/actions' +import { useActiveListUrls, useAllLists, useIsListActive } from 'state/lists/hooks' +import { ExternalLink, IconWrapper, LinkStyledButton, TYPE } from 'theme' import listVersionLabel from '../../utils/listVersionLabel' import { parseENSAddress } from '../../utils/parseENSAddress' import uriToHttp from '../../utils/uriToHttp' import { ButtonEmpty, ButtonPrimary } from '../Button' - import Column, { AutoColumn } from '../Column' import ListLogo from '../ListLogo' -import Row, { RowFixed, RowBetween } from '../Row' -import { PaddedColumn, SearchInput, Separator, SeparatorDark } from './styleds' -import { useListColor } from 'hooks/useColor' -import useTheme from '../../hooks/useTheme' +import Row, { RowBetween, RowFixed } from '../Row' import ListToggle from '../Toggle/ListToggle' -import Card from 'components/Card' import { CurrencyModalView } from './CurrencySearchModal' -import { UNSUPPORTED_LIST_URLS } from '@src/constants/lists' +import { PaddedColumn, SearchInput, Separator, SeparatorDark } from './styleds' const Wrapper = styled(Column)` - width: 100%; height: 100%; ` @@ -80,8 +78,9 @@ const StyledListUrlText = styled(TYPE.main)<{ active: boolean }>` color: ${({ theme, active }) => (active ? theme.white : theme.text2)}; ` -const RowWrapper = styled(Row)<{ bgColor: string; active: boolean }>` +const RowWrapper = styled(Row)<{ bgColor: string; active: boolean; hasActiveTokens: boolean }>` background-color: ${({ bgColor, active, theme }) => (active ? bgColor ?? 'transparent' : theme.bg2)}; + opacity: ${({ hasActiveTokens }) => (hasActiveTokens ? 1 : 0.4)}; transition: 200ms; align-items: center; padding: 1rem; @@ -93,10 +92,18 @@ function listUrlRowHTMLId(listUrl: string) { } const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) { + const { chainId } = useActiveWeb3React() const listsByUrl = useAppSelector((state) => state.lists.byUrl) const dispatch = useAppDispatch() const { current: list, pendingUpdate: pending } = listsByUrl[listUrl] + const activeTokensOnThisChain = useMemo(() => { + if (!list || !chainId) { + return 0 + } + return list.tokens.reduce((acc, cur) => (cur.chainId === chainId ? acc + 1 : acc), 0) + }, [chainId, list]) + const theme = useTheme() const listColor = useListColor(list?.logoURI) const isActive = useIsListActive(listUrl) @@ -130,7 +137,7 @@ const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) { action: 'Start Remove List', label: listUrl, }) - if (window.prompt(`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) { + if (window.prompt(t`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) { ReactGA.event({ category: 'Lists', action: 'Confirm Remove List', @@ -161,7 +168,13 @@ const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) { if (!list) return null return ( - + 0} + bgColor={listColor} + key={listUrl} + id={listUrlRowHTMLId(listUrl)} + > {list.logoURI ? ( ) : ( @@ -173,7 +186,7 @@ const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) { - {list.tokens.length} tokens + {activeTokensOnThisChain} tokens @@ -226,20 +239,29 @@ export function ManageLists({ setImportList: (list: TokenList) => void setListUrl: (url: string) => void }) { + const { chainId } = useActiveWeb3React() const theme = useTheme() const [listUrlInput, setListUrlInput] = useState('') const lists = useAllLists() + const tokenCountByListName = useMemo>( + () => + Object.values(lists).reduce((acc, { current: list }) => { + if (!list) { + return acc + } + return { + ...acc, + [list.name]: list.tokens.reduce((count: number, token) => (token.chainId === chainId ? count + 1 : count), 0), + } + }, {}), + [chainId, lists] + ) + // sort by active but only if not visible const activeListUrls = useActiveListUrls() - const [activeCopy, setActiveCopy] = useState() - useEffect(() => { - if (!activeCopy && activeListUrls) { - setActiveCopy(activeListUrls) - } - }, [activeCopy, activeListUrls]) const handleInput = useCallback((e) => { setListUrlInput(e.target.value) @@ -258,30 +280,36 @@ export function ManageLists({ // only show loaded lists, hide unsupported lists return Boolean(lists[listUrl].current) && !Boolean(UNSUPPORTED_LIST_URLS.includes(listUrl)) }) - .sort((u1, u2) => { - const { current: l1 } = lists[u1] - const { current: l2 } = lists[u2] + .sort((listUrlA, listUrlB) => { + const { current: listA } = lists[listUrlA] + const { current: listB } = lists[listUrlB] // first filter on active lists - if (activeCopy?.includes(u1) && !activeCopy?.includes(u2)) { + if (activeListUrls?.includes(listUrlA) && !activeListUrls?.includes(listUrlB)) { return -1 } - if (!activeCopy?.includes(u1) && activeCopy?.includes(u2)) { + if (!activeListUrls?.includes(listUrlA) && activeListUrls?.includes(listUrlB)) { return 1 } - if (l1 && l2) { - return l1.name.toLowerCase() < l2.name.toLowerCase() + if (listA && listB) { + if (tokenCountByListName[listA.name] > tokenCountByListName[listB.name]) { + return -1 + } + if (tokenCountByListName[listA.name] < tokenCountByListName[listB.name]) { + return 1 + } + return listA.name.toLowerCase() < listB.name.toLowerCase() ? -1 - : l1.name.toLowerCase() === l2.name.toLowerCase() + : listA.name.toLowerCase() === listB.name.toLowerCase() ? 0 : 1 } - if (l1) return -1 - if (l2) return 1 + if (listA) return -1 + if (listB) return 1 return 0 }) - }, [lists, activeCopy]) + }, [lists, activeListUrls, tokenCountByListName]) // temporary fetched list for import flow const [tempList, setTempList] = useState() diff --git a/src/components/SearchModal/ManageTokens.tsx b/src/components/SearchModal/ManageTokens.tsx index b56d72a99..60624f758 100644 --- a/src/components/SearchModal/ManageTokens.tsx +++ b/src/components/SearchModal/ManageTokens.tsx @@ -1,4 +1,4 @@ -import React, { useRef, RefObject, useCallback, useState, useMemo } from 'react' +import { useRef, RefObject, useCallback, useState, useMemo } from 'react' import Column from 'components/Column' import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' import { PaddedColumn, Separator, SearchInput } from './styleds' diff --git a/src/components/SearchModal/SortButton.tsx b/src/components/SearchModal/SortButton.tsx index 861a47601..4a669bd7c 100644 --- a/src/components/SearchModal/SortButton.tsx +++ b/src/components/SearchModal/SortButton.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { Text } from 'rebass' import styled from 'styled-components/macro' import { RowFixed } from '../Row' diff --git a/src/components/SearchModal/styleds.tsx b/src/components/SearchModal/styleds.tsx index 539fc6b99..ddb500abf 100644 --- a/src/components/SearchModal/styleds.tsx +++ b/src/components/SearchModal/styleds.tsx @@ -1,43 +1,6 @@ import styled from 'styled-components/macro' import { AutoColumn } from '../Column' -import { RowBetween, RowFixed } from '../Row' - -export const ModalInfo = styled.div` - ${({ theme }) => theme.flexRowNoWrap} - align-items: center; - padding: 1rem 1rem; - margin: 0.25rem 0.5rem; - justify-content: center; - flex: 1; - user-select: none; -` -export const StyledMenu = styled.div` - display: flex; - justify-content: center; - align-items: center; - position: relative; - border: none; -` - -export const PopoverContainer = styled.div<{ show: boolean }>` - z-index: 100; - visibility: ${(props) => (props.show ? 'visible' : 'hidden')}; - opacity: ${(props) => (props.show ? 1 : 0)}; - transition: visibility 150ms linear, opacity 150ms linear; - background: ${({ theme }) => theme.bg2}; - border: 1px solid ${({ theme }) => theme.bg3}; - box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), - 0px 24px 32px rgba(0, 0, 0, 0.01); - color: ${({ theme }) => theme.text2}; - border-radius: 0.5rem; - padding: 1rem; - display: grid; - grid-template-rows: 1fr; - grid-gap: 8px; - font-size: 1rem; - text-align: left; - top: 80px; -` +import { RowBetween } from '../Row' export const TextDot = styled.div` height: 3px; @@ -46,10 +9,6 @@ export const TextDot = styled.div` border-radius: 50%; ` -export const FadedSpan = styled(RowFixed)` - color: ${({ theme }) => theme.primary1}; - font-size: 14px; -` export const Checkbox = styled.input` border: 1px solid ${({ theme }) => theme.red3}; height: 20px; diff --git a/src/components/Settings/index.tsx b/src/components/Settings/index.tsx index cc1d04db0..ebf47a250 100644 --- a/src/components/Settings/index.tsx +++ b/src/components/Settings/index.tsx @@ -1,9 +1,9 @@ import { t, Trans } from '@lingui/macro' -import React, { useContext, useRef, useState } from 'react' +import { useContext, useRef, useState } from 'react' import { Settings, X } from 'react-feather' import ReactGA from 'react-ga' import { Text } from 'rebass' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { useOnClickOutside } from '../../hooks/useOnClickOutside' import { ApplicationModal } from '../../state/application/actions' import { useModalOpen, useToggleSettingsMenu } from '../../state/application/hooks' @@ -173,7 +173,7 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa - + {expertMode ? ( diff --git a/src/components/Slider/index.tsx b/src/components/Slider/index.tsx index dd00c0ff0..9a51b481c 100644 --- a/src/components/Slider/index.tsx +++ b/src/components/Slider/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react' +import { useCallback } from 'react' import styled from 'styled-components/macro' const StyledRangeInput = styled.input<{ size: number }>` diff --git a/src/components/SwitchLocaleLink/index.tsx b/src/components/SwitchLocaleLink/index.tsx index b4b9b2352..25f3c1331 100644 --- a/src/components/SwitchLocaleLink/index.tsx +++ b/src/components/SwitchLocaleLink/index.tsx @@ -1,13 +1,10 @@ import { Trans } from '@lingui/macro' -import React, { useMemo } from 'react' -import ReactGA from 'react-ga' -import { useLocation } from 'react-router' +import { useMemo } from 'react' import styled from 'styled-components/macro' import { DEFAULT_LOCALE, LOCALE_LABEL, SupportedLocale } from '../../constants/locales' import { navigatorLocale, useActiveLocale } from '../../hooks/useActiveLocale' -import useParsedQueryString from '../../hooks/useParsedQueryString' import { StyledInternalLink, TYPE } from '../../theme' -import { stringify } from 'qs' +import { useLocationLinkProps } from 'hooks/useLocationLinkProps' const Container = styled(TYPE.small)` opacity: 0.6; @@ -17,46 +14,35 @@ const Container = styled(TYPE.small)` margin-top: 1rem !important; ` -export function SwitchLocaleLink() { - const activeLocale = useActiveLocale() +const useTargetLocale = (activeLocale: SupportedLocale) => { const browserLocale = useMemo(() => navigatorLocale(), []) - const location = useLocation() - const qs = useParsedQueryString() if (browserLocale && (browserLocale !== DEFAULT_LOCALE || activeLocale !== DEFAULT_LOCALE)) { - let targetLocale: SupportedLocale if (activeLocale === browserLocale) { - targetLocale = DEFAULT_LOCALE + return DEFAULT_LOCALE } else { - targetLocale = browserLocale - } - - const target = { - ...location, - search: stringify({ ...qs, lng: targetLocale }), + return browserLocale } - - return ( - - - Uniswap available in:{' '} - { - { - ReactGA.event({ - category: 'Localization', - action: 'Switch Locale', - label: `${activeLocale} -> ${targetLocale}`, - }) - }} - to={target} - > - {LOCALE_LABEL[targetLocale]} - - } - - - ) } return null } + +export function SwitchLocaleLink() { + const activeLocale = useActiveLocale() + const targetLocale = useTargetLocale(activeLocale) + + const { to, onClick } = useLocationLinkProps(targetLocale) + + if (!targetLocale || !to) return null + + return ( + + + Uniswap available in:{' '} + + {LOCALE_LABEL[targetLocale]} + + + + ) +} diff --git a/src/components/TextInput/__snapshots__/index.test.tsx.snap b/src/components/TextInput/__snapshots__/index.test.tsx.snap new file mode 100644 index 000000000..801bfeaed --- /dev/null +++ b/src/components/TextInput/__snapshots__/index.test.tsx.snap @@ -0,0 +1,130 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ResizableTextArea renders correctly 1`] = ` + + .c0 { + font-size: 12; + outline: none; + border: none; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + width: 0; + resize: none; + background-color: #D5E9F0; + -webkit-transition: color 300ms step-start; + transition: color 300ms step-start; + color: #000000; + overflow: hidden; + text-overflow: ellipsis; + font-weight: 500; + width: 100%; + line-height: 1.2; + padding: 0px; + -webkit-appearance: textfield; +} + +.c0::-webkit-search-decoration { + -webkit-appearance: none; +} + +.c0::-webkit-outer-spin-button, +.c0::-webkit-inner-spin-button { + -webkit-appearance: none; +} + +.c0::-webkit-input-placeholder { + color: #000000b8; +} + +.c0::-moz-placeholder { + color: #000000b8; +} + +.c0:-ms-input-placeholder { + color: #000000b8; +} + +.c0::placeholder { + color: #000000b8; +} + + + +`; + +exports[`TextInput renders correctly 1`] = ` + + .c0 { + font-size: 12; + outline: none; + border: none; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + width: 0; + background-color: #D5E9F0; + -webkit-transition: color 300ms step-start; + transition: color 300ms step-start; + color: #000000; + overflow: hidden; + text-overflow: ellipsis; + font-weight: 500; + width: 100%; + padding: 0px; + -webkit-appearance: textfield; +} + +.c0::-webkit-search-decoration { + -webkit-appearance: none; +} + +.c0::-webkit-outer-spin-button, +.c0::-webkit-inner-spin-button { + -webkit-appearance: none; +} + +.c0::-webkit-input-placeholder { + color: #000000b8; +} + +.c0::-moz-placeholder { + color: #000000b8; +} + +.c0:-ms-input-placeholder { + color: #000000b8; +} + +.c0::placeholder { + color: #000000b8; +} + +
+ +
+
+`; diff --git a/src/components/TextInput/index.test.tsx b/src/components/TextInput/index.test.tsx new file mode 100644 index 000000000..107a385b0 --- /dev/null +++ b/src/components/TextInput/index.test.tsx @@ -0,0 +1,68 @@ +import { TextInput, ResizingTextArea } from './' +import { render, screen, fireEvent } from 'test-utils' + +describe('TextInput', () => { + it('renders correctly', () => { + const { asFragment } = render( + null} + placeholder="Test Placeholder" + fontSize="12" + /> + ) + expect(asFragment()).toMatchSnapshot() + }) + + it('calls the handler on user input', () => { + const onUserInputSpy = jest.fn() + render( + + ) + + fireEvent.change(screen.getByPlaceholderText('Test Placeholder'), { target: { value: 'New value' } }) + + expect(onUserInputSpy).toHaveBeenCalledWith('New value') + expect(onUserInputSpy).toHaveBeenCalledTimes(1) + }) +}) + +describe('ResizableTextArea', () => { + it('renders correctly', () => { + const { asFragment } = render( + null} + placeholder="Test Placeholder" + fontSize="12" + /> + ) + expect(asFragment()).toMatchSnapshot() + }) + + it('calls the handler on user input', () => { + const onUserInputSpy = jest.fn() + render( + + ) + + fireEvent.change(screen.getByPlaceholderText('Test Placeholder'), { target: { value: 'New value' } }) + + expect(onUserInputSpy).toHaveBeenCalledWith('New value') + expect(onUserInputSpy).toHaveBeenCalledTimes(1) + }) +}) diff --git a/src/components/TextInput/index.tsx b/src/components/TextInput/index.tsx new file mode 100644 index 000000000..fbf36f280 --- /dev/null +++ b/src/components/TextInput/index.tsx @@ -0,0 +1,146 @@ +import { memo, useCallback, useRef } from 'react' +import styled from 'styled-components/macro' + +const Input = styled.input<{ error?: boolean; fontSize?: string }>` + font-size: ${({ fontSize }) => fontSize || '1.25rem'}; + outline: none; + border: none; + flex: 1 1 auto; + width: 0; + background-color: ${({ theme }) => theme.bg1}; + transition: color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')}; + color: ${({ error, theme }) => (error ? theme.red1 : theme.text1)}; + overflow: hidden; + text-overflow: ellipsis; + font-weight: 500; + width: 100%; + padding: 0px; + -webkit-appearance: textfield; + + ::-webkit-search-decoration { + -webkit-appearance: none; + } + + ::-webkit-outer-spin-button, + ::-webkit-inner-spin-button { + -webkit-appearance: none; + } + + ::placeholder { + color: ${({ theme }) => theme.text4}; + } +` + +const TextAreaInput = styled.textarea<{ error?: boolean; fontSize?: string }>` + font-size: ${({ fontSize }) => fontSize || '1.25rem'}; + outline: none; + border: none; + flex: 1 1 auto; + width: 0; + resize: none; + background-color: ${({ theme }) => theme.bg1}; + transition: color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')}; + color: ${({ error, theme }) => (error ? theme.red1 : theme.text1)}; + overflow: hidden; + text-overflow: ellipsis; + font-weight: 500; + width: 100%; + line-height: 1.2; + padding: 0px; + -webkit-appearance: textfield; + + ::-webkit-search-decoration { + -webkit-appearance: none; + } + + ::-webkit-outer-spin-button, + ::-webkit-inner-spin-button { + -webkit-appearance: none; + } + + ::placeholder { + color: ${({ theme }) => theme.text4}; + } +` + +export const TextInput = ({ + className, + value, + onUserInput, + placeholder, + fontSize, +}: { + className?: string + value: string + onUserInput: (value: string) => void + placeholder: string + fontSize: string +}) => { + const handleInput = useCallback( + (event) => { + onUserInput(event.target.value) + }, + [onUserInput] + ) + + return ( +
+ +
+ ) +} + +export const ResizingTextArea = memo( + ({ + className, + value, + onUserInput, + placeholder, + fontSize, + }: { + className?: string + value: string + onUserInput: (value: string) => void + placeholder: string + fontSize: string + }) => { + const inputRef = useRef(document.createElement('textarea')) + + const handleInput = useCallback( + (event) => { + inputRef.current.style.height = 'auto' + inputRef.current.style.height = inputRef.current.scrollHeight + 'px' + onUserInput(event.target.value) + }, + [onUserInput] + ) + + return ( + + ) + } +) + +ResizingTextArea.displayName = 'ResizingTextArea' diff --git a/src/components/ThemeColorPalette/index.tsx b/src/components/ThemeColorPalette/index.tsx index b508eb903..f287c1ef3 100644 --- a/src/components/ThemeColorPalette/index.tsx +++ b/src/components/ThemeColorPalette/index.tsx @@ -1,5 +1,5 @@ import { readableColor } from 'polished' -import React from 'react' + import styled from 'styled-components/macro' import { colors } from 'theme' diff --git a/src/components/Toggle/ListToggle.tsx b/src/components/Toggle/ListToggle.tsx index 26d264d19..01208d929 100644 --- a/src/components/Toggle/ListToggle.tsx +++ b/src/components/Toggle/ListToggle.tsx @@ -1,5 +1,4 @@ import { Trans } from '@lingui/macro' -import React from 'react' import styled from 'styled-components/macro' import { TYPE } from '../../theme' diff --git a/src/components/Toggle/index.tsx b/src/components/Toggle/index.tsx index 3a186b794..2ef6280a9 100644 --- a/src/components/Toggle/index.tsx +++ b/src/components/Toggle/index.tsx @@ -1,55 +1,58 @@ -import { WithClassName } from 'types' import { Trans } from '@lingui/macro' -import React from 'react' +import { WithClassName } from '@src/custom/types' +import { darken } from 'polished' +import { ReactNode } from 'react' import styled from 'styled-components/macro' export const ToggleElement = styled.span<{ isActive?: boolean; isOnSwitch?: boolean }>` - padding: 0.25rem 0.5rem; - border-radius: 14px; - background: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.primary1 : theme.text4) : 'none')}; - color: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.white : theme.text2) : theme.text3)}; - font-size: 1rem; - font-weight: 400; - - padding: 0.35rem 0.6rem; - border-radius: 12px; - background: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.primary1 : theme.text5) : 'none')}; - color: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.white : theme.text2) : theme.text2)}; - font-size: 1rem; + padding: 0.25rem 0.6rem; + border-radius: 9px; + background: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.primary1 : theme.bg4) : 'none')}; + color: ${({ theme, isActive }) => (isActive ? theme.white : theme.text2)}; + font-size: 14px; font-weight: ${({ isOnSwitch }) => (isOnSwitch ? '500' : '400')}; :hover { user-select: ${({ isOnSwitch }) => (isOnSwitch ? 'none' : 'initial')}; background: ${({ theme, isActive, isOnSwitch }) => - isActive ? (isOnSwitch ? theme.primary1 : theme.text5) : 'none'}; - color: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.white : theme.text3) : theme.text3)}; + isActive ? (isOnSwitch ? darken(0.05, theme.primary1) : darken(0.05, theme.bg4)) : 'none'}; + color: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.white : theme.white) : theme.text3)}; } ` const StyledToggle = styled.button<{ isActive?: boolean; activeElement?: boolean }>` border-radius: 12px; border: none; - background: ${({ theme }) => theme.bg3}; + background: ${({ theme }) => theme.bg0}; display: flex; width: fit-content; cursor: pointer; outline: none; - padding: 0; + padding: 2px; ` export interface ToggleProps extends WithClassName { id?: string isActive: boolean toggle: () => void + checked?: ReactNode + unchecked?: ReactNode } -export default function Toggle({ id, isActive, toggle, className }: ToggleProps) { +export default function Toggle({ + id, + isActive, + toggle, + checked = On, + unchecked = Off, + className, +}: ToggleProps) { return ( - On + {checked} - - Off + + {unchecked} ) diff --git a/src/components/TokenWarningModal/index.tsx b/src/components/TokenWarningModal/index.tsx index 8f2161409..a10b70a9e 100644 --- a/src/components/TokenWarningModal/index.tsx +++ b/src/components/TokenWarningModal/index.tsx @@ -1,5 +1,4 @@ import { Token } from '@uniswap/sdk-core' -import React from 'react' import Modal from '../Modal' import { ImportToken } from 'components/SearchModal/ImportToken' diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx index 9d43c67a9..9fbf16b46 100644 --- a/src/components/Tooltip/index.tsx +++ b/src/components/Tooltip/index.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useCallback, useState } from 'react' +import { ReactNode, useCallback, useState } from 'react' import styled from 'styled-components/macro' import Popover, { PopoverProps } from '../Popover' diff --git a/src/components/TransactionConfirmationModal/AnimatedConfirmation.tsx b/src/components/TransactionConfirmationModal/AnimatedConfirmation.tsx new file mode 100644 index 000000000..761bad33e --- /dev/null +++ b/src/components/TransactionConfirmationModal/AnimatedConfirmation.tsx @@ -0,0 +1,70 @@ +import useTheme from 'hooks/useTheme' +import styled, { keyframes } from 'styled-components/macro' + +const Wrapper = styled.div` + height: 90px; + width: 90px; +` + +const dash = keyframes` + 0% { + stroke-dashoffset: 1000; + } + 100% { + stroke-dashoffset: 0; + } +` + +const dashCheck = keyframes` + 0% { + stroke-dashoffset: -100; + } + 100% { + stroke-dashoffset: 900; + } +` + +const Circle = styled.circle` + stroke-dasharray: 1000; + stroke-dashoffset: 0; + -webkit-animation: ${dash} 0.9s ease-in-out; + animation: ${dash} 0.9s ease-in-out; +` + +const PolyLine = styled.polyline` + stroke-dasharray: 1000; + stroke-dashoffset: 0; + stroke-dashoffset: -100; + -webkit-animation: ${dashCheck} 0.9s 0.35s ease-in-out forwards; + animation: ${dashCheck} 0.9s 0.35s ease-in-out forwards; +` + +export default function AnimatedConfirmation() { + const theme = useTheme() + + return ( + + + + + + + ) +} diff --git a/src/components/TransactionConfirmationModal/index.tsx b/src/components/TransactionConfirmationModal/index.tsx index 0010f80fe..d37c198a8 100644 --- a/src/components/TransactionConfirmationModal/index.tsx +++ b/src/components/TransactionConfirmationModal/index.tsx @@ -1,13 +1,13 @@ import { Currency } from '@uniswap/sdk-core' -import React, { ReactNode, useContext } from 'react' -import styled, { ThemeContext } from 'styled-components' +import { ReactNode, useContext } from 'react' +import styled, { ThemeContext } from 'styled-components/macro' import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink' import Modal from '../Modal' import { ExternalLink } from 'theme' import { Text } from 'rebass' import { CloseIcon, CustomLightSpinner } from 'theme' import { RowBetween, RowFixed } from '../Row' -import { AlertTriangle, ArrowUpCircle, CheckCircle } from 'react-feather' +import { AlertCircle, AlertTriangle, ArrowUpCircle, CheckCircle } from 'react-feather' import { ButtonPrimary, ButtonLight } from '../Button' import { AutoColumn, ColumnCenter } from '../Column' import Circle from '../../assets/images/blue-loader.svg' @@ -15,6 +15,10 @@ import MetaMaskLogo from '../../assets/images/metamask.png' import { useActiveWeb3React } from 'hooks/web3' import useAddTokenToMetamask from 'hooks/useAddTokenToMetamask' import { Trans } from '@lingui/macro' +import { CHAIN_INFO, L2_CHAIN_IDS, SupportedL2ChainId } from '@src/constants/chains' +import { useIsTransactionConfirmed, useTransaction } from 'state/transactions/hooks' +import Badge from 'components/Badge' +import AnimatedConfirmation from 'components/TransactionConfirmationModal/AnimatedConfirmation' const Wrapper = styled.div` width: 100%; @@ -30,7 +34,7 @@ const BottomSection = styled(Section)` ` const ConfirmedIcon = styled(ColumnCenter)<{ inline?: boolean }>` - padding: ${({ inline }) => (inline ? '20px 0' : '60px 0;')}; + padding: ${({ inline }) => (inline ? '20px 0' : '32px 0;')}; ` const StyledLogo = styled.img` @@ -64,12 +68,10 @@ export function ConfirmationPendingContent({ Waiting For Confirmation - - - {pendingText} - - - + + {pendingText} + + Confirm this transaction in your wallet @@ -77,8 +79,7 @@ export function ConfirmationPendingContent({ ) } - -export function TransactionSubmittedContent({ +function TransactionSubmittedContent({ onDismiss, chainId, hash, @@ -206,6 +207,106 @@ export function TransactionErrorContent({ message, onDismiss }: { message: React ) } +function L2Content({ + onDismiss, + chainId, + hash, + pendingText, + inline, +}: { + onDismiss: () => void + hash: string | undefined + chainId: number + currencyToAdd?: Currency | undefined + pendingText: ReactNode + inline?: boolean // not in modal +}) { + const theme = useContext(ThemeContext) + + const transaction = useTransaction(hash) + const confirmed = useIsTransactionConfirmed(hash) + const transactionSuccess = transaction?.receipt?.status === 1 + + // convert unix time difference to seconds + const secondsToConfirm = transaction?.confirmedTime + ? (transaction.confirmedTime - transaction.addedTime) / 1000 + : undefined + + const info = CHAIN_INFO[chainId as SupportedL2ChainId] + + return ( + +
+ {!inline && ( + + + + + {info.label} + + + + + )} + + {confirmed ? ( + transactionSuccess ? ( + // + + ) : ( + + ) + ) : ( + + )} + + + + {!hash ? ( + Confirm transaction in wallet + ) : !confirmed ? ( + Transaction Submitted + ) : transactionSuccess ? ( + Success + ) : ( + Error + )} + + + {transaction?.summary ?? pendingText ?? ''} + + {chainId && hash ? ( + + + View on Explorer + + + ) : ( +
+ )} + + {!secondsToConfirm ? ( +
+ ) : ( +
+ Transaction completed in + + {secondsToConfirm} seconds 🎉 + +
+ )} +
+ + + {inline ? Return : Close} + + +
+
+
+ ) +} + interface ConfirmationModalProps { isOpen: boolean onDismiss: () => void @@ -227,12 +328,16 @@ export default function TransactionConfirmationModal({ }: ConfirmationModalProps) { const { chainId } = useActiveWeb3React() + const isL2 = Boolean(chainId && L2_CHAIN_IDS.includes(chainId)) + if (!chainId) return null // confirmation screen return ( - {attemptingTxn ? ( + {isL2 && (hash || attemptingTxn) ? ( + + ) : attemptingTxn ? ( ) : hash ? ( @@ -221,37 +226,41 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction ) : null} - - - - Transaction deadline - - - - - - 0 - ? deadlineInput - : deadline === DEFAULT_DEADLINE_FROM_NOW - ? '' - : (deadline / 60).toString() - } - onChange={(e) => parseCustomDeadline(e.target.value)} - onBlur={() => { - setDeadlineInput('') - setDeadlineError(false) - }} - color={deadlineError ? 'red' : ''} + {showCustomDeadlineRow && ( + + + + Transaction deadline + + - - - minutes - - - +
+ + + 0 + ? deadlineInput + : deadline === DEFAULT_DEADLINE_FROM_NOW + ? '' + : (deadline / 60).toString() + } + onChange={(e) => parseCustomDeadline(e.target.value)} + onBlur={() => { + setDeadlineInput('') + setDeadlineError(false) + }} + color={deadlineError ? 'red' : ''} + /> + + + minutes + + + + )} ) } diff --git a/src/components/WalletModal/Option.tsx b/src/components/WalletModal/Option.tsx index 6bcad3cd4..0438fd2c9 100644 --- a/src/components/WalletModal/Option.tsx +++ b/src/components/WalletModal/Option.tsx @@ -1,4 +1,3 @@ -import React from 'react' import styled from 'styled-components/macro' import { ExternalLink } from 'theme' diff --git a/src/components/WalletModal/PendingView.tsx b/src/components/WalletModal/PendingView.tsx index bb91a8224..abaea6cdf 100644 --- a/src/components/WalletModal/PendingView.tsx +++ b/src/components/WalletModal/PendingView.tsx @@ -1,11 +1,10 @@ import { AbstractConnector } from '@web3-react/abstract-connector' -import React from 'react' import styled from 'styled-components/macro' -import { SUPPORTED_WALLETS } from '../../constants/wallet' +import { SUPPORTED_WALLETS } from 'constants/wallet' import Option from 'components/WalletModal/Option' import { injected } from 'connectors' import { darken } from 'polished' -import Loader from '../Loader' +import Loader from 'components/Loader' import { Trans } from '@lingui/macro' const PendingSection = styled.div` diff --git a/src/components/WalletModal/index.tsx b/src/components/WalletModal/index.tsx index 0a1eddc41..5be9ff3f3 100644 --- a/src/components/WalletModal/index.tsx +++ b/src/components/WalletModal/index.tsx @@ -2,15 +2,15 @@ import { AbstractConnector } from '@web3-react/abstract-connector' import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core' import { WalletConnectConnector } from '@web3-react/walletconnect-connector' import { AutoRow } from 'components/Row' -import React, { useEffect, useState } from 'react' +import { useEffect, useState } from 'react' import { isMobile } from 'react-device-detect' import ReactGA from 'react-ga' import styled from 'styled-components/macro' import MetamaskIcon from '../../assets/images/metamask.png' import { ReactComponent as Close } from '../../assets/images/x.svg' import { fortmatic, injected, portis } from 'connectors' -import { OVERLAY_READY } from 'connectors/Fortmatic' -import { SUPPORTED_WALLETS } from 'constants/wallet' +import { OVERLAY_READY } from '../../connectors/Fortmatic' +import { SUPPORTED_WALLETS } from '../../constants/wallet' import usePrevious from '../../hooks/usePrevious' import { ApplicationModal } from '../../state/application/actions' import { useModalOpen, useWalletModalToggle } from '../../state/application/hooks' diff --git a/src/components/Web3ReactManager/index.tsx b/src/components/Web3ReactManager/index.tsx index 6c16e0df1..d41282bb0 100644 --- a/src/components/Web3ReactManager/index.tsx +++ b/src/components/Web3ReactManager/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' +import { useState, useEffect } from 'react' import { useWeb3React } from '@web3-react/core' import styled from 'styled-components/macro' import { Trans } from '@lingui/macro' diff --git a/src/components/Web3Status/index.tsx b/src/components/Web3Status/index.tsx index dfb388eda..2905e3734 100644 --- a/src/components/Web3Status/index.tsx +++ b/src/components/Web3Status/index.tsx @@ -1,10 +1,10 @@ import { AbstractConnector } from '@web3-react/abstract-connector' import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core' -import { darken, lighten } from 'polished' -import React, { useMemo } from 'react' +import { darken } from 'polished' +import { useMemo } from 'react' import { Activity } from 'react-feather' import { t, Trans } from '@lingui/macro' -import styled, { css } from 'styled-components' +import styled, { css } from 'styled-components/macro' import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg' import FortmaticIcon from '../../assets/images/fortmaticIcon.png' import PortisIcon from '../../assets/images/portisIcon.png' @@ -61,6 +61,7 @@ const Web3StatusError = styled(Web3StatusGeneric)` const Web3StatusConnect = styled(Web3StatusGeneric)<{ faded?: boolean }>` background-color: ${({ theme }) => theme.primary4}; border: none; + color: ${({ theme }) => theme.primaryText1}; font-weight: 500; @@ -86,13 +87,13 @@ const Web3StatusConnect = styled(Web3StatusGeneric)<{ faded?: boolean }>` ` const Web3StatusConnected = styled(Web3StatusGeneric)<{ pending?: boolean }>` - background-color: ${({ pending, theme }) => (pending ? theme.primary1 : theme.bg1)}; - border: 1px solid ${({ pending, theme }) => (pending ? theme.primary1 : theme.bg2)}; + background-color: ${({ pending, theme }) => (pending ? theme.primary1 : theme.bg0)}; + border: 1px solid ${({ pending, theme }) => (pending ? theme.primary1 : theme.bg1)}; color: ${({ pending, theme }) => (pending ? theme.white : theme.text1)}; font-weight: 500; :hover, :focus { - background-color: ${({ pending, theme }) => (pending ? darken(0.05, theme.primary1) : lighten(0.05, theme.bg1))}; + border: 1px solid ${({ theme }) => darken(0.05, theme.bg3)}; :focus { border: 1px solid ${({ pending, theme }) => (pending ? darken(0.1, theme.primary1) : darken(0.1, theme.bg2))}; diff --git a/src/components/claim/AddressClaimModal.tsx b/src/components/claim/AddressClaimModal.tsx index 403a00261..65e33a907 100644 --- a/src/components/claim/AddressClaimModal.tsx +++ b/src/components/claim/AddressClaimModal.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' import Modal from '../Modal' import { AutoColumn, ColumnCenter } from '../Column' @@ -132,7 +132,7 @@ export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boole disabled={!isAddress(parsedAddress ?? '') || !hasAvailableClaim} padding="16px 16px" width="100%" - borderRadius="12px" + $borderRadius="12px" mt="1rem" onClick={onClaim} > diff --git a/src/components/claim/ClaimModal.tsx b/src/components/claim/ClaimModal.tsx index 112049ce8..7055b2653 100644 --- a/src/components/claim/ClaimModal.tsx +++ b/src/components/claim/ClaimModal.tsx @@ -1,7 +1,7 @@ import JSBI from 'jsbi' import { CurrencyAmount, Token } from '@uniswap/sdk-core' import { isAddress } from 'ethers/lib/utils' -import React, { useEffect, useState } from 'react' +import { useEffect, useState } from 'react' import { Text } from 'rebass' import styled from 'styled-components/macro' import Circle from '../../assets/images/blue-loader.svg' @@ -161,7 +161,7 @@ export default function ClaimModal() { disabled={!isAddress(account ?? '')} padding="16px 16px" width="100%" - borderRadius="12px" + $borderRadius="12px" mt="1rem" onClick={onClaim} > diff --git a/src/components/earn/ClaimRewardModal.tsx b/src/components/earn/ClaimRewardModal.tsx index c9e05bb4f..d629a26cb 100644 --- a/src/components/earn/ClaimRewardModal.tsx +++ b/src/components/earn/ClaimRewardModal.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import Modal from '../Modal' import { AutoColumn } from '../Column' import styled from 'styled-components/macro' diff --git a/src/components/earn/PoolCard.tsx b/src/components/earn/PoolCard.tsx index ebcf10413..7cc1e2057 100644 --- a/src/components/earn/PoolCard.tsx +++ b/src/components/earn/PoolCard.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { AutoColumn } from '../Column' import { RowBetween } from '../Row' import styled from 'styled-components/macro' @@ -120,7 +119,7 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo }) - + {isStaking ? Manage : Deposit} @@ -135,7 +134,7 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo }) {valueOfTotalStakedAmountInUSDC ? ( ${valueOfTotalStakedAmountInUSDC.toFixed(0, { groupSeparator: ',' })} ) : ( - ${valueOfTotalStakedAmountInWETH?.toSignificant(4, { groupSeparator: ',' }) ?? '-'} ETH + {valueOfTotalStakedAmountInWETH?.toSignificant(4, { groupSeparator: ',' }) ?? '-'} ETH )}
diff --git a/src/components/earn/StakingModal.tsx b/src/components/earn/StakingModal.tsx index f97409d32..6b17476eb 100644 --- a/src/components/earn/StakingModal.tsx +++ b/src/components/earn/StakingModal.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from 'react' +import { useState, useCallback } from 'react' import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit' import useTransactionDeadline from '../../hooks/useTransactionDeadline' import { formatCurrencyAmount } from '../../utils/formatCurrencyAmount' diff --git a/src/components/earn/UnstakingModal.tsx b/src/components/earn/UnstakingModal.tsx index 9440f9f3d..1568df30b 100644 --- a/src/components/earn/UnstakingModal.tsx +++ b/src/components/earn/UnstakingModal.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import Modal from '../Modal' import { AutoColumn } from '../Column' import styled from 'styled-components/macro' diff --git a/src/components/earn/styled.ts b/src/components/earn/styled.ts index 9f558394e..948e7817e 100644 --- a/src/components/earn/styled.ts +++ b/src/components/earn/styled.ts @@ -5,17 +5,6 @@ import uImage from '../../assets/images/big_unicorn.png' import xlUnicorn from '../../assets/images/xl_uni.png' import noise from '../../assets/images/noise.png' -export const TextBox = styled.div` - display: flex; - align-items: center; - justify-content: center; - padding: 4px 12px; - border: 1px solid rgba(255, 255, 255, 0.4); - border-radius: 20px; - width: fit-content; - justify-self: flex-end; -` - export const DataCard = styled(AutoColumn)<{ disabled?: boolean }>` background: radial-gradient(76.02% 75.41% at 1.84% 0%, #ff007a 0%, #2172e5 100%); border-radius: 12px; diff --git a/src/components/swap/AdvancedSwapDetails.tsx b/src/components/swap/AdvancedSwapDetails.tsx index 399ccf579..599bde4f7 100644 --- a/src/components/swap/AdvancedSwapDetails.tsx +++ b/src/components/swap/AdvancedSwapDetails.tsx @@ -2,8 +2,8 @@ import { Trans } from '@lingui/macro' import { Percent, Currency, TradeType } from '@uniswap/sdk-core' import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk' -import React, { useContext, useMemo } from 'react' -import { ThemeContext } from 'styled-components' +import { useContext, useMemo } from 'react' +import { ThemeContext } from 'styled-components/macro' import { TYPE } from '../../theme' import { computeRealizedLPFeePercent } from '../../utils/prices' import { AutoColumn } from '../Column' @@ -29,7 +29,7 @@ export function AdvancedSwapDetails({ trade, allowedSlippage }: AdvancedSwapDeta }, [trade]) return !trade ? null : ( - + diff --git a/src/components/swap/AdvancedSwapDetailsDropdown.tsx b/src/components/swap/AdvancedSwapDetailsDropdown.tsx index ae2df3f61..a2027baa9 100644 --- a/src/components/swap/AdvancedSwapDetailsDropdown.tsx +++ b/src/components/swap/AdvancedSwapDetailsDropdown.tsx @@ -1,13 +1,21 @@ -import React from 'react' import styled from 'styled-components/macro' -import { useLastTruthy } from '../../hooks/useLast' +import { useLastTruthy } from 'hooks/useLast' import { AdvancedSwapDetails, AdvancedSwapDetailsProps } from '@src/components/swap/AdvancedSwapDetails' const AdvancedDetailsFooter = styled.div<{ show: boolean }>` + // padding-top: calc(16px + 2rem); + // padding-bottom: 16px; + // margin-top: -2rem; width: 100%; + // max-width: 400px; border-bottom-left-radius: 20px; border-bottom-right-radius: 20px; color: ${({ theme }) => theme.text2}; + // background-color: ${({ theme }) => theme.advancedBG}; + // z-index: -1; + + // transform: ${({ show }) => (show ? 'translateY(0%)' : 'translateY(-100%)')}; + // transition: transform 300ms ease-in-out; ` export default function AdvancedSwapDetailsDropdown({ trade, ...rest }: AdvancedSwapDetailsProps) { diff --git a/src/components/swap/BetterTradeLink.tsx b/src/components/swap/BetterTradeLink.tsx index 0fc0f8c43..5027a8be1 100644 --- a/src/components/swap/BetterTradeLink.tsx +++ b/src/components/swap/BetterTradeLink.tsx @@ -1,5 +1,5 @@ import { stringify } from 'qs' -import React, { useMemo } from 'react' +import { useMemo } from 'react' import { useLocation } from 'react-router' import { Link } from 'react-router-dom' diff --git a/src/components/swap/ConfirmSwapModal.tsx b/src/components/swap/ConfirmSwapModal.tsx index bfd5d4de4..233e19e5f 100644 --- a/src/components/swap/ConfirmSwapModal.tsx +++ b/src/components/swap/ConfirmSwapModal.tsx @@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro' import { Currency, Percent, TradeType } from '@uniswap/sdk-core' import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk' -import React, { ReactNode, useCallback, useMemo } from 'react' +import { ReactNode, useCallback, useMemo } from 'react' import TransactionConfirmationModal, { ConfirmationModalContent, TransactionErrorContent, diff --git a/src/components/swap/FormattedPriceImpact.tsx b/src/components/swap/FormattedPriceImpact.tsx index 5f1e16214..56896e4ae 100644 --- a/src/components/swap/FormattedPriceImpact.tsx +++ b/src/components/swap/FormattedPriceImpact.tsx @@ -1,7 +1,6 @@ import { Percent } from '@uniswap/sdk-core' -import React from 'react' import { warningSeverity } from '../../utils/prices' -import { ErrorText, ErrorPill } from './styleds' +import { ErrorText } from './styleds' /** * Formatted version of price impact text with warning colors @@ -13,11 +12,3 @@ export default function FormattedPriceImpact({ priceImpact }: { priceImpact?: Pe ) } - -export function SmallFormattedPriceImpact({ priceImpact }: { priceImpact?: Percent }) { - return ( - - {priceImpact ? `(${priceImpact.multiply(-1).toFixed(2)}%)` : '-'} - - ) -} diff --git a/src/components/swap/SwapHeader.tsx b/src/components/swap/SwapHeader.tsx index ace3ff6eb..1aff5a9eb 100644 --- a/src/components/swap/SwapHeader.tsx +++ b/src/components/swap/SwapHeader.tsx @@ -1,5 +1,4 @@ import { Trans } from '@lingui/macro' -import React from 'react' import styled from 'styled-components/macro' import SettingsTab from 'components/Settings' import { Percent } from '@uniswap/sdk-core' diff --git a/src/components/swap/SwapModalFooter.tsx b/src/components/swap/SwapModalFooter.tsx index 31c8eb0ee..290943c26 100644 --- a/src/components/swap/SwapModalFooter.tsx +++ b/src/components/swap/SwapModalFooter.tsx @@ -3,7 +3,7 @@ import { Currency, TradeType } from '@uniswap/sdk-core' import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk' -import React, { ReactNode } from 'react' +import { ReactNode } from 'react' import { Text } from 'rebass' import { ButtonError } from 'components/Button' import { AutoRow } from '../Row' diff --git a/src/components/swap/SwapModalHeader.tsx b/src/components/swap/SwapModalHeader.tsx index 17f9286d2..4e0b61d95 100644 --- a/src/components/swap/SwapModalHeader.tsx +++ b/src/components/swap/SwapModalHeader.tsx @@ -1,14 +1,14 @@ import { Currency, Percent, TradeType } from '@uniswap/sdk-core' import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk' -import React, { useContext, useState } from 'react' +import { useContext, useState } from 'react' import { ArrowDown, AlertTriangle } from 'react-feather' import { Text } from 'rebass' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { useUSDCValue } from '../../hooks/useUSDCPrice' -import { TYPE } from 'theme' +import { TYPE } from '../../theme' import { ButtonPrimary } from '../Button' -import { isAddress, shortenAddress } from 'utils' +import { isAddress, shortenAddress } from '../../utils' import { computeFiatValuePriceImpact } from '../../utils/computeFiatValuePriceImpact' import { AutoColumn } from '../Column' import { FiatValue } from 'components/CurrencyInputPanel/FiatValue' @@ -22,7 +22,7 @@ import { LightCard } from '../Card' import TradePrice from '../swap/TradePrice' -export const ArrowWrapper = styled.div` +const ArrowWrapper = styled.div` padding: 4px; border-radius: 12px; height: 32px; diff --git a/src/components/swap/SwapRoute.tsx b/src/components/swap/SwapRoute.tsx index b0c504aae..e439d61ff 100644 --- a/src/components/swap/SwapRoute.tsx +++ b/src/components/swap/SwapRoute.tsx @@ -1,10 +1,10 @@ import { Currency, TradeType } from '@uniswap/sdk-core' import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V3Trade, FeeAmount } from '@uniswap/v3-sdk' -import React, { Fragment, memo, useContext } from 'react' +import { Fragment, memo, useContext } from 'react' import { ChevronRight } from 'react-feather' import { Flex } from 'rebass' -import { ThemeContext } from 'styled-components' +import { ThemeContext } from 'styled-components/macro' import { TYPE } from 'theme' import { unwrappedToken } from 'utils/unwrappedToken' diff --git a/src/components/swap/TradePrice.tsx b/src/components/swap/TradePrice.tsx index 7379e5c21..a8836727f 100644 --- a/src/components/swap/TradePrice.tsx +++ b/src/components/swap/TradePrice.tsx @@ -1,8 +1,8 @@ -import React, { useCallback } from 'react' +import { useCallback } from 'react' import { Price, Currency } from '@uniswap/sdk-core' import { useContext } from 'react' import { Text } from 'rebass' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' interface TradePriceProps { price: Price @@ -19,7 +19,7 @@ const StyledPriceContainer = styled.button` font-weight: 400; background-color: transparent; border: none; - /* height: 24px; */ + height: 24px; cursor: pointer; ` diff --git a/src/components/swap/UnsupportedCurrencyFooter.tsx b/src/components/swap/UnsupportedCurrencyFooter.tsx index 135c9ca8d..73fcc076b 100644 --- a/src/components/swap/UnsupportedCurrencyFooter.tsx +++ b/src/components/swap/UnsupportedCurrencyFooter.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import styled from 'styled-components/macro' import { TYPE, CloseIcon, ExternalLink } from 'theme' import { ButtonEmpty } from 'components/Button' @@ -43,7 +43,7 @@ export default function UnsupportedCurrencyFooter({ currencies, }: { show: boolean - currencies: (Currency | undefined)[] + currencies: (Currency | undefined | null)[] }) { const { chainId } = useActiveWeb3React() const [showDetails, setShowDetails] = useState(false) diff --git a/src/components/swap/styleds.tsx b/src/components/swap/styleds.tsx index 1a3acb43b..fecf8d299 100644 --- a/src/components/swap/styleds.tsx +++ b/src/components/swap/styleds.tsx @@ -1,9 +1,8 @@ import { transparentize } from 'polished' -import React, { ReactNode } from 'react' -import { Link } from 'react-router-dom' +import { ReactNode } from 'react' import { AlertTriangle } from 'react-feather' -import styled, { css } from 'styled-components' +import styled, { css } from 'styled-components/macro' import { Text } from 'rebass' import { AutoColumn } from '../Column' @@ -42,11 +41,6 @@ export const SectionBreak = styled.div` background-color: ${({ theme }) => theme.bg3}; ` -export const BottomGrouping = styled.div` - margin-top: ; - /* background-color: ${({ theme }) => theme.bg1}; */ -` - export const ErrorText = styled(Text)<{ severity?: 0 | 1 | 2 | 3 | 4 }>` color: ${({ theme, severity }) => severity === 3 || severity === 4 @@ -58,55 +52,6 @@ export const ErrorText = styled(Text)<{ severity?: 0 | 1 | 2 | 3 | 4 }>` : theme.text2}; ` -export const ErrorPill = styled(Text)<{ severity?: 0 | 1 | 2 | 3 | 4 }>` - border-radius: 8px; - - color: ${({ theme, severity }) => - severity === 3 || severity === 4 - ? theme.red1 - : severity === 2 - ? theme.yellow2 - : severity === 1 - ? theme.text1 - : theme.text3}; - - /* background-color: ${({ theme, severity }) => - severity === 3 || severity === 4 - ? transparentize(0.9, theme.red1) - : severity === 2 - ? transparentize(0.9, theme.yellow2) - : severity === 1 - ? transparentize(0.9, theme.text1) - : transparentize(0.9, theme.green1)}; */ -` - -export const StyledBalanceMaxMini = styled.button` - /* height: 22px; */ - width: fit-content; - background-color: ${({ theme }) => theme.bg1}; - border: none; - border-radius: 8px; - padding: 0; - font-size: 0.875rem; - font-weight: 400; - opacity: 0.6; - margin-right: 0.5rem; - cursor: pointer; - color: ${({ theme }) => theme.text1}; - display: flex; - justify-content: center; - align-items: center; - float: right; - - :hover { - background-color: ${({ theme }) => theme.bg2}; - } - :focus { - background-color: ${({ theme }) => theme.bg2}; - outline: none; - } -` - export const TruncatedText = styled(Text)` text-overflow: ellipsis; max-width: 220px; @@ -183,19 +128,3 @@ export const SwapShowAcceptChanges = styled(AutoColumn)` border-radius: 12px; margin-top: 8px; ` -export const Separator = styled.div` - width: 100%; - height: 1px; - background-color: ${({ theme }) => theme.bg2}; -` - -export const V2TradeAlertWrapper = styled(Link)` - background-color: ${({ theme }) => theme.bg2}; - display: flex; - align-items: center; - border-radius: 12px; - height: 22px; - margin-right: 0.5rem; - padding: 0 0.25rem 0 0.5rem; - text-decoration: none !important; -` diff --git a/src/components/vote/DelegateModal.tsx b/src/components/vote/DelegateModal.tsx index c01d32b73..a2c1b237c 100644 --- a/src/components/vote/DelegateModal.tsx +++ b/src/components/vote/DelegateModal.tsx @@ -1,5 +1,5 @@ -import React, { ReactNode, useState } from 'react' -import { UNI } from 'constants/tokens' +import { ReactNode, useState } from 'react' +import { UNI } from '../../constants/tokens' import Modal from '../Modal' import { AutoColumn } from '../Column' diff --git a/src/components/vote/ProposalEmptyState.tsx b/src/components/vote/ProposalEmptyState.tsx new file mode 100644 index 000000000..02304cb10 --- /dev/null +++ b/src/components/vote/ProposalEmptyState.tsx @@ -0,0 +1,60 @@ +import { Trans } from '@lingui/macro' +import { L2_CHAIN_IDS } from 'constants/chains' +import { useActiveWeb3React } from 'hooks/web3' +import styled from 'styled-components/macro' +import { TYPE } from 'theme' + +const EmptyProposals = styled.div` + border: 1px solid ${({ theme }) => theme.text4}; + padding: 16px 12px; + border-radius: 12px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +` +const Sub = styled.i` + align-items: center; + display: flex; + justify-content: center; + text-align: center; +` +interface EmptyStateProps { + HeaderContent: () => JSX.Element + SubHeaderContent: () => JSX.Element +} +const EmptyState = ({ HeaderContent, SubHeaderContent }: EmptyStateProps) => ( + + + + + + + + + + +) + +export default function ProposalEmptyState() { + const { chainId } = useActiveWeb3React() + if (chainId && L2_CHAIN_IDS.includes(chainId)) { + return ( + Please connect to Layer 1 Ethereum} + SubHeaderContent={() => ( + + Uniswap governance is only available on Layer 1. Switch your network to Ethereum Mainnet to view Proposals + and Vote. + + )} + /> + ) + } + return ( + No proposals found.} + SubHeaderContent={() => Proposals submitted by community members will appear here.} + /> + ) +} diff --git a/src/components/vote/VoteModal.tsx b/src/components/vote/VoteModal.tsx index 2c01f68ce..668d85241 100644 --- a/src/components/vote/VoteModal.tsx +++ b/src/components/vote/VoteModal.tsx @@ -1,19 +1,18 @@ -import React, { useState, useContext } from 'react' +import { useState, useContext } from 'react' import { useActiveWeb3React } from '../../hooks/web3' import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink' import Modal from '../Modal' import { AutoColumn, ColumnCenter } from '../Column' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { RowBetween } from '../Row' import { TYPE, CustomLightSpinner } from 'theme' import { X, ArrowUpCircle } from 'react-feather' import { ButtonPrimary } from '../Button' import Circle from '../../assets/images/blue-loader.svg' -import { useVoteCallback, useUserVotes } from '../../state/governance/hooks' -import { ExternalLink } from 'theme/components' +import { useVoteCallback, useUserVotes, VoteOption } from '../../state/governance/hooks' +import { ExternalLink } from '../../theme/components' import { formatCurrencyAmount } from 'utils/formatCurrencyAmount' -import { CurrencyAmount, Token } from '@uniswap/sdk-core' import { Trans } from '@lingui/macro' const ContentWrapper = styled(AutoColumn)` @@ -39,18 +38,14 @@ const ConfirmedIcon = styled(ColumnCenter)` interface VoteModalProps { isOpen: boolean onDismiss: () => void - support: boolean // if user is for or against proposal + voteOption: VoteOption | undefined proposalId: string | undefined // id for the proposal to vote on } -export default function VoteModal({ isOpen, onDismiss, proposalId, support }: VoteModalProps) { +export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }: VoteModalProps) { const { chainId } = useActiveWeb3React() - const { - voteCallback, - }: { - voteCallback: (proposalId: string | undefined, support: boolean) => Promise | undefined - } = useVoteCallback() - const availableVotes: CurrencyAmount | undefined = useUserVotes() + const { voteCallback } = useVoteCallback() + const { votes: availableVotes } = useUserVotes() // monitor call to help UI loading state const [hash, setHash] = useState() @@ -70,10 +65,10 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, support }: Vo setAttempting(true) // if callback not returned properly ignore - if (!voteCallback) return + if (!voteCallback || voteOption === undefined) return // try delegation and store hash - const hash = await voteCallback(proposalId, support)?.catch((error) => { + const hash = await voteCallback(proposalId, voteOption)?.catch((error) => { setAttempting(false) console.log(error) }) @@ -90,10 +85,12 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, support }: Vo - {support ? ( + {voteOption === VoteOption.Against ? ( + Vote against proposal {proposalId} + ) : voteOption === VoteOption.For ? ( Vote for proposal {proposalId} ) : ( - Vote against proposal {proposalId} + Vote to abstain on proposal {proposalId} )} @@ -103,10 +100,12 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, support }: Vo - {support ? ( + {voteOption === VoteOption.Against ? ( + Vote against proposal {proposalId} + ) : voteOption === VoteOption.For ? ( Vote for proposal {proposalId} ) : ( - Vote against proposal {proposalId} + Vote to abstain on proposal {proposalId} )} diff --git a/src/connectors/index.ts b/src/connectors/index.ts index b7c2c5c24..2b70c8b94 100644 --- a/src/connectors/index.ts +++ b/src/connectors/index.ts @@ -1,46 +1,35 @@ import { Web3Provider } from '@ethersproject/providers' +import { SafeAppConnector } from '@gnosis.pm/safe-apps-web3-react' import { InjectedConnector } from '@web3-react/injected-connector' +import { PortisConnector } from '@web3-react/portis-connector' import { WalletConnectConnector } from '@web3-react/walletconnect-connector' import { WalletLinkConnector } from '@web3-react/walletlink-connector' -import { PortisConnector } from '@web3-react/portis-connector' -import { SupportedChainId } from '../constants/chains' +import UNISWAP_LOGO_URL from '../assets/svg/logo.svg' +import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from '../constants/chains' import getLibrary from '../utils/getLibrary' - import { FortmaticConnector } from './Fortmatic' import { NetworkConnector } from './NetworkConnector' -import UNISWAP_LOGO_URL from '../assets/svg/logo.svg' const INFURA_KEY = process.env.REACT_APP_INFURA_KEY const FORMATIC_KEY = process.env.REACT_APP_FORTMATIC_KEY const PORTIS_ID = process.env.REACT_APP_PORTIS_ID -const WALLETCONNECT_BRIDGE_URL = process.env.REACT_APP_WALLETCONNECT_BRIDGE_URL if (typeof INFURA_KEY === 'undefined') { throw new Error(`REACT_APP_INFURA_KEY must be a defined environment variable`) } -const NETWORK_URLS: { - [chainId in SupportedChainId]: string -} = { +const NETWORK_URLS: { [key in SupportedChainId]: string } = { [SupportedChainId.MAINNET]: `https://mainnet.infura.io/v3/${INFURA_KEY}`, [SupportedChainId.RINKEBY]: `https://rinkeby.infura.io/v3/${INFURA_KEY}`, [SupportedChainId.ROPSTEN]: `https://ropsten.infura.io/v3/${INFURA_KEY}`, [SupportedChainId.GOERLI]: `https://goerli.infura.io/v3/${INFURA_KEY}`, [SupportedChainId.KOVAN]: `https://kovan.infura.io/v3/${INFURA_KEY}`, - [SupportedChainId.ARBITRUM_KOVAN]: `https://kovan5.arbitrum.io/rpc`, - [SupportedChainId.ARBITRUM_ONE]: `https://arb1.arbitrum.io/rpc`, + [SupportedChainId.OPTIMISM]: `https://optimism-mainnet.infura.io/v3/${INFURA_KEY}`, + [SupportedChainId.OPTIMISTIC_KOVAN]: `https://optimism-kovan.infura.io/v3/${INFURA_KEY}`, + [SupportedChainId.ARBITRUM_ONE]: `https://arbitrum-mainnet.infura.io/v3/${INFURA_KEY}`, + [SupportedChainId.ARBITRUM_RINKEBY]: `https://arbitrum-rinkeby.infura.io/v3/${INFURA_KEY}`, } -const SUPPORTED_CHAIN_IDS: SupportedChainId[] = [ - SupportedChainId.MAINNET, - SupportedChainId.KOVAN, - SupportedChainId.GOERLI, - SupportedChainId.RINKEBY, - SupportedChainId.ROPSTEN, - SupportedChainId.ARBITRUM_KOVAN, - SupportedChainId.ARBITRUM_ONE, -] - export const network = new NetworkConnector({ urls: NETWORK_URLS, defaultChainId: 1, @@ -52,15 +41,15 @@ export function getNetworkLibrary(): Web3Provider { } export const injected = new InjectedConnector({ - supportedChainIds: SUPPORTED_CHAIN_IDS, + supportedChainIds: ALL_SUPPORTED_CHAIN_IDS, }) +export const gnosisSafe = new SafeAppConnector() + export const walletconnect = new WalletConnectConnector({ - supportedChainIds: SUPPORTED_CHAIN_IDS, + supportedChainIds: ALL_SUPPORTED_CHAIN_IDS, rpc: NETWORK_URLS, - bridge: WALLETCONNECT_BRIDGE_URL, qrcode: true, - pollingInterval: 15000, }) // mainnet only @@ -77,8 +66,7 @@ export const portis = new PortisConnector({ // mainnet only export const walletlink = new WalletLinkConnector({ - url: NETWORK_URLS[1], + url: NETWORK_URLS[SupportedChainId.MAINNET], appName: 'Uniswap', - // TODO: review UNISWAP_LOGO_URL appLogoUrl: UNISWAP_LOGO_URL, }) diff --git a/src/constants/addresses.ts b/src/constants/addresses.ts index 547b99723..6bad1241f 100644 --- a/src/constants/addresses.ts +++ b/src/constants/addresses.ts @@ -5,50 +5,64 @@ import { SupportedChainId } from './chains' export type AddressMap = { [chainId: number]: string } -export const UNI_ADDRESS: AddressMap = constructSameAddressMap('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984', false) -export const MULTICALL2_ADDRESSES: AddressMap = { - ...constructSameAddressMap('0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696', false), - [SupportedChainId.ARBITRUM_KOVAN]: '0xc80e33a6f02cf08557a0ca3d94d1474d73f64bc1', - [SupportedChainId.ARBITRUM_ONE]: '0x021CeAC7e681dBCE9b5039d2535ED97590eB395c', +export const UNI_ADDRESS: AddressMap = constructSameAddressMap('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984') +export const MULTICALL_ADDRESS: AddressMap = { + ...constructSameAddressMap('0x1F98415757620B543A52E61c46B32eB19261F984', [SupportedChainId.OPTIMISTIC_KOVAN]), + [SupportedChainId.OPTIMISM]: '0x90f872b3d8f33f305e0250db6A2761B354f7710A', + [SupportedChainId.ARBITRUM_ONE]: '0xadF885960B47eA2CD9B55E6DAc6B42b7Cb2806dB', + [SupportedChainId.ARBITRUM_RINKEBY]: '0xa501c031958F579dB7676fF1CE78AD305794d579', } -export const V2_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V2_FACTORY_ADDRESS, false) -export const V2_ROUTER_ADDRESS: AddressMap = constructSameAddressMap( - '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', - false -) +export const V2_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V2_FACTORY_ADDRESS) +export const V2_ROUTER_ADDRESS: AddressMap = constructSameAddressMap('0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D') -// most current governance contract address should always be the 0 index -// only support governance on mainnet -export const GOVERNANCE_ADDRESSES: AddressMap[] = [ - { - [SupportedChainId.MAINNET]: '0xC4e172459f1E7939D522503B81AFAaC1014CE6F6', - }, - { - [SupportedChainId.MAINNET]: '0x5e4be8Bc9637f0EAA1A755019e06A68ce081D58F', - }, -] -export const TIMELOCK_ADDRESS: AddressMap = { - [SupportedChainId.MAINNET]: '0x1a9C8182C09F50C8318d769245beA52c32BE35BC', +/** + * The oldest V0 governance address + */ +export const GOVERNANCE_ALPHA_V0_ADDRESSES: AddressMap = constructSameAddressMap( + '0x5e4be8Bc9637f0EAA1A755019e06A68ce081D58F' +) +/** + * The older V1 governance address + */ +export const GOVERNANCE_ALPHA_V1_ADDRESSES: AddressMap = { + [SupportedChainId.MAINNET]: '0xC4e172459f1E7939D522503B81AFAaC1014CE6F6', +} +/** + * The latest governor bravo that is currently admin of timelock + */ +export const GOVERNANCE_BRAVO_ADDRESSES: AddressMap = { + [SupportedChainId.MAINNET]: '0x408ED6354d4973f66138C91495F2f2FCbd8724C3', } +export const TIMELOCK_ADDRESS: AddressMap = constructSameAddressMap('0x1a9C8182C09F50C8318d769245beA52c32BE35BC') + export const MERKLE_DISTRIBUTOR_ADDRESS: AddressMap = { [SupportedChainId.MAINNET]: '0x090D4613473dEE047c3f2706764f49E0821D256e', } export const ARGENT_WALLET_DETECTOR_ADDRESS: AddressMap = { [SupportedChainId.MAINNET]: '0xeca4B0bDBf7c55E9b7925919d03CbF8Dc82537E8', } -export const V3_CORE_FACTORY_ADDRESSES: AddressMap = { - ...constructSameAddressMap(V3_FACTORY_ADDRESS, true), - [SupportedChainId.ARBITRUM_KOVAN]: '0xf594DEF7751440197879149f46E98b334E6DF1fa', -} -export const QUOTER_ADDRESSES: AddressMap = { - ...constructSameAddressMap('0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6', true), - [SupportedChainId.ARBITRUM_KOVAN]: '0xAC06b88FA9adB7584A659b190F37F085352cB783', -} -export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: AddressMap = { - ...constructSameAddressMap('0xC36442b4a4522E871399CD717aBDD847Ab11FE88', true), - [SupportedChainId.ARBITRUM_KOVAN]: '0x9E1498aE1F508E86462e8A0F213CF385A6622464', -} +export const V3_CORE_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V3_FACTORY_ADDRESS, [ + SupportedChainId.OPTIMISM, + SupportedChainId.OPTIMISTIC_KOVAN, + SupportedChainId.ARBITRUM_ONE, + SupportedChainId.ARBITRUM_RINKEBY, +]) +export const QUOTER_ADDRESSES: AddressMap = constructSameAddressMap('0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6', [ + SupportedChainId.OPTIMISM, + SupportedChainId.OPTIMISTIC_KOVAN, + SupportedChainId.ARBITRUM_ONE, + SupportedChainId.ARBITRUM_RINKEBY, +]) +export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: AddressMap = constructSameAddressMap( + '0xC36442b4a4522E871399CD717aBDD847Ab11FE88', + [ + SupportedChainId.OPTIMISM, + SupportedChainId.OPTIMISTIC_KOVAN, + SupportedChainId.ARBITRUM_ONE, + SupportedChainId.ARBITRUM_RINKEBY, + ] +) export const ENS_REGISTRAR_ADDRESSES: AddressMap = { [SupportedChainId.MAINNET]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', [SupportedChainId.ROPSTEN]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', @@ -58,11 +72,13 @@ export const ENS_REGISTRAR_ADDRESSES: AddressMap = { export const SOCKS_CONTROLLER_ADDRESSES: AddressMap = { [SupportedChainId.MAINNET]: '0x65770b5283117639760beA3F867b69b3697a91dd', } -export const SWAP_ROUTER_ADDRESSES: AddressMap = { - ...constructSameAddressMap('0xE592427A0AEce92De3Edee1F18E0157C05861564', true), - [SupportedChainId.ARBITRUM_KOVAN]: '0x6ae2DE23F2BE35B3921ba15DA52e4b173667dCb9', -} -export const V3_MIGRATOR_ADDRESSES: AddressMap = constructSameAddressMap( - '0xA5644E29708357803b5A882D272c41cC0dF92B34', - true -) +export const SWAP_ROUTER_ADDRESSES: AddressMap = constructSameAddressMap('0xE592427A0AEce92De3Edee1F18E0157C05861564', [ + SupportedChainId.OPTIMISM, + SupportedChainId.OPTIMISTIC_KOVAN, + SupportedChainId.ARBITRUM_ONE, + SupportedChainId.ARBITRUM_RINKEBY, +]) +export const V3_MIGRATOR_ADDRESSES: AddressMap = constructSameAddressMap('0xA5644E29708357803b5A882D272c41cC0dF92B34', [ + SupportedChainId.ARBITRUM_ONE, + SupportedChainId.ARBITRUM_RINKEBY, +]) diff --git a/src/constants/chains.test.ts b/src/constants/chains.test.ts new file mode 100644 index 000000000..6b6f32d5f --- /dev/null +++ b/src/constants/chains.test.ts @@ -0,0 +1,30 @@ +import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from './chains' + +describe('chains', () => { + describe('ALL_SUPPORTED_CHAIN_IDS', () => { + it('contains all the values in the SupportedChainId enum', () => { + Object.values(SupportedChainId).forEach((chainId) => { + if (typeof chainId === 'number') expect(ALL_SUPPORTED_CHAIN_IDS.includes(chainId as number)).toBeTruthy() + }) + }) + + it('contains no duplicates', () => { + const set = new Set() + ALL_SUPPORTED_CHAIN_IDS.forEach((chainId) => { + expect(set.has(chainId)).toEqual(false) + set.add(chainId) + }) + }) + + it('all values are in the SupportedChainId mapping', () => { + ALL_SUPPORTED_CHAIN_IDS.forEach((chainId) => { + // takes advantage of the reverse mapping + expect(SupportedChainId[chainId]).toBeTruthy() + }) + }) + + it('all values are numeric', () => { + expect(ALL_SUPPORTED_CHAIN_IDS.every((chainId) => typeof chainId === 'number')).toBeTruthy() + }) + }) +}) diff --git a/src/constants/chains.ts b/src/constants/chains.ts index 4095456a2..3583d3351 100644 --- a/src/constants/chains.ts +++ b/src/constants/chains.ts @@ -1,19 +1,127 @@ +import arbitrumLogoUrl from 'assets/svg/arbitrum_logo.svg' +import optimismLogoUrl from 'assets/svg/optimism_logo.svg' + export enum SupportedChainId { MAINNET = 1, ROPSTEN = 3, RINKEBY = 4, GOERLI = 5, KOVAN = 42, - ARBITRUM_KOVAN = 144545313136048, + ARBITRUM_ONE = 42161, + ARBITRUM_RINKEBY = 421611, + OPTIMISM = 10, + OPTIMISTIC_KOVAN = 69, +} + +export const ALL_SUPPORTED_CHAIN_IDS: SupportedChainId[] = [ + SupportedChainId.MAINNET, + SupportedChainId.ROPSTEN, + SupportedChainId.RINKEBY, + SupportedChainId.GOERLI, + SupportedChainId.KOVAN, + + SupportedChainId.ARBITRUM_ONE, + SupportedChainId.ARBITRUM_RINKEBY, + SupportedChainId.OPTIMISM, + SupportedChainId.OPTIMISTIC_KOVAN, +] + +export const L1_CHAIN_IDS = [ + SupportedChainId.MAINNET, + SupportedChainId.ROPSTEN, + SupportedChainId.RINKEBY, + SupportedChainId.GOERLI, + SupportedChainId.KOVAN, +] as const + +export type SupportedL1ChainId = typeof L1_CHAIN_IDS[number] + +export const L2_CHAIN_IDS = [ + SupportedChainId.ARBITRUM_ONE, + SupportedChainId.ARBITRUM_RINKEBY, + SupportedChainId.OPTIMISM, + SupportedChainId.OPTIMISTIC_KOVAN, +] as const + +export type SupportedL2ChainId = typeof L2_CHAIN_IDS[number] + +export interface L1ChainInfo { + readonly docs: string + readonly explorer: string + readonly infoLink: string + readonly label: string +} +export interface L2ChainInfo extends L1ChainInfo { + readonly bridge: string + readonly logoUrl: string } -export const NETWORK_LABELS: { [chainId in SupportedChainId | number]: string } = { - [SupportedChainId.MAINNET]: 'Mainnet', - [SupportedChainId.RINKEBY]: 'Rinkeby', - [SupportedChainId.ROPSTEN]: 'Ropsten', - [SupportedChainId.GOERLI]: 'Görli', - [SupportedChainId.KOVAN]: 'Kovan', - [SupportedChainId.ARBITRUM_KOVAN]: 'kArbitrum', - [SupportedChainId.ARBITRUM_ONE]: 'Arbitrum One', +export type ChainInfo = { readonly [chainId: number]: L1ChainInfo | L2ChainInfo } & { + readonly [chainId in SupportedL2ChainId]: L2ChainInfo +} & { readonly [chainId in SupportedL1ChainId]: L1ChainInfo } + +export const CHAIN_INFO: ChainInfo = { + [SupportedChainId.ARBITRUM_ONE]: { + bridge: 'https://bridge.arbitrum.io/', + docs: 'https://offchainlabs.com/', + explorer: 'https://arbiscan.io/', + infoLink: 'https://info.uniswap.org/#/arbitrum', + label: 'Arbitrum', + logoUrl: arbitrumLogoUrl, + }, + [SupportedChainId.ARBITRUM_RINKEBY]: { + bridge: 'https://bridge.arbitrum.io/', + docs: 'https://offchainlabs.com/', + explorer: 'https://rinkeby-explorer.arbitrum.io/', + infoLink: 'https://info.uniswap.org/#/arbitrum/', + label: 'Arbitrum Rinkeby', + logoUrl: arbitrumLogoUrl, + }, + [SupportedChainId.MAINNET]: { + docs: 'https://docs.uniswap.org/', + explorer: 'https://etherscan.io/', + infoLink: 'https://info.uniswap.org/#/', + label: 'Mainnet', + }, + [SupportedChainId.RINKEBY]: { + docs: 'https://docs.uniswap.org/', + explorer: 'https://rinkeby.etherscan.io/', + infoLink: 'https://info.uniswap.org/#/', + label: 'Rinkeby', + }, + [SupportedChainId.ROPSTEN]: { + docs: 'https://docs.uniswap.org/', + explorer: 'https://ropsten.etherscan.io/', + infoLink: 'https://info.uniswap.org/#/', + label: 'Ropsten', + }, + [SupportedChainId.KOVAN]: { + docs: 'https://docs.uniswap.org/', + explorer: 'https://kovan.etherscan.io/', + infoLink: 'https://info.uniswap.org/#/', + label: 'Kovan', + }, + [SupportedChainId.GOERLI]: { + docs: 'https://docs.uniswap.org/', + explorer: 'https://goerli.etherscan.io/', + infoLink: 'https://info.uniswap.org/#/', + label: 'Görli', + }, + [SupportedChainId.OPTIMISM]: { + bridge: 'https://gateway.optimism.io/', + docs: 'https://optimism.io/', + explorer: 'https://optimistic.etherscan.io/', + infoLink: 'https://info.uniswap.org/#/optimism/', + label: 'Optimism', + logoUrl: optimismLogoUrl, + }, + [SupportedChainId.OPTIMISTIC_KOVAN]: { + bridge: 'https://gateway.optimism.io/', + docs: 'https://optimism.io/', + explorer: 'https://optimistic.etherscan.io/', + infoLink: 'https://info.uniswap.org/#/optimism', + label: 'Optimistic Kovan', + logoUrl: optimismLogoUrl, + }, } diff --git a/src/constants/governance.ts b/src/constants/governance.ts index a00fd82b9..829577324 100644 --- a/src/constants/governance.ts +++ b/src/constants/governance.ts @@ -1,23 +1,17 @@ -import { GOVERNANCE_ADDRESSES, TIMELOCK_ADDRESS, UNI_ADDRESS } from 'constants/addresses' +import { + GOVERNANCE_ALPHA_V0_ADDRESSES, + GOVERNANCE_ALPHA_V1_ADDRESSES, + TIMELOCK_ADDRESS, + UNI_ADDRESS, +} from './addresses' import { SupportedChainId } from './chains' -// returns { [address]: `Governance (V${n})`} for each address in GOVERNANCE_ADDRESSES except the current, which gets no version indicator -const governanceContracts = (): Record => - GOVERNANCE_ADDRESSES.reduce( - (acc, addressMap, i) => ({ - ...acc, - [addressMap[SupportedChainId.MAINNET]]: `Governance${ - i === 0 ? '' : ` (V${GOVERNANCE_ADDRESSES.length - 1 - i})` - }`, - }), - {} - ) - export const COMMON_CONTRACT_NAMES: Record = { [SupportedChainId.MAINNET]: { [UNI_ADDRESS[SupportedChainId.MAINNET]]: 'UNI', [TIMELOCK_ADDRESS[SupportedChainId.MAINNET]]: 'Timelock', - ...governanceContracts(), + [GOVERNANCE_ALPHA_V0_ADDRESSES[SupportedChainId.MAINNET]]: 'Governance (V0)', + [GOVERNANCE_ALPHA_V1_ADDRESSES[SupportedChainId.MAINNET]]: 'Governance', }, } diff --git a/src/constants/lists.ts b/src/constants/lists.ts index 823620a0c..b715a0b20 100644 --- a/src/constants/lists.ts +++ b/src/constants/lists.ts @@ -1,38 +1,39 @@ -// used to mark unsupported tokens, these are hosted lists of unsupported tokens - -const COMPOUND_LIST = 'https://raw.githubusercontent.com/compound-finance/token-list/master/compound.tokenlist.json' -const UMA_LIST = 'https://umaproject.org/uma.tokenlist.json' const AAVE_LIST = 'tokenlist.aave.eth' -const SYNTHETIX_LIST = 'synths.snx.eth' -const WRAPPED_LIST = 'wrapped.tokensoft.eth' -const SET_LIST = 'https://raw.githubusercontent.com/SetProtocol/uniswap-tokenlist/main/set.tokenlist.json' -const OPYN_LIST = 'https://raw.githubusercontent.com/opynfinance/opyn-tokenlist/master/opyn-v1.tokenlist.json' -const ROLL_LIST = 'https://app.tryroll.com/tokens.json' -const COINGECKO_LIST = 'https://tokens.coingecko.com/uniswap/all.json' +const BA_LIST = 'https://raw.githubusercontent.com/The-Blockchain-Association/sec-notice-list/master/ba-sec-list.json' const CMC_ALL_LIST = 'defi.cmc.eth' const CMC_STABLECOIN = 'stablecoin.cmc.eth' -const KLEROS_LIST = 't2crtokens.eth' +const COINGECKO_LIST = 'https://tokens.coingecko.com/uniswap/all.json' +const COMPOUND_LIST = 'https://raw.githubusercontent.com/compound-finance/token-list/master/compound.tokenlist.json' const GEMINI_LIST = 'https://www.gemini.com/uniswap/manifest.json' -const BA_LIST = 'https://raw.githubusercontent.com/The-Blockchain-Association/sec-notice-list/master/ba-sec-list.json' +export const ARBITRUM_LIST = 'https://bridge.arbitrum.io/token-list-42161.json' +const KLEROS_LIST = 't2crtokens.eth' +export const OPTIMISM_LIST = 'https://static.optimism.io/optimism.tokenlist.json' +const ROLL_LIST = 'https://app.tryroll.com/tokens.json' +const SET_LIST = 'https://raw.githubusercontent.com/SetProtocol/uniswap-tokenlist/main/set.tokenlist.json' +const WRAPPED_LIST = 'wrapped.tokensoft.eth' export const UNSUPPORTED_LIST_URLS: string[] = [BA_LIST] +// this is the default list of lists that are exposed to users // lower index == higher priority for token import -export const DEFAULT_LIST_OF_LISTS: string[] = [ +const DEFAULT_LIST_OF_LISTS_TO_DISPLAY: string[] = [ COMPOUND_LIST, AAVE_LIST, - SYNTHETIX_LIST, - UMA_LIST, + CMC_ALL_LIST, + CMC_STABLECOIN, WRAPPED_LIST, SET_LIST, - OPYN_LIST, ROLL_LIST, COINGECKO_LIST, - CMC_ALL_LIST, - CMC_STABLECOIN, KLEROS_LIST, + ARBITRUM_LIST, + OPTIMISM_LIST, GEMINI_LIST, - ...UNSUPPORTED_LIST_URLS, // need to load unsupported tokens as well +] + +export const DEFAULT_LIST_OF_LISTS: string[] = [ + ...DEFAULT_LIST_OF_LISTS_TO_DISPLAY, + ...UNSUPPORTED_LIST_URLS, // need to load dynamic unsupported tokens as well ] // default lists to be 'active' aka searched across diff --git a/src/constants/locales.ts b/src/constants/locales.ts index 359a5f9bc..cad84e427 100644 --- a/src/constants/locales.ts +++ b/src/constants/locales.ts @@ -1,4 +1,6 @@ export const SUPPORTED_LOCALES = [ + // order as they appear in the language dropdown + 'en-US', 'af-ZA', 'ar-SA', 'ca-ES', @@ -6,7 +8,6 @@ export const SUPPORTED_LOCALES = [ 'da-DK', 'de-DE', 'el-GR', - 'en-US', 'es-ES', 'fi-FI', 'fr-FR', @@ -65,6 +66,6 @@ export const LOCALE_LABEL: { [locale in SupportedLocale]: string } = { 'tr-TR': 'Türkçe', 'uk-UA': 'Український', 'vi-VN': 'Tiếng Việt', - 'zh-CN': '中文 ( 中国 )', - 'zh-TW': '中文 ( 台灣 )', + 'zh-CN': '简体中文', + 'zh-TW': '繁体中文', } diff --git a/src/constants/misc.ts b/src/constants/misc.ts index 28b45f5d9..445a03e4b 100644 --- a/src/constants/misc.ts +++ b/src/constants/misc.ts @@ -7,6 +7,7 @@ export const NetworkContextName = 'NETWORK' // 30 minutes, denominated in seconds export const DEFAULT_DEADLINE_FROM_NOW = 60 * 30 +export const L2_DEADLINE_FROM_NOW = 60 * 5 // used for rewards deadlines export const BIG_INT_SECONDS_IN_WEEK = JSBI.BigInt(60 * 60 * 24 * 7) @@ -14,8 +15,9 @@ export const BIG_INT_SECONDS_IN_WEEK = JSBI.BigInt(60 * 60 * 24 * 7) export const BIG_INT_ZERO = JSBI.BigInt(0) // one basis JSBI.BigInt -export const ONE_BIPS = new Percent(JSBI.BigInt(1), JSBI.BigInt(10000)) -export const BIPS_BASE = JSBI.BigInt(10000) +const BIPS_BASE = JSBI.BigInt(10000) +export const ONE_BIPS = new Percent(JSBI.BigInt(1), BIPS_BASE) + // used for warning states export const ALLOWED_PRICE_IMPACT_LOW: Percent = new Percent(JSBI.BigInt(100), BIPS_BASE) // 1% export const ALLOWED_PRICE_IMPACT_MEDIUM: Percent = new Percent(JSBI.BigInt(300), BIPS_BASE) // 3% @@ -25,8 +27,7 @@ export const PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN: Percent = new Percent(JSBI.Bi // for non expert mode disable swaps above this export const BLOCKED_PRICE_IMPACT_NON_EXPERT: Percent = new Percent(JSBI.BigInt(1500), BIPS_BASE) // 15% -// used to ensure the user doesn't send so much ETH so they end up with <.01 -export const BETTER_TRADE_LESS_HOPS_THRESHOLD = new Percent(JSBI.BigInt(50), JSBI.BigInt(10000)) +export const BETTER_TRADE_LESS_HOPS_THRESHOLD = new Percent(JSBI.BigInt(50), BIPS_BASE) export const ZERO_PERCENT = new Percent('0') export const ONE_HUNDRED_PERCENT = new Percent('1') diff --git a/src/constants/proposals/index.ts b/src/constants/proposals/index.ts index 3a1ad3caf..46c108b52 100644 --- a/src/constants/proposals/index.ts +++ b/src/constants/proposals/index.ts @@ -1 +1,2 @@ export const UNISWAP_GRANTS_START_BLOCK = 11473815 +export const BRAVO_START_BLOCK = 13059344 diff --git a/src/constants/routing.ts b/src/constants/routing.ts index 0d4f0a5f6..6c11f73c9 100644 --- a/src/constants/routing.ts +++ b/src/constants/routing.ts @@ -8,17 +8,16 @@ import { FEI, FRAX, FXS, - MIR, renBTC, TRIBE, - UMA, - UNI, USDC, USDT, - UST, WBTC, ETH2X_FLI, WETH9_EXTENDED, + DAI_OPTIMISM, + USDT_OPTIMISM, + WBTC_OPTIMISM, } from 'constants/tokens' type ChainTokenList = { @@ -29,49 +28,19 @@ type ChainCurrencyList = { readonly [chainId: number]: Currency[] } -// List of all mirror's assets addresses. -// Last pulled from : https://whitelist.mirror.finance/eth/tokenlists.json -// TODO: Generate this programmatically ? -const mAssetsAdditionalBases: { [tokenAddress: string]: Token[] } = { - [UST.address]: [MIR], - [MIR.address]: [UST], - '0xd36932143F6eBDEDD872D5Fb0651f4B72Fd15a84': [MIR, UST], // mAAPL - '0x59A921Db27Dd6d4d974745B7FfC5c33932653442': [MIR, UST], // mGOOGL - '0x21cA39943E91d704678F5D00b6616650F066fD63': [MIR, UST], // mTSLA - '0xC8d674114bac90148d11D3C1d33C61835a0F9DCD': [MIR, UST], // mNFLX - '0x13B02c8dE71680e71F0820c996E4bE43c2F57d15': [MIR, UST], // mQQQ - '0xEdb0414627E6f1e3F082DE65cD4F9C693D78CCA9': [MIR, UST], // mTWTR - '0x41BbEDd7286dAab5910a1f15d12CBda839852BD7': [MIR, UST], // mMSFT - '0x0cae9e4d663793c2a2A0b211c1Cf4bBca2B9cAa7': [MIR, UST], // mAMZN - '0x56aA298a19C93c6801FDde870fA63EF75Cc0aF72': [MIR, UST], // mBABA - '0x1d350417d9787E000cc1b95d70E9536DcD91F373': [MIR, UST], // mIAU - '0x9d1555d8cB3C846Bb4f7D5B1B1080872c3166676': [MIR, UST], // mSLV - '0x31c63146a635EB7465e5853020b39713AC356991': [MIR, UST], // mUSO - '0xf72FCd9DCF0190923Fadd44811E240Ef4533fc86': [MIR, UST], // mVIXY -} -const WETH_ONLY: ChainTokenList = { - [SupportedChainId.MAINNET]: [WETH9_EXTENDED[SupportedChainId.MAINNET]], - [SupportedChainId.ROPSTEN]: [WETH9_EXTENDED[SupportedChainId.ROPSTEN]], - [SupportedChainId.RINKEBY]: [WETH9_EXTENDED[SupportedChainId.RINKEBY]], - [SupportedChainId.GOERLI]: [WETH9_EXTENDED[SupportedChainId.GOERLI]], - [SupportedChainId.KOVAN]: [WETH9_EXTENDED[SupportedChainId.KOVAN]], - [SupportedChainId.ARBITRUM_KOVAN]: [WETH9_EXTENDED[SupportedChainId.ARBITRUM_KOVAN]], - [SupportedChainId.ARBITRUM_ONE]: [WETH9_EXTENDED[SupportedChainId.ARBITRUM_ONE]], -} +const WETH_ONLY: ChainTokenList = Object.fromEntries( + Object.entries(WETH9_EXTENDED).map(([key, value]) => [key, [value]]) +) + // used to construct intermediary pairs for trading export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = { ...WETH_ONLY, - [1]: [...WETH_ONLY[1], DAI, USDC, USDT, WBTC], + [SupportedChainId.MAINNET]: [...WETH_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC], + [SupportedChainId.OPTIMISM]: [...WETH_ONLY[SupportedChainId.OPTIMISM], DAI_OPTIMISM, USDT_OPTIMISM, WBTC_OPTIMISM], } export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = { - [1]: { - ...mAssetsAdditionalBases, + [SupportedChainId.MAINNET]: { '0xF16E4d813f4DcfDe4c5b44f305c908742De84eF0': [ETH2X_FLI], - '0xA948E86885e12Fb09AfEF8C52142EBDbDf73cD18': [UNI[1]], - '0x561a4717537ff4AF5c687328c0f7E90a319705C0': [UNI[1]], - '0xE0360A9e2cdd7d03B9408c7D3001E017BAc2EcD5': [UNI[1]], - '0xa6e3454fec677772dd771788a079355e43910638': [UMA], - '0xB46F57e7Ce3a284d74b70447Ef9352B5E5Df8963': [UMA], [FEI.address]: [TRIBE], [TRIBE.address]: [FEI], [FRAX.address]: [FXS], @@ -85,8 +54,8 @@ export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: To * tokens. */ export const CUSTOM_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = { - [1]: { - [AMPL.address]: [DAI, WETH9_EXTENDED[1]], + [SupportedChainId.MAINNET]: { + [AMPL.address]: [DAI, WETH9_EXTENDED[SupportedChainId.MAINNET]], }, } @@ -94,31 +63,52 @@ export const CUSTOM_BASES: { [chainId: number]: { [tokenAddress: string]: Token[ * Shows up in the currency select for swap and add liquidity */ export const COMMON_BASES: ChainCurrencyList = { - [1]: [ExtendedEther.onChain(1), DAI, USDC, USDT, WBTC, WETH9_EXTENDED[1]], - [3]: [ExtendedEther.onChain(3), WETH9_EXTENDED[3]], - [4]: [ExtendedEther.onChain(4), WETH9_EXTENDED[4]], - [5]: [ExtendedEther.onChain(5), WETH9_EXTENDED[5]], - [42]: [ExtendedEther.onChain(42), WETH9_EXTENDED[42]], - [SupportedChainId.ARBITRUM_KOVAN]: [ - ExtendedEther.onChain(SupportedChainId.ARBITRUM_KOVAN), - WETH9_EXTENDED[SupportedChainId.ARBITRUM_KOVAN], + [SupportedChainId.MAINNET]: [ + ExtendedEther.onChain(SupportedChainId.MAINNET), + DAI, + USDC, + USDT, + WBTC, + WETH9_EXTENDED[SupportedChainId.MAINNET], ], + [SupportedChainId.ROPSTEN]: [ + ExtendedEther.onChain(SupportedChainId.ROPSTEN), + WETH9_EXTENDED[SupportedChainId.ROPSTEN], + ], + [SupportedChainId.RINKEBY]: [ + ExtendedEther.onChain(SupportedChainId.RINKEBY), + WETH9_EXTENDED[SupportedChainId.RINKEBY], + ], + [SupportedChainId.GOERLI]: [ExtendedEther.onChain(SupportedChainId.GOERLI), WETH9_EXTENDED[SupportedChainId.GOERLI]], + [SupportedChainId.KOVAN]: [ExtendedEther.onChain(SupportedChainId.KOVAN), WETH9_EXTENDED[SupportedChainId.KOVAN]], [SupportedChainId.ARBITRUM_ONE]: [ ExtendedEther.onChain(SupportedChainId.ARBITRUM_ONE), WETH9_EXTENDED[SupportedChainId.ARBITRUM_ONE], ], + [SupportedChainId.ARBITRUM_RINKEBY]: [ + ExtendedEther.onChain(SupportedChainId.ARBITRUM_RINKEBY), + WETH9_EXTENDED[SupportedChainId.ARBITRUM_RINKEBY], + ], + [SupportedChainId.OPTIMISM]: [ExtendedEther.onChain(SupportedChainId.OPTIMISM)], + [SupportedChainId.OPTIMISTIC_KOVAN]: [ExtendedEther.onChain(SupportedChainId.OPTIMISTIC_KOVAN)], } // used to construct the list of all pairs we consider by default in the frontend export const BASES_TO_TRACK_LIQUIDITY_FOR: ChainTokenList = { ...WETH_ONLY, - [1]: [...WETH_ONLY[1], DAI, USDC, USDT, WBTC], + [SupportedChainId.MAINNET]: [...WETH_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC], } export const PINNED_PAIRS: { readonly [chainId: number]: [Token, Token][] } = { - [1]: [ + [SupportedChainId.MAINNET]: [ [ - new Token(1, '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643', 8, 'cDAI', 'Compound Dai'), - new Token(1, '0x39AA39c021dfbaE8faC545936693aC917d5E7563', 8, 'cUSDC', 'Compound USD Coin'), + new Token(SupportedChainId.MAINNET, '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643', 8, 'cDAI', 'Compound Dai'), + new Token( + SupportedChainId.MAINNET, + '0x39AA39c021dfbaE8faC545936693aC917d5E7563', + 8, + 'cUSDC', + 'Compound USD Coin' + ), ], [USDC, USDT], [DAI, USDT], diff --git a/src/constants/tokenLists/broken.tokenlist.json b/src/constants/tokenLists/broken.tokenlist.json new file mode 100644 index 000000000..7c23a05ec --- /dev/null +++ b/src/constants/tokenLists/broken.tokenlist.json @@ -0,0 +1,22 @@ +{ + "name": "Broken Token List", + "timestamp": "2021-01-05T20:47:02.923Z", + "version": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "tags": {}, + "logoURI": "ipfs://QmNa8mQkrNKp1WEEeGjFezDmDeodkWRevGFN8JCV7b4Xir", + "keywords": ["uniswap", "broken"], + "tokens": [ + { + "name": "UNI HODL", + "address": "0x4bf5dc91E2555449293D7824028Eb8Fe5879B689", + "symbol": "UniH", + "decimals": 18, + "chainId": 1, + "logoURI": "" + } + ] +} diff --git a/src/constants/tokenLists/uniswap-v2-unsupported.tokenlist.json b/src/constants/tokenLists/uniswap-v2-unsupported.tokenlist.json deleted file mode 100644 index b773893d1..000000000 --- a/src/constants/tokenLists/uniswap-v2-unsupported.tokenlist.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "Unsupported Tokens", - "timestamp": "2021-01-05T20:47:02.923Z", - "version": { - "major": 1, - "minor": 0, - "patch": 0 - }, - "tags": {}, - "logoURI": "ipfs://QmNa8mQkrNKp1WEEeGjFezDmDeodkWRevGFN8JCV7b4Xir", - "keywords": ["uniswap", "unsupported"], - "tokens": [ - { - "name": "Gold Tether", - "address": "0x4922a015c4407F87432B179bb209e125432E4a2A", - "symbol": "XAUt", - "decimals": 6, - "chainId": 1, - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4922a015c4407F87432B179bb209e125432E4a2A/logo.png" - }, - { - "name": "Grump Cat", - "address": "0x93B2FfF814FCaEFFB01406e80B4Ecd89Ca6A021b", - "symbol": "GRUMPY", - "decimals": 9, - "chainId": 1, - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4922a015c4407F87432B179bb209e125432E4a2A/logo.png" - } - ] -} diff --git a/src/constants/tokenLists/unsupported.tokenlist.json b/src/constants/tokenLists/unsupported.tokenlist.json new file mode 100644 index 000000000..5fd78ad63 --- /dev/null +++ b/src/constants/tokenLists/unsupported.tokenlist.json @@ -0,0 +1,1078 @@ +{ + "name": "Unsupported Tokens", + "timestamp": "2021-01-05T20:47:02.923Z", + "version": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "tags": {}, + "logoURI": "ipfs://QmNa8mQkrNKp1WEEeGjFezDmDeodkWRevGFN8JCV7b4Xir", + "keywords": ["uniswap", "unsupported"], + "tokens": [ + { + "name": "Gold Tether", + "address": "0x4922a015c4407F87432B179bb209e125432E4a2A", + "symbol": "XAUt", + "decimals": 6, + "chainId": 1, + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4922a015c4407F87432B179bb209e125432E4a2A/logo.png" + }, + { + "name": "Grump Cat", + "address": "0x93B2FfF814FCaEFFB01406e80B4Ecd89Ca6A021b", + "symbol": "GRUMPY", + "decimals": 9, + "chainId": 1, + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4922a015c4407F87432B179bb209e125432E4a2A/logo.png" + }, + { + "name": "apeUSD-UMA Synthetic USD (Dec 2021)", + "address": "0xfA5e27893aee4805283D86e4283Da64F8c72dd56", + "symbol": "apeUSD-UMA-DEC21", + "decimals": 18, + "chainId": 1, + "logoURI": "" + }, + { + "chainId": 1, + "address": "0xc6b11850241c5127eab73af4b6c68bc267cbbff4", + "name": "oWETHp Put 360 DEC2520", + "symbol": "oWETH-360P-12/25/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "oYFIp Put 25000 OCT0220", + "address": "0x452b421be5b30f0c6ad8c3f03c06bdaab4f5c56c", + "symbol": "oYFI-25000P-10/02/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "oWETHp Put 360 OCT3020", + "address": "0x0578779e746d7186253a36cf651ea786acfcf087", + "symbol": "oWETH-360P-10/30/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "ETHc Call 500 OCT3020", + "address": "0xf9aba2e43fb19184408ea3b572a0fd672946f87b", + "symbol": "oETH-500C-10/30/20", + "decimals": 6 + }, + { + "chainId": 1, + "name": "oBALp Put 22 OCT0220", + "address": "0xdb0991dfc7e828b5a2837dc82d68e16490562c8d", + "symbol": "oBAL-22P-10/02/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "oCOMPp Put 150 SEP2520", + "address": "0xe951ebe6b4420ab3f4844cf36dedd263d095b416", + "symbol": "oCOMP-150P-09/25/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "oCRVp Put 3 SEP252020", + "address": "0x9215bd49b59748419eac6bad9dbe247df06ebdb9", + "symbol": "oCRV-3P-09/25/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "oETHp Put 180 SEP2520", + "address": "0xE3A2c34Fa2F59ffa95C4ACd1E5663633d45Bc3AD", + "symbol": "oETH-180P-09/25/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "oETHc Call 400 SEP2520", + "address": "0x05977EBC26825C0CD6097E0Ad7204721516711Eb", + "symbol": "oETH-400C-09/25/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "oWETHp Put 380 SEP1820", + "address": "0x31f88266301b08631f9f0e33fd5c43c2a5d1e5b2", + "symbol": "oWETH-380P-09/18/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "oYFIp Put 8500 SEP1820", + "address": "0xd1cec2f67fdc4c60e0963515dfc3343f31e32e47", + "symbol": "oYFI-8500P-09/18/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "oWETHp Put 370 SEP1120", + "address": "0x15844029b2c2bf24506e9937739a9a912f1e4354", + "symbol": "oWETH-370P-09/11/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "oWETHp Put 400 SEP0420", + "address": "0x5562c33c383f6386be4f6dcdbd35a3a99bbcfde6", + "symbol": "oWETH-400P-09/04/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "oETHp Put 200 AUG2820", + "address": "0x3CBFC1397deF0602c2d211c70A1c0c38CEDB5448", + "symbol": "oWETH-400P-09/04/20", + "decimals": 7 + }, + { + "chainId": 1, + "name": "Opyn cDai Insurance", + "symbol": "ocDai", + "address": "0x98cc3bd6af1880fcfda17ac477b2f612980e5e33", + "decimals": 8 + }, + { + "chainId": 1, + "name": "Opyn cUSDC Insurance", + "symbol": "ocUSDC", + "address": "0x8ED9f862363fFdFD3a07546e618214b6D59F03d4", + "decimals": 8 + }, + { + "chainId": 1, + "address": "0x176C674Ee533C6139B0dc8b458D72A93dCB3e705", + "symbol": "iAAVE", + "name": "Synth Inverse Aave", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iAAVE.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x8A8079c7149B8A1611e5C5d978DCA3bE16545F83", + "symbol": "iADA", + "name": "Synth Inverse Cardano", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iADA.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0xAFD870F32CE54EfdBF677466B612bf8ad164454B", + "symbol": "iBNB", + "name": "Synth Inverse Binance Coin", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iBNB.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0xD6014EA05BDe904448B743833dDF07c3C7837481", + "symbol": "iBTC", + "name": "Synth Inverse Bitcoin", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iBTC.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x336213e1DDFC69f4701Fc3F86F4ef4A160c1159d", + "symbol": "iCEX", + "name": "Synth Inverse Centralised Exchange Index", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iCEX.svg", + "tags": ["index", "inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x6345728B1ccE16E6f8C509950b5c84FFF88530d9", + "symbol": "iCOMP", + "name": "Synth Inverse Compound", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iCOMP.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0xCB98f42221b2C251A4E74A1609722eE09f0cc08E", + "symbol": "iDASH", + "name": "Synth Inverse Dash", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iDASH.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x14d10003807AC60d07BB0ba82cAeaC8d2087c157", + "symbol": "iDEFI", + "name": "Synth Inverse DeFi Index", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iDEFI.svg", + "tags": ["index", "inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x46a97629C9C1F58De6EC18C7F536e7E6d6A6ecDe", + "symbol": "iDOT", + "name": "Synth Inverse Polkadot", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iDOT.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0xF4EebDD0704021eF2a6Bbe993fdf93030Cd784b4", + "symbol": "iEOS", + "name": "Synth Inverse EOS", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iEOS.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0xd50c1746D835d2770dDA3703B69187bFfeB14126", + "symbol": "iETC", + "name": "Synth Inverse Ethereum Classic", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iETC.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0xA9859874e1743A32409f75bB11549892138BBA1E", + "symbol": "iETH", + "name": "Synth Inverse Ether", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iETH.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x2d7aC061fc3db53c39fe1607fB8cec1B2C162B01", + "symbol": "iLINK", + "name": "Synth Inverse Chainlink", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iLINK.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x79da1431150C9b82D2E5dfc1C68B33216846851e", + "symbol": "iLTC", + "name": "Synth Inverse Litecoin", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iLTC.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0xA5a5DF41883Cdc00c4cCC6E8097130535399d9a3", + "symbol": "iOIL", + "name": "Synth Inverse Perpetual Oil Futures", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iOIL.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x0fEd38108bdb8e62ef7b5680E8E0726E2F29e0De", + "symbol": "iREN", + "name": "Synth Inverse Ren", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iREN.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0xC5807183a9661A533CB08CbC297594a0B864dc12", + "symbol": "iTRX", + "name": "Synth Inverse TRON", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iTRX.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x36A00FF9072570eF4B9292117850B8FE08d96cce", + "symbol": "iUNI", + "name": "Synth Inverse Uniswap", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iUNI.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x4AdF728E2Df4945082cDD6053869f51278fae196", + "symbol": "iXMR", + "name": "Synth Inverse Monero", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iXMR.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x27269b3e45A4D3E79A3D6BFeE0C8fB13d0D711A6", + "symbol": "iXRP", + "name": "Synth Inverse Ripple", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iXRP.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x8deef89058090ac5655A99EEB451a4f9183D1678", + "symbol": "iXTZ", + "name": "Synth Inverse Tezos", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iXTZ.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0x592244301CeA952d6daB2fdC1fE6bd9E53917306", + "symbol": "iYFI", + "name": "Synth Inverse yearn.finance", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/iYFI.svg", + "tags": ["inverse", "synth"] + }, + { + "chainId": 1, + "address": "0xcD39b5434a0A92cf47D1F567a7dF84bE356814F0", + "symbol": "s1INCH", + "name": "Synth 1inch", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/s1INCH.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x7537AAe01f3B218DAE75e10d952473823F961B87", + "symbol": "sAAPL", + "name": "Synth Apple", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sAAPL.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xd2dF355C19471c8bd7D8A3aa27Ff4e26A21b4076", + "symbol": "sAAVE", + "name": "Synth Aave", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sAAVE.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xe36E2D3c7c34281FA3bC737950a68571736880A1", + "symbol": "sADA", + "name": "Synth Cardano", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sADA.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x9CF7E61853ea30A41b02169391b393B901eac457", + "symbol": "sAMZN", + "name": "Synth Amazon", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sAMZN.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xF48e200EAF9906362BB1442fca31e0835773b8B4", + "symbol": "sAUD", + "name": "Synth Australian Dollars", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sAUD.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x617aeCB6137B5108D1E7D4918e3725C8cEbdB848", + "symbol": "sBNB", + "name": "Synth Binance Coin", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sBNB.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xfE18be6b3Bd88A2D2A7f928d00292E7a9963CfC6", + "symbol": "sBTC", + "name": "Synth Bitcoin", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sBTC.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xeABACD844A196D7Faf3CE596edeBF9900341B420", + "symbol": "sCEX", + "name": "Synth Centralised Exchange Index", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sCEX.svg", + "tags": ["index", "synth"] + }, + { + "chainId": 1, + "address": "0x0F83287FF768D1c1e17a42F44d644D7F22e8ee1d", + "symbol": "sCHF", + "name": "Synth Swiss Franc", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sCHF.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x9EeF4CA7aB9fa8bc0650127341C2d3F707a40f8A", + "symbol": "sCOIN", + "name": "Synth Coinbase", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sCOIN.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xEb029507d3e043DD6C87F2917C4E82B902c35618", + "symbol": "sCOMP", + "name": "Synth Compound", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sCOMP.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xD38aEb759891882e78E957c80656572503D8c1B1", + "symbol": "sCRV", + "name": "Synth Curve DAO Token", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sCRV.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xfE33ae95A9f0DA8A845aF33516EDc240DCD711d6", + "symbol": "sDASH", + "name": "Synth Dash", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sDASH.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xe1aFe1Fd76Fd88f78cBf599ea1846231B8bA3B6B", + "symbol": "sDEFI", + "name": "Synth DeFi Index", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sDEFI.svg", + "tags": ["index", "synth"] + }, + { + "chainId": 1, + "address": "0x1715AC0743102BF5Cd58EfBB6Cf2dC2685d967b6", + "symbol": "sDOT", + "name": "Synth Polkadot", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sDOT.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x88C8Cf3A212c0369698D13FE98Fcb76620389841", + "symbol": "sEOS", + "name": "Synth EOS", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sEOS.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x22602469d704BfFb0936c7A7cfcD18f7aA269375", + "symbol": "sETC", + "name": "Synth Ethereum Classic", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sETC.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x5e74C9036fb86BD7eCdcb084a0673EFc32eA31cb", + "symbol": "sETH", + "name": "Synth Ether", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sETH.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xD71eCFF9342A5Ced620049e616c5035F1dB98620", + "symbol": "sEUR", + "name": "Synth Euros", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sEUR.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xf50B5e535F62a56A9BD2d8e2434204E726c027Fa", + "symbol": "sFB", + "name": "Synth Facebook", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sFB.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x23348160D7f5aca21195dF2b70f28Fce2B0be9fC", + "symbol": "sFTSE", + "name": "Synth FTSE 100 Index", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sFTSE.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x97fe22E7341a0Cd8Db6F6C021A24Dc8f4DAD855F", + "symbol": "sGBP", + "name": "Synth Pound Sterling", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sGBP.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xC63B8ECCE56aB9C46184eC6aB85e4771fEa4c8AD", + "symbol": "sGOOG", + "name": "Synth Alphabet", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sGOOG.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xF6b1C627e95BFc3c1b4c9B825a032Ff0fBf3e07d", + "symbol": "sJPY", + "name": "Synth Japanese Yen", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sJPY.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x269895a3dF4D73b077Fc823dD6dA1B95f72Aaf9B", + "symbol": "sKRW", + "name": "Synth South Korean Won", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sKRW.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xbBC455cb4F1B9e4bFC4B73970d360c8f032EfEE6", + "symbol": "sLINK", + "name": "Synth Chainlink", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sLINK.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xC14103C2141E842e228FBaC594579e798616ce7A", + "symbol": "sLTC", + "name": "Synth Litecoin", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sLTC.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x745a824D6aBBD236AA794b5530062778A6Ad7523", + "symbol": "sMSFT", + "name": "Synth Microsoft", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sMSFT.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x5A7E3c07604EB515C16b36cd51906a65f021F609", + "symbol": "sNFLX", + "name": "Synth Netflix", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sNFLX.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x757de3ac6B830a931eF178C6634c5C551773155c", + "symbol": "sNIKKEI", + "name": "Synth Nikkei 225 Index", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sNIKKEI.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x6d16cF3EC5F763d4d99cB0B0b110eefD93B11B56", + "symbol": "sOIL", + "name": "Synth Perpetual Oil Futures", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sOIL.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xD31533E8d0f3DF62060e94B3F1318137bB6E3525", + "symbol": "sREN", + "name": "Synth Ren", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sREN.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x0352557B007A4Aae1511C114409b932F06F9E2f4", + "symbol": "sRUNE", + "name": "Synth THORChain", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sRUNE.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xf2E08356588EC5cd9E437552Da87C0076b4970B0", + "symbol": "sTRX", + "name": "Synth TRON", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sTRX.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x918dA91Ccbc32B7a6A0cc4eCd5987bbab6E31e6D", + "symbol": "sTSLA", + "name": "Synth Tesla", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sTSLA.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x30635297E450b930f8693297eBa160D9e6c8eBcf", + "symbol": "sUNI", + "name": "Synth Uniswap", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sUNI.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x6A22e5e94388464181578Aa7A6B869e00fE27846", + "symbol": "sXAG", + "name": "Synth Silver Ounce", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sXAG.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x261EfCdD24CeA98652B9700800a13DfBca4103fF", + "symbol": "sXAU", + "name": "Synth Gold Ounce", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sXAU.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x5299d6F7472DCc137D7f3C4BcfBBB514BaBF341A", + "symbol": "sXMR", + "name": "Synth Monero", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sXMR.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0xa2B0fDe6D710e201d0d608e924A484d1A5fEd57c", + "symbol": "sXRP", + "name": "Synth Ripple", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sXRP.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x2e59005c5c0f0a4D77CcA82653d48b46322EE5Cd", + "symbol": "sXTZ", + "name": "Synth Tezos", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sXTZ.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x992058B7DB08F9734d84485bfbC243C4ee6954A7", + "symbol": "sYFI", + "name": "Synth yearn.finance", + "decimals": 18, + "logoURI": "https://raw.githubusercontent.com/Synthetixio/synthetix-assets/v2.0.10/synths/sYFI.svg", + "tags": ["synth"] + }, + { + "chainId": 1, + "address": "0x81ab848898b5ffD3354dbbEfb333D5D183eEDcB5", + "name": "yUSD Synthetic Expiring 1 September 2020", + "symbol": "yUSDSEP20", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0xB2FdD60AD80ca7bA89B9BAb3b5336c2601C020b4", + "name": "yUSD Synthetic Expiring 1 October 2020", + "symbol": "yUSDOCT20", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x208d174775dc39fe18b1b374972f77ddec6c0f73", + "name": "uUSDrBTC Synthetic Expiring 1 Oct 2020", + "symbol": "uUSDrBTC-OCT", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0xf06ddacf71e2992e2122a1a0168c6967afdf63ce", + "name": "uUSDrBTC Synthetic Expiring 31 Dec 2020", + "symbol": "uUSDrBTC-DEC", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0xd16c79c8a39d44b2f3eb45d2019cd6a42b03e2a9", + "name": "uUSDwETH Synthetic Expiring 31 Dec 2020", + "symbol": "uUSDwETH-DEC", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x3d995510f8d82c2ea341845932b5ddde0bead9a3", + "name": "uGAS-JAN21 Token Expiring 31 Jan 2021", + "symbol": "uGAS-JAN21", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x90f802c7e8fb5d40b0de583e34c065a3bd2020d8", + "name": "YD-ETH-MAR21 Token Expiring 31 Mar 2021", + "symbol": "YD-ETH-MAR21", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x002f0b1a71c5730cf2f4da1970a889207bdb6d0d", + "name": "YD-BTC-MAR21 Token Expiring 31 Mar 2021", + "symbol": "YD-BTC-MAR21", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x1062ad0e59fa67fa0b27369113098cc941dd0d5f", + "name": "UMA 35 Call Expirying 30 Apr 2021", + "symbol": "UMAc35-0421", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0xf93340b1a3adf7eedcaec25fae8171d4b736e89f", + "name": "pxUSD Synthetic USD Expiring 1 April 2021", + "symbol": "pxUSD_MAR2021", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x84bd083b1c8bf929f39c98bc17cf518f40154f58", + "name": "Mario Cash Synthetic Token Expiring 15 January 2021", + "symbol": "Mario Cash-JAN-2021", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x81fab276aec924fbde190cf379783526d413cf70", + "name": "uGAS-FEB21 Token Expiring 28 Feb 2021", + "symbol": "uGAS-FEB21", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x4e110603e70b0b5f1c403ee543b37e1f1244cf28", + "name": "uGAS-MAR21 Token Expiring 31 Mar 2021", + "symbol": "uGAS-MAR21", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0xcf55a7f92d5e0c6683debbc1fc20c0a6e056df13", + "name": "Zelda Elastic Cash", + "symbol": "Zelda Elastic Cash", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x654eebac62240e6c56bab5f6adf7cfa74a894510", + "name": "Zelda Spring Nuts Cash", + "symbol": "Zelda Spring Nuts Cash", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0xa48920cc1ad85d8ea13af5d7be180c0338c306dd", + "name": "Zelda Summer Nuts Cash", + "symbol": "Zelda Summer Nuts Cash", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x249a198d59b57fda5dda90630febc86fd8c7594c", + "name": "Zelda Whirlwind Cash", + "symbol": "Zelda Whirlwind Cash", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x5ed1406873c9eb91f6f9a67ac4e152387c1132e7", + "name": "Zelda Reinforced Cash", + "symbol": "Zelda Reinforced Cash", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x8104c9f13118320eefe5fbea8a44d600b85981ef", + "name": "Mini Mario Summer Cash", + "symbol": "Mini Mario Summer Cash", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x69746c719e59674b147df25f50e7cfa0673cb625", + "name": "Mini Mario Spring Cash", + "symbol": "Mini Mario Spring Cash", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x6b1257641d18791141f025eab36fb567c4b564ff", + "name": "Bitcoin Dominance Token 31 March 2021", + "symbol": "BTCDOM-MAR2021", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x4e83b6287588a96321b2661c5e041845ff7814af", + "name": "Altcoin Dominance Token 31 March 2021", + "symbol": "ALTDOM-MAR2021", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x59fec83ec709c893aedd1a144cf1828eb04127cd", + "name": "pxGOLD Synthetic GOLD Expiring 31 May 2021", + "symbol": "pxGOLD_MAY2021", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x89337BFb7938804c3776C9FB921EccAf5ab76758", + "name": "Compound Annualized Rate Future Expiring 28 March 2021", + "symbol": "CAR-USDC-MAR21", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0xec58d3aefc9aaa2e0036fa65f70d569f49d9d1ed", + "name": "uSTONKS Index Token April 2021", + "symbol": "uSTONKS_APR21", + "decimals": 6 + }, + { + "chainId": 1, + "address": "0xa6B9d7E3d76cF23549293Fb22c488E0Ea591A44e", + "name": "uGAS-JUN21 Token Expiring 30 Jun 2021", + "symbol": "uGAS-JUN21", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0xe813b65da6c38a04591aed3f082d32db7d53c382", + "name": "Yield Dollar [WETH Dec 2021]", + "symbol": "YD-ETH-DEC21", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x4b606e9eb2228c70f44453afe5a73e1fea258ce1", + "name": "pxUSD Synthetic USD Expiring 31 Mar 2022", + "symbol": "pxUSD_MAR2022", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x5247c0db4044fb6f97f32c7e1b48758019a5a912", + "name": "pxGOLD Synthetic Gold Expiring 31 Mar 2022", + "symbol": "pxGOLD_MAR2022", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x56fb1acaff95c0b6ebcd17c8361a63d98b1a5a11", + "name": "uForex CNYUSD Synthetic Token April 2021", + "symbol": "uCNYUSD-APR", + "decimals": 6 + }, + { + "chainId": 1, + "address": "0xd49fa405dce086c65d66ca1ca41f8e98583812b4", + "name": "uForex EURUSD Synthetic Token April 2021", + "symbol": "uEURUSD-APR", + "decimals": 6 + }, + { + "chainId": 1, + "address": "0x29dddacba3b231ee8d673dd0f0fa759ea145561b", + "name": "DEFI_PULSE_TOTAL_TVL Synthetic Token Expiring 15 April 2021", + "symbol": "TVL_ALL_APRIL15", + "decimals": 6 + }, + { + "chainId": 1, + "address": "0xcbe430927370e95b4b10cfc702c6017ec7abefc3", + "name": "Yield Dollar [WETH Jun 2021]", + "symbol": "YD-ETH-JUN21", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x4b7fb448df91c8ed973494f8c8c4f12daf3a8521", + "name": "Yield Dollar [renBTC Jun 2021]", + "symbol": "YD-BTC-JUN21", + "decimals": 8 + }, + { + "chainId": 1, + "address": "0x3108c33b6fb38efedaefd8b5f7ca01d5f5c7372d", + "name": "Yield Dollar UMA 21", + "symbol": "yUMA21", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x0cae9e4d663793c2a2A0b211c1Cf4bBca2B9cAa7", + "name": "Mirrored Amazon", + "symbol": "MAMZN", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x31c63146a635EB7465e5853020b39713AC356991", + "name": "M US Oil", + "symbol": "MUSO", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x59A921Db27Dd6d4d974745B7FfC5c33932653442", + "name": "M Google", + "symbol": "MGOOGL", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0xf72FCd9DCF0190923Fadd44811E240Ef4533fc86", + "name": "Mirrored ProShares", + "symbol": "MVIXY", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x56aA298a19C93c6801FDde870fA63EF75Cc0aF72", + "name": "Mirrored Alibaba", + "symbol": "MBABA", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x0e99cC0535BB6251F6679Fa6E65d6d3b430e840B", + "name": "Mirrored Facebook", + "symbol": "MFB", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x13B02c8dE71680e71F0820c996E4bE43c2F57d15", + "name": "Mirrored Invesco QQ", + "symbol": "MQQQ", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x41BbEDd7286dAab5910a1f15d12CBda839852BD7", + "name": "Mirrored Microsoft", + "symbol": "MMSFT", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x9d1555d8cB3C846Bb4f7D5B1B1080872c3166676", + "name": "Mirrored iShares Si", + "symbol": "MSLV", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0x21cA39943E91d704678F5D00b6616650F066fD63", + "name": "Mirrored Tesla", + "symbol": "MTSLA", + "decimals": 18 + }, + { + "chainId": 1, + "address": "0xe82bbB62fA81d0701643d1675FB50ec52fD3Df92", + "name": "DYDX Token", + "symbol": "DYDX", + "decimals": 18 + }, + { + "chainId": 10, + "address": "0xE405de8F52ba7559f9df3C368500B6E6ae6Cee49", + "name": "sETH", + "symbol": "Synth Ether", + "decimals": 18 + }, + { + "chainId": 10, + "address": "0x298B9B95708152ff6968aafd889c6586e9169f1D", + "name": "sBTC", + "symbol": "Synth Bitcoin", + "decimals": 18 + }, + { + "chainId": 10, + "address": "0xc5Db22719A06418028A40A9B5E9A7c02959D0d08", + "name": "sLINK", + "symbol": "Synth Link", + "decimals": 18 + } + ] +} diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts index 248ad7c52..de70db2a8 100644 --- a/src/constants/tokens.ts +++ b/src/constants/tokens.ts @@ -1,28 +1,112 @@ -import { WETH9, Token /* , Ether */ } from '@uniswap/sdk-core' +import { WETH9, Token, Ether } from '@uniswap/sdk-core' import { UNI_ADDRESS } from './addresses' import { SupportedChainId } from './chains' -export const AMPL = new Token(1, '0xD46bA6D942050d489DBd938a2C909A5d5039A161', 9, 'AMPL', 'Ampleforth') -export const DAI = new Token(1, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'Dai Stablecoin') -export const USDC = new Token(1, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', 'USD//C') -export const USDT = new Token(1, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT', 'Tether USD') -export const WBTC = new Token(1, '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 8, 'WBTC', 'Wrapped BTC') -export const FEI = new Token(1, '0x956F47F50A910163D8BF957Cf5846D573E7f87CA', 18, 'FEI', 'Fei USD') -export const TRIBE = new Token(1, '0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B', 18, 'TRIBE', 'Tribe') -export const FRAX = new Token(1, '0x853d955aCEf822Db058eb8505911ED77F175b99e', 18, 'FRAX', 'Frax') -export const FXS = new Token(1, '0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0', 18, 'FXS', 'Frax Share') -export const renBTC = new Token(1, '0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D', 8, 'renBTC', 'renBTC') -export const UMA = new Token(1, '0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828', 18, 'UMA', 'UMA Voting Token v1') +export const AMPL = new Token( + SupportedChainId.MAINNET, + '0xD46bA6D942050d489DBd938a2C909A5d5039A161', + 9, + 'AMPL', + 'Ampleforth' +) +export const DAI = new Token( + SupportedChainId.MAINNET, + '0x6B175474E89094C44Da98b954EedeAC495271d0F', + 18, + 'DAI', + 'Dai Stablecoin' +) +export const USDC = new Token( + SupportedChainId.MAINNET, + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + 6, + 'USDC', + 'USD//C' +) +export const USDC_ARBITRUM = new Token( + SupportedChainId.ARBITRUM_ONE, + '0xe865dF68133fcEd7c2285ff3896B406CAfAa2dB8', + 6, + 'USDC', + 'USD//C' +) +export const DAI_OPTIMISM = new Token( + SupportedChainId.OPTIMISM, + '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', + 18, + 'DAI', + 'Dai stable coin' +) +export const USDT = new Token( + SupportedChainId.MAINNET, + '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 6, + 'USDT', + 'Tether USD' +) +export const USDT_OPTIMISM = new Token( + SupportedChainId.OPTIMISM, + '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', + 6, + 'USDT', + 'Tether USD' +) +export const WBTC = new Token( + SupportedChainId.MAINNET, + '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', + 8, + 'WBTC', + 'Wrapped BTC' +) +export const WBTC_OPTIMISM = new Token( + SupportedChainId.OPTIMISM, + '0x68f180fcCe6836688e9084f035309E29Bf0A2095', + 8, + 'WBTC', + 'Wrapped BTC' +) +export const FEI = new Token( + SupportedChainId.MAINNET, + '0x956F47F50A910163D8BF957Cf5846D573E7f87CA', + 18, + 'FEI', + 'Fei USD' +) +export const TRIBE = new Token( + SupportedChainId.MAINNET, + '0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B', + 18, + 'TRIBE', + 'Tribe' +) +export const FRAX = new Token( + SupportedChainId.MAINNET, + '0x853d955aCEf822Db058eb8505911ED77F175b99e', + 18, + 'FRAX', + 'Frax' +) +export const FXS = new Token( + SupportedChainId.MAINNET, + '0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0', + 18, + 'FXS', + 'Frax Share' +) +export const renBTC = new Token( + SupportedChainId.MAINNET, + '0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D', + 8, + 'renBTC', + 'renBTC' +) export const ETH2X_FLI = new Token( - 1, + SupportedChainId.MAINNET, '0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD', 18, 'ETH2x-FLI', 'ETH 2x Flexible Leverage Index' ) -// Mirror Protocol compat. -export const UST = new Token(1, '0xa47c8bf37f92abed4a126bda807a7b7498661acd', 18, 'UST', 'Wrapped UST') -export const MIR = new Token(1, '0x09a3ecafa817268f77be1283176b946c4ff2e608', 18, 'MIR', 'Wrapped MIR') export const UNI: { [chainId: number]: Token } = { [SupportedChainId.MAINNET]: new Token(SupportedChainId.MAINNET, UNI_ADDRESS[1], 18, 'UNI', 'Uniswap'), [SupportedChainId.RINKEBY]: new Token(SupportedChainId.RINKEBY, UNI_ADDRESS[4], 18, 'UNI', 'Uniswap'), @@ -30,13 +114,21 @@ export const UNI: { [chainId: number]: Token } = { [SupportedChainId.GOERLI]: new Token(SupportedChainId.GOERLI, UNI_ADDRESS[5], 18, 'UNI', 'Uniswap'), [SupportedChainId.KOVAN]: new Token(SupportedChainId.KOVAN, UNI_ADDRESS[42], 18, 'UNI', 'Uniswap'), } + export const WETH9_EXTENDED: { [chainId: number]: Token } = { ...WETH9, - [SupportedChainId.ARBITRUM_KOVAN]: new Token( - SupportedChainId.ARBITRUM_KOVAN, - '0x4A5e4A42dC430f669086b417AADf2B128beFEfac', + [SupportedChainId.OPTIMISM]: new Token( + SupportedChainId.OPTIMISM, + '0x4200000000000000000000000000000000000006', + 18, + 'WETH', + 'Wrapped Ether' + ), + [SupportedChainId.OPTIMISTIC_KOVAN]: new Token( + SupportedChainId.OPTIMISTIC_KOVAN, + '0x4200000000000000000000000000000000000006', 18, - 'WETH9', + 'WETH', 'Wrapped Ether' ), [SupportedChainId.ARBITRUM_ONE]: new Token( @@ -46,15 +138,24 @@ export const WETH9_EXTENDED: { [chainId: number]: Token } = { 'WETH', 'Wrapped Ether' ), + [SupportedChainId.ARBITRUM_RINKEBY]: new Token( + SupportedChainId.ARBITRUM_RINKEBY, + '0xB47e6A5f8b33b3F17603C83a0535A9dcD7E32681', + 18, + 'WETH', + 'Wrapped Ether' + ), } -// export class ExtendedEther extends Ether { -// public get wrapped(): Token { -// if (this.chainId in WETH9_EXTENDED) return WETH9_EXTENDED[this.chainId] -// throw new Error('Unsupported chain ID') -// } +export class ExtendedEther extends Ether { + public get wrapped(): Token { + if (this.chainId in WETH9_EXTENDED) return WETH9_EXTENDED[this.chainId] + throw new Error('Unsupported chain ID') + } -// public static onChain(chainId: number): ExtendedEther { -// return new ExtendedEther(chainId) -// } -// } + private static _cachedEther: { [chainId: number]: ExtendedEther } = {} + + public static onChain(chainId: number): ExtendedEther { + return this._cachedEther[chainId] ?? (this._cachedEther[chainId] = new ExtendedEther(chainId)) + } +} diff --git a/src/custom/api/matcha-0x/index.ts b/src/custom/api/matcha-0x/index.ts index b1c102b12..18c444396 100644 --- a/src/custom/api/matcha-0x/index.ts +++ b/src/custom/api/matcha-0x/index.ts @@ -59,8 +59,6 @@ function getApiUrl(): Partial> { // See https://0x.org/docs/api#introduction return { [ChainId.MAINNET]: 'https://api.0x.org/swap', - // we don't support ropsten but let's leave it for posterity - [ChainId.ROPSTEN]: 'https://ropsten.api.0x.org/swap', } } diff --git a/src/custom/api/paraswap/index.ts b/src/custom/api/paraswap/index.ts index f21a0f095..6bcb0f1bc 100644 --- a/src/custom/api/paraswap/index.ts +++ b/src/custom/api/paraswap/index.ts @@ -21,7 +21,6 @@ function getParaswapChainId(chainId: ChainId): NetworkID | null { // Only Mainnnet and Ropsten supported // See https://developers.paraswap.network/api/list-all-tokens case ChainId.MAINNET: - case ChainId.ROPSTEN: return chainId default: diff --git a/src/custom/assets/cow-swap/discord.svg b/src/custom/assets/cow-swap/discord.svg new file mode 100644 index 000000000..58721be55 --- /dev/null +++ b/src/custom/assets/cow-swap/discord.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/custom/assets/cow-swap/twitter.svg b/src/custom/assets/cow-swap/twitter.svg index f493c077a..69882d6e2 100644 --- a/src/custom/assets/cow-swap/twitter.svg +++ b/src/custom/assets/cow-swap/twitter.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/custom/components/AccountDetails/AccountDetailsMod.tsx b/src/custom/components/AccountDetails/AccountDetailsMod.tsx index 46c423226..983336b03 100644 --- a/src/custom/components/AccountDetails/AccountDetailsMod.tsx +++ b/src/custom/components/AccountDetails/AccountDetailsMod.tsx @@ -1,5 +1,5 @@ import React /* , { useCallback, useContext } */ from 'react' -import styled /* , { ThemeContext } */ from 'styled-components' +import styled /* , { ThemeContext } */ from 'styled-components/macro' // import { useActiveWeb3React } from 'hooks/web3' // import { clearAllTransactions } from 'state/transactions/actions' // import { shortenAddress } from 'utils' diff --git a/src/custom/components/AccountDetails/Transaction/index.tsx b/src/custom/components/AccountDetails/Transaction/index.tsx index 86bd0f4eb..493fdc454 100644 --- a/src/custom/components/AccountDetails/Transaction/index.tsx +++ b/src/custom/components/AccountDetails/Transaction/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { CurrencyAmount } from '@uniswap/sdk-core' import { useActiveWeb3React } from 'hooks/web3' diff --git a/src/custom/components/AccountDetails/Transaction/styled.ts b/src/custom/components/AccountDetails/Transaction/styled.ts index 383fe959e..83646a622 100644 --- a/src/custom/components/AccountDetails/Transaction/styled.ts +++ b/src/custom/components/AccountDetails/Transaction/styled.ts @@ -1,4 +1,4 @@ -import styled, { css } from 'styled-components' +import styled, { css } from 'styled-components/macro' import { StyledSVG } from 'components/Loader' import { LinkStyledButton } from 'theme' import { TransactionState as OldTransactionState } from '../TransactionMod' diff --git a/src/custom/components/AccountDetails/TransactionMod.tsx b/src/custom/components/AccountDetails/TransactionMod.tsx index a467f1794..52113fab7 100644 --- a/src/custom/components/AccountDetails/TransactionMod.tsx +++ b/src/custom/components/AccountDetails/TransactionMod.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { CheckCircle, Triangle } from 'react-feather' import { useActiveWeb3React } from 'hooks/web3' diff --git a/src/custom/components/AccountDetails/index.tsx b/src/custom/components/AccountDetails/index.tsx index 4ecd76bf2..6d4355f11 100644 --- a/src/custom/components/AccountDetails/index.tsx +++ b/src/custom/components/AccountDetails/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react' +import { useCallback } from 'react' import { batch, useDispatch } from 'react-redux' import { useActiveWeb3React } from 'hooks/web3' diff --git a/src/custom/components/AccountDetails/styled.ts b/src/custom/components/AccountDetails/styled.ts index 715423395..9037132cd 100644 --- a/src/custom/components/AccountDetails/styled.ts +++ b/src/custom/components/AccountDetails/styled.ts @@ -1,7 +1,6 @@ -import styled from 'styled-components' +import styled from 'styled-components/macro' import { CopyIcon, TransactionStatusText } from 'components/Copy' import { LinkStyledButton } from 'theme' -import { NetworkCard as NetworkCardUni } from 'components/Header/HeaderMod' import { WalletName, AccountSection as AccountSectionMod, @@ -11,6 +10,7 @@ import { TransactionListWrapper, AccountControl, } from './AccountDetailsMod' +import { YellowCard } from 'components/Card' export const WalletActions = styled.div` display: flex; @@ -90,19 +90,6 @@ export const Wrapper = styled.div` } ` -export const NetworkCard = styled(NetworkCardUni)` - background-color: ${({ theme }) => theme.networkCard.background}; - color: ${({ theme }) => theme.networkCard.text}; - padding: 6px 8px; - font-size: 13px; - margin: 0 8px 0 0; - letter-spacing: 0.7px; - - ${({ theme }) => theme.mediaWidth.upToSmall` - flex-shrink: 0; - `}; -` - export const UpperSection = styled(UpperSectionMod)` flex: 1 1 auto; width: 100%; @@ -193,3 +180,31 @@ export const LowerSection = styled.div` text-decoration: underline; } ` + +const NetworkCardUni = styled(YellowCard)` + border-radius: 12px; + padding: 8px 12px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + margin: 0; + margin-right: 0.5rem; + width: initial; + overflow: hidden; + text-overflow: ellipsis; + flex-shrink: 1; + `}; +` + +export const NetworkCard = styled(NetworkCardUni)` + background-color: ${({ theme }) => theme.networkCard.background}; + color: ${({ theme }) => theme.networkCard.text}; + + padding: 6px 8px; + font-size: 13px; + margin: 0 8px 0 0; + letter-spacing: 0.7px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + flex-shrink: 0; + `}; +` diff --git a/src/custom/components/AppziButton/index.tsx b/src/custom/components/AppziButton/index.tsx index 1a568c3e1..b72c08710 100644 --- a/src/custom/components/AppziButton/index.tsx +++ b/src/custom/components/AppziButton/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { ButtonPrimary } from '../Button' import FeedbackIcon from './../../assets/cow-swap/feedback.svg' import ReactAppzi from 'react-appzi' diff --git a/src/custom/components/ArrowWrapperLoader/index.tsx b/src/custom/components/ArrowWrapperLoader/index.tsx index 1b04b6642..bc18da974 100644 --- a/src/custom/components/ArrowWrapperLoader/index.tsx +++ b/src/custom/components/ArrowWrapperLoader/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import loadingCowGif from 'assets/cow-swap/cow-load.gif' import { ArrowDown } from 'react-feather' import useLoadingWithTimeout from 'hooks/useLoadingWithTimeout' diff --git a/src/custom/components/Button/ButtonMod.tsx b/src/custom/components/Button/ButtonMod.tsx index 0ef6236b6..8304c5b45 100644 --- a/src/custom/components/Button/ButtonMod.tsx +++ b/src/custom/components/Button/ButtonMod.tsx @@ -1,6 +1,5 @@ -import React from 'react' import styled from 'styled-components/macro' -import { darken, lighten } from 'polished' +import { darken } from 'polished' import { RowBetween } from 'components/Row' import { ChevronDown, Check } from 'react-feather' @@ -14,18 +13,17 @@ const Base = styled(RebassButton)< { padding?: string width?: string - borderRadius?: string + $borderRadius?: string altDisabledStyle?: boolean buttonSize?: ButtonSize } & ButtonProps >` - padding: ${({ padding }) => (padding ? padding : '16px')}; - width: ${({ width }) => (width ? width : '100%')}; + padding: ${({ padding }) => padding ?? '16px'}; + width: ${({ width }) => width ?? '100%'}; ${({ theme, buttonSize = ButtonSize.DEFAULT }) => theme.buttonSizes[buttonSize]}; font-weight: 500; text-align: center; - border-radius: 20px; - border-radius: ${({ borderRadius }) => borderRadius && borderRadius}; + border-radius: ${({ $borderRadius }) => $borderRadius ?? '20px'}; outline: none; border: 1px solid transparent; color: white; @@ -161,63 +159,63 @@ export const ButtonSecondary = styled(Base)` } ` -export const ButtonPink = styled(Base)` - background-color: ${({ theme }) => theme.primary1}; - color: white; - +export const ButtonOutlined = styled(Base)` + border: 1px solid ${({ theme }) => theme.bg2}; + background-color: transparent; + color: ${({ theme }) => theme.text1}; &:focus { - box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.primary1)}; - background-color: ${({ theme }) => darken(0.05, theme.primary1)}; + box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; } &:hover { - background-color: ${({ theme }) => darken(0.05, theme.primary1)}; + box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; } &:active { - box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.primary1)}; - background-color: ${({ theme }) => darken(0.1, theme.primary1)}; + box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; } &:disabled { - background-color: ${({ theme }) => theme.primary1}; opacity: 50%; cursor: auto; } ` -export const ButtonUNIGradient = styled(ButtonPrimary)` +export const ButtonYellow = styled(Base)` + background-color: ${({ theme }) => theme.yellow3}; color: white; - padding: 4px 8px; - height: 36px; - font-weight: 500; - background-color: ${({ theme }) => theme.bg3}; - background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2; - width: fit-content; - position: relative; - cursor: pointer; - border: none; - white-space: no-wrap; - :hover { - opacity: 0.8; + &:focus { + box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.yellow3)}; + background-color: ${({ theme }) => darken(0.05, theme.yellow3)}; } - :active { - opacity: 0.9; + &:hover { + background-color: ${({ theme }) => darken(0.05, theme.yellow3)}; + } + &:active { + box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.yellow3)}; + background-color: ${({ theme }) => darken(0.1, theme.yellow3)}; + } + &:disabled { + background-color: ${({ theme }) => theme.yellow3}; + opacity: 50%; + cursor: auto; } ` -export const ButtonOutlined = styled(Base)` - border: 1px solid ${({ theme }) => theme.bg2}; - background-color: transparent; - color: ${({ theme }) => theme.text1}; +export const ButtonPink = styled(Base)` + background-color: ${({ theme }) => theme.primary1}; + color: white; &:focus { - box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; + box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.primary1)}; + background-color: ${({ theme }) => darken(0.05, theme.primary1)}; } &:hover { - box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; + background-color: ${({ theme }) => darken(0.05, theme.primary1)}; } &:active { - box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4}; + box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.primary1)}; + background-color: ${({ theme }) => darken(0.1, theme.primary1)}; } &:disabled { + background-color: ${({ theme }) => theme.primary1}; opacity: 50%; cursor: auto; } @@ -267,6 +265,19 @@ export const ButtonText = styled(Base)` } ` +export const ButtonConfirmedStyle = styled(Base)` + background-color: ${({ theme }) => theme.bg3}; + color: ${({ theme }) => theme.text1}; + /* border: 1px solid ${({ theme }) => theme.green1}; */ + + &:disabled { + /* opacity: 50%; */ + background-color: ${({ theme }) => theme.bg2}; + color: ${({ theme }) => theme.text2}; + cursor: auto; + } +` + export const ButtonWhite = styled(Base)` border: 1px solid #edeef2; background-color: ${({ theme }) => theme.bg1}; @@ -288,19 +299,6 @@ export const ButtonWhite = styled(Base)` } ` -export const ButtonConfirmedStyle = styled(Base)` - background-color: ${({ theme }) => lighten(0.5, theme.green1)}; - color: ${({ theme }) => theme.green1}; - border: 1px solid ${({ theme }) => theme.green1}; - - &:disabled { - opacity: 50%; - background-color: ${({ theme }) => theme.bg2}; - color: ${({ theme }) => theme.text2}; - cursor: auto; - } -` - export const ButtonErrorStyle = styled(Base)` background-color: ${({ theme }) => theme.red1}; border: 1px solid ${({ theme }) => theme.red1}; @@ -356,17 +354,6 @@ export function ButtonDropdown({ disabled = false, children, ...rest }: { disabl ) } -export function ButtonDropdownGrey({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) { - return ( - - -
{children}
- -
-
- ) -} - export function ButtonDropdownLight({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) { return ( diff --git a/src/custom/components/Button/index.tsx b/src/custom/components/Button/index.tsx index 2a518271f..bc43f7f17 100644 --- a/src/custom/components/Button/index.tsx +++ b/src/custom/components/Button/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { ButtonProps } from 'rebass/styled-components' import { ChevronDown } from 'react-feather' @@ -21,6 +20,8 @@ import { } from './ButtonMod' import { ButtonSize } from 'theme' +export * from './ButtonMod' + export const ButtonPrimary = styled(ButtonPrimaryMod)` // CSS overrides ${({ theme }) => theme.buttonPrimary.background} @@ -106,13 +107,18 @@ export const ButtonLight = styled(ButtonLightMod)` export const ButtonGray = styled(ButtonGrayMod)` // CSS overrides + &:hover, + &:focus { + box-shadow: none; + } ` export const ButtonSecondary = styled(ButtonSecondaryMod)` // CSS overrides transition: box-shadow 0.1s ease-in-out; - &:hover { + &:hover, + &:focus { box-shadow: none; } ` diff --git a/src/custom/components/ClickWrap/index.tsx b/src/custom/components/ClickWrap/index.tsx index a31f4f7c7..09852010b 100644 --- a/src/custom/components/ClickWrap/index.tsx +++ b/src/custom/components/ClickWrap/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { NavLink } from 'react-router-dom' import { ButtonPrimary, ButtonOutlined } from 'components/Button' diff --git a/src/custom/components/Copy/CopyMod.tsx b/src/custom/components/Copy/CopyMod.tsx index 9ebac2dfb..1d4ace101 100644 --- a/src/custom/components/Copy/CopyMod.tsx +++ b/src/custom/components/Copy/CopyMod.tsx @@ -1,4 +1,3 @@ -import React from 'react' import styled from 'styled-components/macro' import useCopyClipboard from 'hooks/useCopyClipboard' @@ -13,7 +12,6 @@ export const CopyIcon = styled(LinkStyledButton)` display: flex; align-items: center; justify-content: center; - padding: 0; text-decoration: none; font-size: 0.825rem; border-radius: 50%; diff --git a/src/custom/components/CurrencyInputPanel/CurrencyInputPanelMod.tsx b/src/custom/components/CurrencyInputPanel/CurrencyInputPanelMod.tsx index a821e7a27..f1db0a950 100644 --- a/src/custom/components/CurrencyInputPanel/CurrencyInputPanelMod.tsx +++ b/src/custom/components/CurrencyInputPanel/CurrencyInputPanelMod.tsx @@ -1,6 +1,6 @@ import { Pair } from '@uniswap/v2-sdk' import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core' -import React, { useState, useCallback, ReactNode } from 'react' +import { useState, useCallback, ReactNode } from 'react' import styled from 'styled-components/macro' import { darken } from 'polished' import { useCurrencyBalance } from 'state/wallet/hooks' @@ -8,7 +8,7 @@ import { useCurrencyBalance } from 'state/wallet/hooks' import { CurrencySearchModal } from '.' // mod import CurrencyLogo from 'components/CurrencyLogo' import DoubleCurrencyLogo from 'components/DoubleLogo' -// import { ButtonGray } from '../Button' +import { ButtonGray } from 'components/Button' import { RowBetween, RowFixed } from 'components/Row' import { TYPE } from 'theme' import { Input as NumericalInput } from 'components/NumericalInput' @@ -59,7 +59,8 @@ export const Container = styled.div<{ hideInput: boolean; showAux?: boolean }>` } ` -export const CurrencySelect = styled.button<{ selected: boolean; hideInput?: boolean }>` +export const CurrencySelect = styled(ButtonGray)<{ visible: boolean; selected: boolean; hideInput?: boolean }>` + visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')}; align-items: center; font-size: 24px; font-weight: 500; @@ -160,6 +161,8 @@ export interface CurrencyInputPanelProps extends WithClassName { priceImpact?: Percent id: string showCommonBases?: boolean + showCurrencyAmount?: boolean + disableNonToken?: boolean renderBalance?: (amount: CurrencyAmount) => ReactNode locked?: boolean customBalanceText?: string @@ -175,6 +178,8 @@ export default function CurrencyInputPanel({ otherCurrency, id, showCommonBases, + showCurrencyAmount, + disableNonToken, renderBalance, fiatValue, priceImpact, @@ -210,6 +215,7 @@ export default function CurrencyInputPanel({ )} diff --git a/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx b/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx index ff973994b..cb851cbb1 100644 --- a/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx +++ b/src/custom/components/CurrencyInputPanel/FiatValue/FiatValueMod.tsx @@ -1,5 +1,5 @@ import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' -import React, { useMemo } from 'react' +import { useMemo } from 'react' import useTheme from 'hooks/useTheme' import { TYPE } from 'theme' import { warningSeverity } from 'utils/prices' diff --git a/src/custom/components/CurrencyInputPanel/index.tsx b/src/custom/components/CurrencyInputPanel/index.tsx index 2fc0aa351..df798c30d 100644 --- a/src/custom/components/CurrencyInputPanel/index.tsx +++ b/src/custom/components/CurrencyInputPanel/index.tsx @@ -1,6 +1,6 @@ // import { darken } from 'polished' -import React from 'react' -import styled, { css } from 'styled-components' + +import styled, { css } from 'styled-components/macro' import { darken } from 'polished' import useLoadingWithTimeout from 'hooks/useLoadingWithTimeout' import { useIsQuoteRefreshing } from 'state/price/hooks' diff --git a/src/custom/components/CurrencyLogo/CurrencyLogoMod.tsx b/src/custom/components/CurrencyLogo/CurrencyLogoMod.tsx index 0c68ffc54..b9f120ca6 100644 --- a/src/custom/components/CurrencyLogo/CurrencyLogoMod.tsx +++ b/src/custom/components/CurrencyLogo/CurrencyLogoMod.tsx @@ -1,5 +1,5 @@ import { Currency } from '@uniswap/sdk-core' -import React, { useMemo } from 'react' +import { useMemo } from 'react' import styled from 'styled-components/macro' import EthereumLogo from 'assets/images/ethereum-logo.png' import useHttpLocations from 'hooks/useHttpLocations' @@ -31,7 +31,7 @@ export default function CurrencyLogo({ style, ...rest }: { - currency?: Currency + currency?: Currency | null size?: string style?: React.CSSProperties }) { @@ -52,7 +52,7 @@ export default function CurrencyLogo({ }, [currency, uriLocations]) if (currency?.isNative) { - return + return } return diff --git a/src/custom/components/ErrorBoundary/ErrorBoundaryMod.tsx b/src/custom/components/ErrorBoundary/ErrorBoundaryMod.tsx index 7c7172067..050e2383a 100644 --- a/src/custom/components/ErrorBoundary/ErrorBoundaryMod.tsx +++ b/src/custom/components/ErrorBoundary/ErrorBoundaryMod.tsx @@ -1,5 +1,5 @@ import { Trans } from '@lingui/macro' -import React, { ErrorInfo } from 'react' +import { Component, ErrorInfo } from 'react' import store, { AppState } from 'state/index' import { ExternalLink, TYPE } from 'theme/index' import Page, { Title } from 'components/Page' @@ -116,7 +116,7 @@ function truncate(value?: string): string | undefined { return value ? value.slice(0, 1000) : undefined } -export default class ErrorBoundary extends React.Component { +export default class ErrorBoundary extends Component { constructor(props: unknown) { super(props) this.state = { error: null } diff --git a/src/custom/components/ExplorerLink/index.tsx b/src/custom/components/ExplorerLink/index.tsx index df2125512..3a3d08ef1 100644 --- a/src/custom/components/ExplorerLink/index.tsx +++ b/src/custom/components/ExplorerLink/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { ExternalLink } from 'theme' import { useActiveWeb3React } from '@src/hooks/web3' import { BlockExplorerLinkType, getExplorerLabel, getEtherscanLink } from 'utils' diff --git a/src/custom/components/Footer/index.tsx b/src/custom/components/Footer/index.tsx index 392c85af1..e086fcfca 100644 --- a/src/custom/components/Footer/index.tsx +++ b/src/custom/components/Footer/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import Version from '../Version' // import ClickWrap from '../ClickWrap' diff --git a/src/custom/components/Header/HeaderMod.tsx b/src/custom/components/Header/HeaderMod.tsx index 870a6d44f..bc8941511 100644 --- a/src/custom/components/Header/HeaderMod.tsx +++ b/src/custom/components/Header/HeaderMod.tsx @@ -1,36 +1,31 @@ -import React, { PropsWithChildren /*, { useState }*/ } from 'react' -import { Text } from 'rebass' -import { NavLink } from 'react-router-dom' +// import { Trans } from '@lingui/macro' +import useScrollPosition from '@react-hook/window-scroll' +/* import { CHAIN_INFO, SupportedChainId } from 'constants/chains' */ import { darken } from 'polished' -// import { useTranslation } from 'react-i18next' - -import styled from 'styled-components' - -// import Logo from '../../assets/svg/logo.svg' -// import LogoDark from '../../assets/svg/logo_white.svg' -// import { useActiveWeb3React } from '../../hooks' -// import { useDarkModeManager } from '../../state/user/hooks' -// import { useETHBalances, useAggregateUniBalance } from '../../state/wallet/hooks' -// import { CardNoise } from '../earn/styled' -// import { CountUp } from 'use-count-up' -import { /* TYPE,*/ ExternalLink } from 'theme' - -import { YellowCard } from 'components/Card' -// import { Moon, Sun } from 'react-feather' -// import Menu from '../Menu' - +import { /* useState, */ PropsWithChildren } from 'react' +import { NavLink } from 'react-router-dom' +import { Text } from 'rebass' +/* import { useShowClaimPopup, useToggleSelfClaimModal } from 'state/application/hooks' +import { useUserHasAvailableClaim } from 'state/claim/hooks' +import { useUserHasSubmittedClaim } from 'state/transactions/hooks' +import { useDarkModeManager } from 'state/user/hooks' +import { useETHBalances } from 'state/wallet/hooks' */ +import styled from 'styled-components/macro' +/* import Logo from '../../assets/svg/logo.svg' +import LogoDark from '../../assets/svg/logo_white.svg' +import { useActiveWeb3React } from '../../hooks/web3' +import { ExternalLink, TYPE } from '../../theme' +import ClaimModal from '../claim/ClaimModal' +import { CardNoise } from '../earn/styled' +import Menu from '../Menu' +import Modal from '../Modal' */ import Row, { RowFixed } from 'components/Row' -// import Web3Status from '../Web3Status' -// import ClaimModal from '../claim/ClaimModal' -// import { useToggleSelfClaimModal, useShowClaimPopup } from 'state/application/hooks' -// import { useUserHasAvailableClaim } from '../../state/claim/hooks' -// import { useUserHasSubmittedClaim } from '../../state/transactions/hooks' -// import { Dots } from '../swap/styleds' -// import Modal from '../Modal' -// import UniBalanceContent from './UniBalanceContent' -// import usePrevious from '../../hooks/usePrevious' - -const HeaderFrame = styled.div` +/* import { Dots } from '../swap/styleds' +import Web3Status from '../Web3Status' +import NetworkCard from './NetworkCard' +import UniBalanceContent from './UniBalanceContent' */ + +export const HeaderFrame = styled.div<{ showBackground: boolean }>` display: grid; grid-template-columns: 1fr 120px; align-items: center; @@ -44,8 +39,7 @@ const HeaderFrame = styled.div` padding: 1rem; z-index: 2; ${({ theme }) => theme.mediaWidth.upToMedium` - grid-template-columns: 1fr; - width: calc(100%); + grid-template-columns: 1fr 1fr; position: relative; `}; @@ -64,17 +58,11 @@ export const HeaderControls = styled.div` flex-direction: row; justify-content: space-between; justify-self: center; - width: 100%; max-width: 960px; padding: 1rem; - position: fixed; - bottom: 0px; - left: 0px; - width: 100%; z-index: 99; height: 72px; border-radius: 12px 12px 0 0; - background-color: ${({ theme }) => theme.bg1}; `}; ` @@ -91,11 +79,18 @@ export const HeaderElement = styled.div` flex-direction: row-reverse; align-items: center; `}; + + ${({ theme }) => theme.mediaWidth.upToVerySmall` + width: 115px; + `}; ` export const HeaderElementWrap = styled.div` display: flex; align-items: center; + ${({ theme }) => theme.mediaWidth.upToSmall` + display: none; + `}; ` export const HeaderRow = styled(RowFixed)` @@ -107,9 +102,8 @@ export const HeaderRow = styled(RowFixed)` export const HeaderLinks = styled(Row)` justify-content: center; ${({ theme }) => theme.mediaWidth.upToMedium` - padding: 1rem 0 1rem 1rem; - justify-content: flex-end; -`}; + display: none; + `}; ` export const AccountElement = styled.div<{ active: boolean }>` @@ -158,19 +152,6 @@ export const HideSmall = styled.span` `}; ` -export const NetworkCard = styled(YellowCard)` - border-radius: 12px; - padding: 8px 12px; - ${({ theme }) => theme.mediaWidth.upToSmall` - margin: 0; - margin-right: 0.5rem; - width: initial; - overflow: hidden; - text-overflow: ellipsis; - flex-shrink: 1; - `}; -` - export const BalanceText = styled(Text)` ${({ theme }) => theme.mediaWidth.upToExtraSmall` display: none; @@ -182,7 +163,7 @@ export const Title = styled.a` align-items: center; pointer-events: auto; justify-self: flex-start; - margin-right: 12px; + // margin-right: 12px; ${({ theme }) => theme.mediaWidth.upToSmall` justify-self: center; `}; @@ -191,12 +172,12 @@ export const Title = styled.a` } ` -// export const UniIcon = styled.div` -// transition: transform 0.3s ease; -// :hover { -// transform: rotate(-5deg); -// } -// ` +/* export const UniIcon = styled.div` + transition: transform 0.3s ease; + :hover { + transform: rotate(-5deg); + } +` */ const activeClassName = 'ACTIVE' @@ -227,7 +208,7 @@ export const StyledNavLink = styled(NavLink).attrs({ } ` -export const StyledExternalLink = styled(ExternalLink).attrs({ +/* const StyledExternalLink = styled(ExternalLink).attrs({ activeClassName, })<{ isActive?: boolean }>` ${({ theme }) => theme.flexRowNoWrap} @@ -256,7 +237,7 @@ export const StyledExternalLink = styled(ExternalLink).attrs({ ${({ theme }) => theme.mediaWidth.upToExtraSmall` display: none; `} -` +` */ export const StyledMenuButton = styled.button` position: relative; @@ -287,46 +268,33 @@ export const StyledMenuButton = styled.button` } ` -/* -const NETWORK_LABELS: { [chainId in ChainId]?: string } = { - [ChainId.RINKEBY]: 'Rinkeby', - [ChainId.ROPSTEN]: 'Ropsten', - [ChainId.GOERLI]: 'Görli', - [ChainId.KOVAN]: 'Kovan' -} -*/ - export default function Header({ children }: PropsWithChildren) { - // const { account, chainId } = useActiveWeb3React() - // const { t } = useTranslation() + /* const { account, chainId } = useActiveWeb3React() - // const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? ''] - // // const [isDark] = useDarkModeManager() - // const [darkMode, toggleDarkMode] = useDarkModeManager() + const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? ''] + const [darkMode] = useDarkModeManager() - // const toggleClaimModal = useToggleSelfClaimModal() + const toggleClaimModal = useToggleSelfClaimModal() - // const availableClaim: boolean = useUserHasAvailableClaim(account) + const availableClaim: boolean = useUserHasAvailableClaim(account) - // const { claimTxn } = useUserHasSubmittedClaim(account ?? undefined) + const { claimTxn } = useUserHasSubmittedClaim(account ?? undefined) - // const aggregateBalance: TokenAmount | undefined = useAggregateUniBalance() + const [showUniBalanceModal, setShowUniBalanceModal] = useState(false) + const showClaimPopup = useShowClaimPopup() */ - // const [showUniBalanceModal, setShowUniBalanceModal] = useState(false) - // const showClaimPopup = useShowClaimPopup() + const scrollY = useScrollPosition() - // const countUpValue = aggregateBalance?.toFixed(0) ?? '0' - // const countUpValuePrevious = usePrevious(countUpValue) ?? '0' + // const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET] return ( - + 45}> {children} {/* setShowUniBalanceModal(false)}> - <UniIcon> <img width={'24px'} src={darkMode ? LogoDark : Logo} alt="logo" /> @@ -334,7 +302,7 @@ export default function Header({ children }: PropsWithChildren<void>) { - {t('swap')} + Swap ) { Boolean(match) || pathname.startsWith('/add') || pathname.startsWith('/remove') || - pathname.startsWith('/create') || + pathname.startsWith('/increase') || pathname.startsWith('/find') } > - {t('pool')} + Pool + + {chainId && chainId === SupportedChainId.MAINNET && ( + + Vote - - UNI - - - Vote - - - Charts - - - + )} + + Charts + + + + // - - {chainId && NETWORK_LABELS[chainId] && ( - {NETWORK_LABELS[chainId]} - )} - {availableClaim && !showClaimPopup && ( - {claimTxn && !claimTxn?.receipt ? Claiming UNI : 'Claim UNI'} + {claimTxn && !claimTxn?.receipt ? ( + + Claiming UNI + + ) : ( + Claim UNI + )} )} - {!availableClaim && aggregateBalance && ( - setShowUniBalanceModal(true)}> - - {account && ( - - - - - - )} - UNI - - - - )} - + {account && userEthBalance ? ( - - {userEthBalance?.toSignificant(4)} ETH + + {userEthBalance?.toSignificant(3)} ETH ) : null} - - - toggleDarkMode()}> - {darkMode ? : } - - + */} diff --git a/src/custom/components/Header/NetworkCard/NetworkCardMod.tsx b/src/custom/components/Header/NetworkCard/NetworkCardMod.tsx new file mode 100644 index 000000000..df6daff2e --- /dev/null +++ b/src/custom/components/Header/NetworkCard/NetworkCardMod.tsx @@ -0,0 +1,340 @@ +// import { Trans } from '@lingui/macro' +import { YellowCard } from 'components/Card' +import { useOnClickOutside } from 'hooks/useOnClickOutside' +import { useActiveWeb3React } from 'hooks/web3' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { /* ArrowDownCircle, */ AlertCircle, ChevronDown, ToggleLeft } from 'react-feather' +import { ApplicationModal } from 'state/application/actions' +import { useModalOpen, useToggleModal, useWalletModalToggle } from 'state/application/hooks' +import styled, { css } from 'styled-components/macro' +// import { ExternalLink } from 'theme' +import { switchToNetwork } from 'utils/switchToNetwork' +import { + CHAIN_INFO, + // L1_CHAIN_IDS, + /* L2_CHAIN_IDS, */ NETWORK_LABELS, + SupportedChainId, + // SupportedL2ChainId, + ALL_SUPPORTED_CHAIN_IDS, +} from 'constants/chains' +import { supportedChainId } from 'utils/supportedChainId' +import EthereumLogo from 'assets/images/ethereum-logo.png' +import QuestionHelper from 'components/QuestionHelper' +import { StyledPollingDot } from '@src/components/Header/Polling' +import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core' + +const BaseWrapper = css` + position: relative; + margin-right: 8px; + ${({ theme }) => theme.mediaWidth.upToMedium` + justify-self: end; + `}; + + ${({ theme }) => theme.mediaWidth.upToSmall` + margin: 0 0.5rem 0 0; + width: initial; + text-overflow: ellipsis; + flex-shrink: 1; + `}; +` +const L2Wrapper = styled.div` + ${BaseWrapper} +` +const BaseMenuItem = css` + align-items: center; + background-color: transparent; + border-radius: 12px; + color: ${({ theme }) => theme.text2}; + cursor: pointer; + display: flex; + flex: 1; + flex-direction: row; + font-size: 16px; + font-weight: 400; + justify-content: space-between; + :hover { + // color: ${({ theme }) => theme.text1}; + text-decoration: none; + } +` +/* const DisabledMenuItem = styled.div` + ${BaseMenuItem} + align-items: center; + background-color: ${({ theme }) => theme.bg2}; + cursor: auto; + display: flex; + font-size: 10px; + font-style: italic; + justify-content: center; + :hover, + :active, + :focus { + color: ${({ theme }) => theme.text2}; + } +` */ +const FallbackWrapper = styled(YellowCard)` + ${BaseWrapper} + width: auto; + border-radius: 12px; + padding: 8px 12px; + width: 100%; + user-select: none; +` +const Icon = styled.img` + width: 16px; + margin-right: 2px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + margin-right: 4px; + + `}; +` + +const MenuFlyout = styled.span` + background-color: ${({ theme }) => theme.bg1}; + border: 1px solid ${({ theme }) => theme.bg0}; + + box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), + 0px 24px 32px rgba(0, 0, 0, 0.01); + border-radius: 12px; + // padding: 1rem; + padding: 0.3rem; + display: flex; + flex-direction: column; + font-size: 1rem; + position: absolute; + left: 0rem; + top: 3rem; + z-index: 100; + // width: 237px; + min-width: 185px; + ${({ theme }) => theme.mediaWidth.upToMedium` + + bottom: unset; + top: 4.5em + right: 0; + + `}; + > { + padding: 12px; + } + // > :not(:first-child) { + // margin-top: 8px; + // } + // > :not(:last-child) { + // margin-bottom: 8px; + // } +` +/* const LinkOutCircle = styled(ArrowDownCircle)` + transform: rotate(230deg); + width: 16px; + height: 16px; + opacity: 0.6; +` +const MenuItem = styled(ExternalLink)` + ${BaseMenuItem} +` */ + +const NetworkName = styled.div<{ chainId?: SupportedChainId; hide?: boolean }>` + border-radius: 6px; + font-size: 16px; + font-weight: 500; + padding: 0 2px 0.5px 4px; + margin: 0 2px; + white-space: pre; + ${({ theme, hide }) => theme.mediaWidth.upToSmall` + display: ${hide ? 'none' : 'block'}; + `}; +` + +const ButtonMenuItem = styled.button<{ $disabled?: boolean; $selected?: boolean }>` + ${BaseMenuItem} + cursor: ${({ $disabled, $selected }) => ($disabled ? 'not-allowed' : $selected ? 'initial' : 'pointer')}; + border: none; + box-shadow: none; + // color: ${({ theme }) => theme.text2}; + color: ${({ theme, $selected }) => ($selected ? theme.text2 : theme.text1)}; + background-color: ${({ theme, $selected }) => $selected && theme.primary1}; + outline: none; + padding: 6px 10px; + + ${({ $selected }) => $selected && `margin: 3px 0;`} + + > ${NetworkName} { + margin: 0 auto 0 8px; + } + + &:hover { + color: ${({ theme, $selected, $disabled }) => (!$selected || !$disabled) && theme.text1}; + background: ${({ theme, $selected, $disabled }) => ($disabled ? 'transparent' : !$selected && theme.bg4)}; + } + + transition: background 0.13s ease-in-out; +` +export const NetworkInfo = styled.button<{ chainId: SupportedChainId }>` + align-items: center; + background-color: ${({ theme }) => theme.bg4}; + border-radius: 12px; + border: 1px solid ${({ theme }) => theme.bg0}; + color: ${({ theme }) => theme.text1}; + display: flex; + flex-direction: row; + font-weight: 500; + font-size: 12px; + height: 100%; + margin: 0; + height: 38px; + padding: 0.7rem; + + :hover, + :focus { + cursor: pointer; + outline: none; + border: 1px solid ${({ theme }) => theme.bg3}; + } +` + +export default function NetworkCard() { + const { account, chainId: preChainId, library } = useActiveWeb3React() + const { error } = useWeb3React() // MOD: check unsupported network + const node = useRef(null) + const open = useModalOpen(ApplicationModal.ARBITRUM_OPTIONS) + const toggle = useToggleModal(ApplicationModal.ARBITRUM_OPTIONS) + const toggleWalletModal = useWalletModalToggle() // MOD + useOnClickOutside(node, open ? toggle : undefined) + + const [implements3085, setImplements3085] = useState(false) + + // MOD: checks if a requested network switch was sent + // used for when user disconnected and selects a network internally + // if 3085 supported, will connect wallet and change network + const [queuedNetworkSwitch, setQueuedNetworkSwitch] = useState(null) + // MOD: get supported chain and check unsupported + const [chainId, isUnsupportedChain] = useMemo(() => { + const chainId = supportedChainId(preChainId) + + return [chainId, error instanceof UnsupportedChainIdError] // Mod - return if chainId is unsupported + }, [preChainId, error]) + + useEffect(() => { + // metamask is currently the only known implementer of this EIP + // here we proceed w/ a noop feature check to ensure the user's version of metamask supports network switching + // if not, we hide the UI + if (!library?.provider?.request || !chainId || !library?.provider?.isMetaMask) { + return setImplements3085(false) + } + switchToNetwork({ library, chainId }) + .then((x) => x ?? setImplements3085(true)) + .catch(() => setImplements3085(false)) + }, [chainId, library]) + + const networkCallback = useCallback( + (supportedChainId) => { + if (!account) { + toggleWalletModal() + return setQueuedNetworkSwitch(supportedChainId) + } else if (implements3085 && library && supportedChainId) { + return switchToNetwork({ library, chainId: supportedChainId }) + } + + return + }, + [account, implements3085, library, toggleWalletModal] + ) + + // MOD: used with mod hook - used to connect disconnected wallet to selected network + // if wallet supports 3085 + useEffect(() => { + if (queuedNetworkSwitch && account && chainId && implements3085) { + networkCallback(queuedNetworkSwitch) + setQueuedNetworkSwitch(null) + } + }, [networkCallback, queuedNetworkSwitch, chainId, account, implements3085]) + + const info = chainId ? CHAIN_INFO[chainId] : undefined + if (!chainId || !info || !library || isUnsupportedChain) { + return null + } + + // if (L1_CHAIN_IDS.includes(chainId)) { + if (ALL_SUPPORTED_CHAIN_IDS.includes(chainId)) { + // const info = CHAIN_INFO[chainId /* as SupportedL2ChainId */] + // const isArbitrum = [SupportedChainId.ARBITRUM_ONE, SupportedChainId.ARBITRUM_RINKEBY].includes(chainId) + return ( + + + + + {info.label} + + + + {open && ( + + {/* +
{isArbitrum ? {info.label} Bridge : Optimistic L2 Gateway}
+ +
+ + {isArbitrum ? {info.label} Explorer : Optimistic Etherscan} + + + +
+ Learn more +
+ +
*/} + {/* {implements3085 ? ( + switchToNetwork({ library, chainId: SupportedChainId.MAINNET })}> +
+ Switch to L1 (Mainnet) +
+ +
+ ) : ( + + Change your network to go back to L1 + + )} */} + {/* Supported networks to change to */} + {ALL_SUPPORTED_CHAIN_IDS.map((supportedChainId) => { + if (supportedChainId === chainId) { + /* Current selected network */ + return ( + + + {NETWORK_LABELS[chainId]} + + + ) + } + + const callback = () => networkCallback(supportedChainId) + return ( + + + {NETWORK_LABELS[supportedChainId]} + {implements3085 || !account ? ( + + ) : ( + <> + + + + )} + + ) + })} +
+ )} +
+ ) + } + + return {info.label} +} diff --git a/src/custom/components/Header/NetworkCard/index.ts b/src/custom/components/Header/NetworkCard/index.ts new file mode 100644 index 000000000..9fd197ff5 --- /dev/null +++ b/src/custom/components/Header/NetworkCard/index.ts @@ -0,0 +1,4 @@ +import NetworkCard from './NetworkCardMod' + +export * from './NetworkCardMod' +export default NetworkCard diff --git a/src/custom/components/Header/Polling/PollingMod.tsx b/src/custom/components/Header/Polling/PollingMod.tsx index 219d2892a..13eb7d2dd 100644 --- a/src/custom/components/Header/Polling/PollingMod.tsx +++ b/src/custom/components/Header/Polling/PollingMod.tsx @@ -1,10 +1,10 @@ -import React, { useState, useEffect } from 'react' -import styled, { keyframes } from 'styled-components' -import { TYPE, ExternalLink } from 'theme' +import { useEffect, useState } from 'react' +import styled, { keyframes } from 'styled-components/macro' +import { useActiveWeb3React } from 'hooks/web3' import { useBlockNumber } from 'state/application/hooks' import { getEtherscanLink } from 'utils' -import { useActiveWeb3React } from 'hooks/web3' +import { ExternalLink, TYPE } from 'theme' export const StyledPolling = styled.div` position: fixed; @@ -23,6 +23,13 @@ export const StyledPolling = styled.div` display: none; `} ` +const StyledPollingNumber = styled(TYPE.small)<{ breathe: boolean; hovering: boolean }>` + transition: opacity 0.25s ease; + opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.5)}; + :hover { + opacity: 1; + } +` export const StyledPollingDot = styled.div` width: 8px; height: 8px; @@ -67,16 +74,21 @@ export default function Polling() { const blockNumber = useBlockNumber() - const [isMounted, setIsMounted] = useState(true) + const [isMounting, setIsMounting] = useState(false) + const [isHover, setIsHover] = useState(false) useEffect( () => { - const timer1 = setTimeout(() => setIsMounted(true), 1000) + if (!blockNumber) { + return + } + + setIsMounting(true) + const mountingTimer = setTimeout(() => setIsMounting(false), 1000) // this will clear Timeout when component unmount like in willComponentUnmount return () => { - setIsMounted(false) - clearTimeout(timer1) + clearTimeout(mountingTimer) } }, [blockNumber] //useEffect will run only one time @@ -85,13 +97,11 @@ export default function Polling() { return ( - - - {blockNumber} - - {!isMounted && } + setIsHover(true)} onMouseLeave={() => setIsHover(false)}> + + {blockNumber}  + + {isMounting && } ) diff --git a/src/custom/components/Header/Polling/index.tsx b/src/custom/components/Header/Polling/index.tsx index ba89045fa..ff8958f75 100644 --- a/src/custom/components/Header/Polling/index.tsx +++ b/src/custom/components/Header/Polling/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import PollingUni, { StyledPolling, StyledPollingDot, Spinner } from './PollingMod' export * from './PollingMod' diff --git a/src/custom/components/Header/URLWarning/URLWarningMod.tsx b/src/custom/components/Header/URLWarning/URLWarningMod.tsx index d3b71094e..65092d1c6 100644 --- a/src/custom/components/Header/URLWarning/URLWarningMod.tsx +++ b/src/custom/components/Header/URLWarning/URLWarningMod.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { AlertTriangle, X } from 'react-feather' import { useURLWarningToggle, useURLWarningVisible } from 'state/user/hooks' diff --git a/src/custom/components/Header/URLWarning/index.tsx b/src/custom/components/Header/URLWarning/index.tsx index 714490bdd..355d365c9 100644 --- a/src/custom/components/Header/URLWarning/index.tsx +++ b/src/custom/components/Header/URLWarning/index.tsx @@ -1,6 +1,4 @@ -import React from 'react' -import styled from 'styled-components' - +import styled from 'styled-components/macro' import { PRODUCTION_URL } from 'constants/index' import { AlertTriangle } from 'react-feather' import URLWarningUni, { PhishAlert, StyledClose } from './URLWarningMod' diff --git a/src/custom/components/Header/index.tsx b/src/custom/components/Header/index.tsx index 2dc1b4359..6810486e4 100644 --- a/src/custom/components/Header/index.tsx +++ b/src/custom/components/Header/index.tsx @@ -1,25 +1,24 @@ -import React, { useState, useEffect } from 'react' +import { useState, useEffect } from 'react' import { SupportedChainId as ChainId } from 'constants/chains' import Web3Status from 'components/Web3Status' import { ExternalLink } from 'theme' import HeaderMod, { - NetworkCard as NetworkCardUni, Title, HeaderLinks, HeaderRow, HeaderControls as HeaderControlsUni, BalanceText as BalanceTextUni, HeaderElement, - HideSmall, AccountElement, HeaderElementWrap, StyledNavLink as StyledNavLinkUni, StyledMenuButton, + HeaderFrame, } from './HeaderMod' -import Menu from '../Menu' +import Menu from 'components/Menu' import { Moon, Sun } from 'react-feather' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { useActiveWeb3React } from 'hooks/web3' import { useETHBalances } from 'state/wallet/hooks' import { AMOUNT_PRECISION } from 'constants/index' @@ -27,15 +26,19 @@ import { useDarkModeManager } from 'state/user/hooks' import { darken } from 'polished' import TwitterImage from 'assets/cow-swap/twitter.svg' import OrdersPanel from 'components/OrdersPanel' +import { ApplicationModal } from 'state/application/actions' +import { useModalOpen } from 'state/application/hooks' import { supportedChainId } from 'utils/supportedChainId' import { formatSmart } from 'utils/format' +import NetworkCard, { NetworkInfo } from './NetworkCard' +import SVG from 'react-inlinesvg' export const NETWORK_LABELS: { [chainId in ChainId]?: string } = { [ChainId.RINKEBY]: 'Rinkeby', - [ChainId.ROPSTEN]: 'Ropsten', - [ChainId.GOERLI]: 'Görli', - [ChainId.KOVAN]: 'Kovan', + // [ChainId.ROPSTEN]: 'Ropsten', + // [ChainId.GOERLI]: 'Görli', + // [ChainId.KOVAN]: 'Kovan', [ChainId.XDAI]: 'xDAI', } @@ -53,8 +56,12 @@ const StyledNavLink = styled(StyledNavLinkUni)` transition: color 0.15s ease-in-out; color: ${({ theme }) => darken(0.3, theme.text1)}; - :hover, - :focus { + &:first-of-type { + margin: 0 12px 0 0; + } + + &:hover, + &:focus { color: ${({ theme }) => theme.text1}; } ` @@ -66,11 +73,47 @@ const BalanceText = styled(BalanceTextUni)` ` const HeaderControls = styled(HeaderControlsUni)` + justify-content: flex-end; + ${({ theme }) => theme.mediaWidth.upToMedium` max-width: 100%; + padding: 0; + height: auto; + width: 100%; `}; ` +export const Wrapper = styled.div` + width: 100%; + + ${HeaderFrame} { + padding: 12px 16px; + grid-template-columns: auto auto; + grid-gap: 16px; + + ${({ theme }) => theme.mediaWidth.upToExtraSmall` + padding: 10px 10px 0; + `} + } + + ${HeaderElement} { + ${({ theme }) => theme.mediaWidth.upToSmall` + width: 100%; + `}; + } + + ${NetworkInfo} { + height: 38px; + } + + ${StyledMenuButton} { + margin-left: 0.5rem; + padding: 0; + height: 38px; + width: 38px; + } +` + export const HeaderModWrapper = styled(HeaderMod)` ${Title} { margin: 0; @@ -83,40 +126,34 @@ export const HeaderModWrapper = styled(HeaderMod)` } ` -const NetworkCard = styled(NetworkCardUni)` - background-color: ${({ theme }) => theme.networkCard.background}; - color: ${({ theme }) => theme.networkCard.text}; - - ${({ theme }) => theme.mediaWidth.upToMedium` - margin: 0 0 0 8px; - `}; - - ${({ theme }) => theme.mediaWidth.upToSmall` - display: none; - `}; -` - -const TwitterLink = styled(StyledMenuButton)` - margin-left: 0.5rem; - padding: 0; - +export const TwitterLink = styled(StyledMenuButton)` > a { ${({ theme }) => theme.cursor}; - padding: 7px; + padding: 8px; display: flex; align-items: center; justify-content: center; - margin-bottom: -3px; - height: 35px; - width: 35px; + height: 100%; + width: 100%; } - > a > img { + > a > svg { width: 100%; height: 100%; object-fit: contain; border: 0; display: flex; + margin: 0; + padding: 0; + stroke: transparent; + } + + > a > svg > path { + fill: ${({ theme }) => theme.text1}; + } + + > a:hover > svg > path { + fill: ${({ theme }) => theme.primary1}; } ` @@ -127,6 +164,7 @@ export const LogoImage = styled.img.attrs((props) => ({ height: props.theme.logo.height, }))` object-fit: contain; + width: 100%; ${({ theme }) => theme.mediaWidth.upToSmall` width: 150px; @@ -135,10 +173,14 @@ export const LogoImage = styled.img.attrs((props) => ({ const UniIcon = styled.div` display: flex; - margin: 0 16px 0 0; position: relative; transition: transform 0.3s ease; + ${({ theme }) => theme.mediaWidth.upToVerySmall` + overflow-x: hidden; + width: 30px; + `}; + &:hover { transform: rotate(-5deg); } @@ -154,56 +196,57 @@ export default function Header() { const [isOrdersPanelOpen, setIsOrdersPanelOpen] = useState(false) const closeOrdersPanel = () => setIsOrdersPanelOpen(false) const openOrdersPanel = () => setIsOrdersPanelOpen(true) + const isMenuOpen = useModalOpen(ApplicationModal.MENU) - // Toggle the 'noScroll' class on body, whenever the orders panel is open. + // Toggle the 'noScroll' class on body, whenever the orders panel or flyout menu is open. // This removes the inner scrollbar on the page body, to prevent showing double scrollbars. useEffect(() => { - isOrdersPanelOpen ? document.body.classList.add('noScroll') : document.body.classList.remove('noScroll') - }, [isOrdersPanelOpen]) + isOrdersPanelOpen || isMenuOpen + ? document.body.classList.add('noScroll') + : document.body.classList.remove('noScroll') + }, [isOrdersPanelOpen, isMenuOpen]) return ( - - - - <UniIcon> - <LogoImage /> - </UniIcon> - - - Swap - Profile - About - - - - - - {chainId && NETWORK_LABELS[chainId] && ( - {NETWORK_LABELS[chainId]} - )} - - - {account && userEthBalance ? ( - - {formatSmart(userEthBalance, AMOUNT_PRECISION)} {nativeToken} - - ) : null} - - - - - - - Follow CowSwap on Twitter! - - - toggleDarkMode()}> - {darkMode ? : } - - - - - {isOrdersPanelOpen && } - + + + + + <UniIcon> + <LogoImage /> + </UniIcon> + + + Swap + Profile + About + + + + + + + {account && userEthBalance ? ( + + {formatSmart(userEthBalance, AMOUNT_PRECISION)} {nativeToken} + + ) : null} + + + + + + + + + + toggleDarkMode()}> + {darkMode ? : } + + + + + {isOrdersPanelOpen && } + + ) } diff --git a/src/custom/components/HighFeeWarning/index.tsx b/src/custom/components/HighFeeWarning/index.tsx index ba8dc1566..db1d56def 100644 --- a/src/custom/components/HighFeeWarning/index.tsx +++ b/src/custom/components/HighFeeWarning/index.tsx @@ -1,6 +1,6 @@ import React, { useContext, useMemo } from 'react' import { AlertTriangle } from 'react-feather' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { Fraction } from '@uniswap/sdk-core' import { MouseoverTooltipContent } from 'components/Tooltip' import { StyledInfo } from 'pages/Swap/styleds' diff --git a/src/custom/components/Link/index.tsx b/src/custom/components/Link/index.tsx index 42ddb2e63..ad705f1f9 100644 --- a/src/custom/components/Link/index.tsx +++ b/src/custom/components/Link/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { ExternalLink } from 'theme/index' import HashLink from 'components/HashLink' diff --git a/src/custom/components/Markdown/index.tsx b/src/custom/components/Markdown/index.tsx index 0a8adaaf4..977d0d8bd 100644 --- a/src/custom/components/Markdown/index.tsx +++ b/src/custom/components/Markdown/index.tsx @@ -1,10 +1,10 @@ -import React, { ReactNode } from 'react' +import { ReactNode } from 'react' import ReactMarkdownHtml from 'react-markdown/with-html' import ReactMarkdown, { ReactMarkdownPropsBase } from 'react-markdown' import useFetchFile from 'hooks/useFetchFile' import { HeadingRenderer } from './renderers' import Page, { Title, Content } from 'components/Page' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { WithClassName } from 'types' import { LinkScrollable, Link } from 'components/Link' diff --git a/src/custom/components/Markdown/renderers.tsx b/src/custom/components/Markdown/renderers.tsx index 98ac9fe32..26ab5be92 100644 --- a/src/custom/components/Markdown/renderers.tsx +++ b/src/custom/components/Markdown/renderers.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react' +import { ReactNode } from 'react' import visit from 'unist-util-visit' import { Node as MarkdownNode } from 'unist' diff --git a/src/custom/components/Menu/MenuMod.tsx b/src/custom/components/Menu/MenuMod.tsx index e347d0fdc..b457685d2 100644 --- a/src/custom/components/Menu/MenuMod.tsx +++ b/src/custom/components/Menu/MenuMod.tsx @@ -1,17 +1,22 @@ -import React, { useRef } from 'react' -// import { BookOpen, Code, Info, MessageCircle, PieChart } from 'react-feather' +import { t } from '@lingui/macro' +import { useRef } from 'react' +// import { BookOpen, Code, Info, MessageCircle, PieChart, Moon, Sun, Globe, ChevronLeft, Check } from 'react-feather' import { Link } from 'react-router-dom' -import styled, { css } from 'styled-components' +import styled, { css } from 'styled-components/macro' import { ReactComponent as MenuIcon } from 'assets/images/menu.svg' // import { useActiveWeb3React } from 'hooks/web3' import { useOnClickOutside } from 'hooks/useOnClickOutside' import { ApplicationModal } from 'state/application/actions' import { useModalOpen, useToggleModal } from 'state/application/hooks' // import { Trans } from '@lingui/macro' - import { ExternalLink } from 'theme' // import { ButtonPrimary } from 'components/Button' +/* import { useDarkModeManager } from 'state/user/hooks' +import { L2_CHAIN_IDS, CHAIN_INFO, SupportedChainId } from 'constants/chains' +import { LOCALE_LABEL, SupportedLocale, SUPPORTED_LOCALES } from 'constants/locales' +import { useLocationLinkProps } from 'hooks/useLocationLinkProps' +import { useActiveLocale } from 'hooks/useActiveLocale' */ import { WithClassName } from 'types' export enum FlyoutAlignment { @@ -93,7 +98,7 @@ export const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>` `}; ` -export const MenuItem = styled(ExternalLink)` +export const MenuItemBase = css` display: flex; flex: 1; flex-direction: row; @@ -110,6 +115,10 @@ export const MenuItem = styled(ExternalLink)` } ` +export const MenuItem = styled(ExternalLink)` + ${MenuItemBase} +` + export const InternalMenuItem = styled(Link)` flex: 1; padding: 0.5rem 0.5rem; @@ -125,65 +134,161 @@ export const InternalMenuItem = styled(Link)` } ` +/* const InternalLinkMenuItem = styled(InternalMenuItem)` + display: flex; + flex-direction: row; + align-items: center; + padding: 0.5rem 0.5rem; + justify-content: space-between; + text-decoration: none; + :hover { + color: ${({ theme }) => theme.text1}; + cursor: pointer; + text-decoration: none; + } +` + +const ToggleMenuItem = styled.button` + background-color: transparent; + margin: 0; + padding: 0; + border: none; + display: flex; + flex: 1; + flex-direction: row; + align-items: center; + padding: 0.5rem 0.5rem; + justify-content: space-between; + font-size: 1rem; + font-weight: 500; + color: ${({ theme }) => theme.text2}; + :hover { + color: ${({ theme }) => theme.text1}; + cursor: pointer; + text-decoration: none; + } +` */ + // const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface' +/* function LanguageMenuItem({ locale, active, key }: { locale: SupportedLocale; active: boolean; key: string }) { + const { to, onClick } = useLocationLinkProps(locale) + + if (!to) return null + + return ( + +
{LOCALE_LABEL[locale]}
+ {active && } +
+ ) +} */ + +/* function LanguageMenu({ close }: { close: () => void }) { + const activeLocale = useActiveLocale() + + return ( + + + + + {SUPPORTED_LOCALES.map((locale) => ( + + ))} + + ) +} */ + export default function Menu(props: { children?: React.ReactNode } & WithClassName) { - // const { account } = useActiveWeb3React() + // const { account, chainId } = useActiveWeb3React() const node = useRef() const open = useModalOpen(ApplicationModal.MENU) const toggle = useToggleModal(ApplicationModal.MENU) useOnClickOutside(node, open ? toggle : undefined) - // const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM) + /* const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM) + const showUNIClaimOption = Boolean(!!account && !!chainId && !L2_CHAIN_IDS.includes(chainId)) + const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET] + const [darkMode, toggleDarkMode] = useDarkModeManager() + + const [menu, setMenu] = useState<'main' | 'lang'>('main') + + useEffect(() => { + setMenu('main') + }, [open]) + */ return ( // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451 - + {open && - /* - - - -
- About -
-
- - -
- Docs -
-
- - -
- Code -
-
- - -
- Discord -
-
- - -
- Analytics -
-
- {account && ( - - Claim UNI - - )} -
- - */ + /* (() => { + switch (menu) { + case 'lang': + return setMenu('main')} /> + case 'main': + default: + return ( + + +
+ About +
+ +
+ +
+ Docs +
+ +
+ +
+ Code +
+ +
+ +
+ Discord +
+ +
+ +
+ Analytics +
+ +
+ setMenu('lang')}> +
+ Language +
+ +
+ toggleDarkMode()}> +
{darkMode ? Light Theme : Dark Theme}
+ {darkMode ? : } +
+ {showUNIClaimOption && ( + + Claim UNI + + )} +
+ ) + } + })()} */ props?.children}
) diff --git a/src/custom/components/Menu/index.tsx b/src/custom/components/Menu/index.tsx index dc674c127..9368f290f 100644 --- a/src/custom/components/Menu/index.tsx +++ b/src/custom/components/Menu/index.tsx @@ -1,12 +1,54 @@ -import React from 'react' -import { Code, MessageCircle, HelpCircle, BookOpen, PieChart } from 'react-feather' - -import MenuMod, { MenuItem, InternalMenuItem, MenuFlyout as MenuFlyoutUni } from './MenuMod' -import { useCloseModals } from 'state/application/hooks' -import styled from 'styled-components' -import { Separator as SeparatorBase } from 'components/swap/styleds' -import { CONTRACTS_CODE_LINK, DISCORD_LINK, DOCS_LINK, DUNE_DASHBOARD_LINK } from 'constants/index' +import { Code, HelpCircle, BookOpen, PieChart, Moon, Sun, Repeat, Star } from 'react-feather' + +import MenuMod, { + MenuItem, + InternalMenuItem, + MenuFlyout as MenuFlyoutUni, + MenuItemBase, + StyledMenuButton, +} from './MenuMod' +import { useToggleModal } from 'state/application/hooks' +import styled from 'styled-components/macro' +import { Separator as SeparatorBase } from 'components/SearchModal/styleds' +import { CONTRACTS_CODE_LINK, DISCORD_LINK, DOCS_LINK, DUNE_DASHBOARD_LINK, TWITTER_LINK } from 'constants/index' import GameIcon from 'assets/cow-swap/game.gif' +import { ApplicationModal } from 'state/application/actions' + +import TwitterImage from 'assets/cow-swap/twitter.svg' +import DiscordImage from 'assets/cow-swap/discord.svg' +import SVG from 'react-inlinesvg' + +export * from './MenuMod' + +const ResponsiveInternalMenuItem = styled(InternalMenuItem)` + display: none; + + ${({ theme }) => theme.mediaWidth.upToMedium` + display: flex; + `}; +` + +const MenuItemResponsiveBase = styled.div` + ${MenuItemBase} + display: none; + + ${({ theme }) => theme.mediaWidth.upToSmall` + display: flex; + `}; +` + +const MenuItemResponsive = styled(MenuItemResponsiveBase)` + font-weight: 500; + flex: 0 1 auto; + padding: 16px; + font-size: 18px; + svg { + width: 18px; + height: 18px; + object-fit: contain; + margin: 0 8px 0 0; + } +` export const StyledMenu = styled(MenuMod)` hr { @@ -14,20 +56,42 @@ export const StyledMenu = styled(MenuMod)` } ${MenuItem}, - ${InternalMenuItem} { + ${InternalMenuItem}, + ${MenuItemResponsive} { color: ${({ theme }) => theme.header.menuFlyout.color}; background: ${({ theme }) => theme.header.menuFlyout.background}; - :hover { + font-size: 16px; + width: 100%; + + ${({ theme }) => theme.mediaWidth.upToSmall` + padding: 16px; + `}; + + &:hover { color: ${({ theme }) => theme.header.menuFlyout.colorHover}; background: ${({ theme }) => theme.header.menuFlyout.colorHoverBg}; } } + ${InternalMenuItem} > a > svg { + width: 18px; + height: 18px; + stroke: transparent; + + > path { + fill: ${({ theme }) => theme.header.menuFlyout.color}; + } + } + span[aria-label='Play CowGame'] > img { - width: 20px; + width: 25px; height: 20px; object-fit: contain; - padding: 0 4px 0 0; + padding: 0 6px 0 0; + } + + ${StyledMenuButton} { + height: 38px; } ` @@ -39,31 +103,33 @@ const Policy = styled(InternalMenuItem).attrs((attrs) => ({ ` const MenuFlyout = styled(MenuFlyoutUni)` - min-width: 11rem; + min-width: 240px; box-shadow: 0 0 100vh 100vw rgb(0 0 0 / 25%); top: calc(100% + 16px); order: 1; - ${({ theme }) => theme.mediaWidth.upToMedium` - top: initial; - bottom: calc(100% + 32px); - `} - ${({ theme }) => theme.mediaWidth.upToSmall` top: 0; left: 0; position: fixed; - height: 100vh; - width: 100vw; + height: 100%; + width: 100%; border-radius: 0; box-shadow: none; padding: 0; overflow-y: auto; + flex-flow: row wrap; + padding: 0 0 56px; + align-items: flex-start; + align-content: flex-start; `}; + > a:not(${ResponsiveInternalMenuItem}) { + display: flex; + } + > a { transition: background 0.2s ease-in-out; - display: flex; align-items: center; text-decoration: none; @@ -71,27 +137,28 @@ const MenuFlyout = styled(MenuFlyoutUni)` flex: 0 1 auto; padding: 16px; font-size: 18px; - svg { - width: 18px; - height: 18px; - object-fit: contain; - margin: 0 8px 0 0; - } `}; > span { display: flex; align-items: center; } - - > span > svg { - margin: 0 8px 0 0; - } } > a:hover { background: ${({ theme }) => theme.disabled}; border-radius: 6px; } + + > a > span > svg { + width: 18px; + height: 18px; + object-fit: contain; + margin: 0 8px 0 0; + + > path { + fill: ${({ theme }) => theme.text1}; + } + } ` export const Separator = styled(SeparatorBase)` @@ -101,7 +168,7 @@ export const Separator = styled(SeparatorBase)` ${({ theme }) => theme.mediaWidth.upToMedium` width: 100%; - margin: 24px auto; + margin: 16px auto; `}; ` @@ -122,9 +189,11 @@ export const CloseMenu = styled.button` margin: 0 0 8px; ${({ theme }) => theme.mediaWidth.upToSmall` - height: 48px; + height: 56px; border-radius: 0; justify-content: flex-end; + margin: 0; + width: 100%; `}; &::after { @@ -140,13 +209,25 @@ export const CloseMenu = styled.button` } ` -export function Menu() { - const close = useCloseModals() +interface MenuProps { + darkMode: boolean + toggleDarkMode: () => void +} + +export function Menu({ darkMode, toggleDarkMode }: MenuProps) { + const close = useToggleModal(ApplicationModal.MENU) return ( + + Swap + + + About + + FAQ @@ -170,11 +251,17 @@ export function Menu() { + + + + Play CowGame @@ -182,6 +269,19 @@ export function Menu() { CowGame + toggleDarkMode()}> + {darkMode ? ( + <> + Dark Theme + + ) : ( + <> + + Light Theme + + )} + + diff --git a/src/custom/components/Modal/index.ts b/src/custom/components/Modal/index.ts index 6ea49415b..7e27badca 100644 --- a/src/custom/components/Modal/index.ts +++ b/src/custom/components/Modal/index.ts @@ -1,4 +1,4 @@ -import styled from 'styled-components' +import styled from 'styled-components/macro' import Modal from '@src/components/Modal' import { HeaderRow, ContentWrapper, CloseIcon, HoverText } from 'components/WalletModal/WalletModalMod' diff --git a/src/custom/components/NotificationBanner/index.tsx b/src/custom/components/NotificationBanner/index.tsx index a7f74a3bb..699d990bd 100644 --- a/src/custom/components/NotificationBanner/index.tsx +++ b/src/custom/components/NotificationBanner/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import styled from 'styled-components/macro' import { Colors } from 'theme/styled' import { X } from 'react-feather' diff --git a/src/custom/components/OrdersPanel/index.tsx b/src/custom/components/OrdersPanel/index.tsx index d7ecae59e..4d02c78dc 100644 --- a/src/custom/components/OrdersPanel/index.tsx +++ b/src/custom/components/OrdersPanel/index.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react' +import { useMemo } from 'react' import styled from 'styled-components/macro' import { ReactComponent as Close } from 'assets/images/x.svg' import AccountDetails from 'components/AccountDetails' diff --git a/src/custom/components/Page/index.tsx b/src/custom/components/Page/index.tsx index 220a6af3e..e205ba14d 100644 --- a/src/custom/components/Page/index.tsx +++ b/src/custom/components/Page/index.tsx @@ -1,6 +1,6 @@ -import React, { PropsWithChildren } from 'react' +import { PropsWithChildren } from 'react' -import styled, { css } from 'styled-components' +import styled, { css } from 'styled-components/macro' import AppBody from 'pages/AppBody' import { WithClassName } from 'types' diff --git a/src/custom/components/Pill/index.tsx b/src/custom/components/Pill/index.tsx index 645a684b6..fbafc38e0 100644 --- a/src/custom/components/Pill/index.tsx +++ b/src/custom/components/Pill/index.tsx @@ -1,4 +1,4 @@ -import styled from 'styled-components' +import styled from 'styled-components/macro' const Pill = styled.strong<{ color?: string; bgColor?: string; minWidth?: string }>` padding: 0.2rem 0.4rem; diff --git a/src/custom/components/Popover/PopoverMod.tsx b/src/custom/components/Popover/PopoverMod.tsx index 5b2c4a162..b615b8996 100644 --- a/src/custom/components/Popover/PopoverMod.tsx +++ b/src/custom/components/Popover/PopoverMod.tsx @@ -1,6 +1,6 @@ import { Placement } from '@popperjs/core' import { transparentize } from 'polished' -import React, { useCallback, useState } from 'react' +import { useCallback, useState } from 'react' import { usePopper } from 'react-popper' import styled, { DefaultTheme, StyledComponent } from 'styled-components/macro' import useInterval from 'hooks/useInterval' diff --git a/src/custom/components/Popover/index.tsx b/src/custom/components/Popover/index.tsx index 5eb70666c..bc56ec0ac 100644 --- a/src/custom/components/Popover/index.tsx +++ b/src/custom/components/Popover/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import PopoverMod, { Arrow as ArrowMod, PopoverContainer as PopoverContainerMod } from './PopoverMod' import styled from 'styled-components/macro' import { PopoverProps } from './PopoverMod' diff --git a/src/custom/components/Popups/ListUpdatePopup.tsx b/src/custom/components/Popups/ListUpdatePopup.tsx index c74bdbe8e..0834e2523 100644 --- a/src/custom/components/Popups/ListUpdatePopup.tsx +++ b/src/custom/components/Popups/ListUpdatePopup.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import { useCallback } from 'react' import ListUpdatePopupMod from './ListUpdatePopupMod' import { TokenList } from '@uniswap/token-lists' import { ListRowProps } from '../SearchModal/ManageLists' @@ -18,7 +18,7 @@ export interface ListUpdatePopupProps { export default function ListUpdatePopup(props: Omit) { const { chainId = DEFAULT_NETWORK_FOR_LISTS } = useActiveWeb3React() - const acceptListUpdateCustom = React.useCallback((url: string) => acceptListUpdate({ url, chainId }), [chainId]) + const acceptListUpdateCustom = useCallback((url: string) => acceptListUpdate({ url, chainId }), [chainId]) return } diff --git a/src/custom/components/Popups/ListUpdatePopupMod.tsx b/src/custom/components/Popups/ListUpdatePopupMod.tsx index 176ecc446..1cf5a47b4 100644 --- a/src/custom/components/Popups/ListUpdatePopupMod.tsx +++ b/src/custom/components/Popups/ListUpdatePopupMod.tsx @@ -3,7 +3,7 @@ import React, { useCallback, useMemo } from 'react' import ReactGA from 'react-ga' import { useDispatch } from 'react-redux' import { Text } from 'rebass' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { AppDispatch } from 'state' import { useRemovePopup } from 'state/application/hooks' // import { acceptListUpdate } from 'state/lists/actions' diff --git a/src/custom/components/Popups/PopupItem.tsx b/src/custom/components/Popups/PopupItem.tsx index 7724e9c4b..b8f4276a4 100644 --- a/src/custom/components/Popups/PopupItem.tsx +++ b/src/custom/components/Popups/PopupItem.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { PopupContent } from 'state/application/actions' import { default as PopupItemUni, Fader, StyledClose } from './PopupItemMod' diff --git a/src/custom/components/Popups/PopupItemMod.tsx b/src/custom/components/Popups/PopupItemMod.tsx index e091054ba..e554970e4 100644 --- a/src/custom/components/Popups/PopupItemMod.tsx +++ b/src/custom/components/Popups/PopupItemMod.tsx @@ -1,7 +1,7 @@ -import React, { useCallback, useContext, useEffect } from 'react' +import { useCallback, useContext, useEffect } from 'react' import { X } from 'react-feather' import { useSpring } from 'react-spring/web' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { animated } from 'react-spring' import { PopupContent } from 'state/application/actions' import { useRemovePopup } from 'state/application/hooks' diff --git a/src/custom/components/Popups/PopupsMod.tsx b/src/custom/components/Popups/PopupsMod.tsx index 41b125632..1a3d12be2 100644 --- a/src/custom/components/Popups/PopupsMod.tsx +++ b/src/custom/components/Popups/PopupsMod.tsx @@ -1,10 +1,12 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { useActivePopups } from 'state/application/hooks' import { AutoColumn } from 'components/Column' import PopupItem from 'components/Popups/PopupItem' // import ClaimPopup from './ClaimPopup' import { useURLWarningVisible } from 'state/user/hooks' +import { useActiveWeb3React } from 'hooks/web3' +import { SupportedChainId } from 'constants/chains' +import { MEDIA_WIDTHS } from '@src/theme' const MobilePopupWrapper = styled.div<{ height: string | number }>` position: relative; @@ -31,7 +33,11 @@ const MobilePopupInner = styled.div` } ` -const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>` +const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium + 1}px) and (max-width: ${ + MEDIA_WIDTHS.upToMedium + 500 +}px)` + +const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean; xlPadding: boolean }>` position: fixed; top: ${({ extraPadding }) => (extraPadding ? '108px' : '88px')}; right: 1rem; @@ -42,6 +48,10 @@ const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>` ${({ theme }) => theme.mediaWidth.upToSmall` display: none; `}; + + ${StopOverflowQuery} { + top: ${({ extraPadding, xlPadding }) => (xlPadding ? '64px' : extraPadding ? '64px' : '56px')}; + } ` export default function Popups() { @@ -50,9 +60,13 @@ export default function Popups() { const urlWarningActive = useURLWarningVisible() + // need extra padding if network is not L1 Ethereum + const { chainId } = useActiveWeb3React() + const isNotOnMainnet = Boolean(chainId && chainId !== SupportedChainId.MAINNET) + return ( <> - + {/* */} {activePopups.map((item) => ( diff --git a/src/custom/components/Popups/TransactionPopupMod.tsx b/src/custom/components/Popups/TransactionPopupMod.tsx index 1d7e396f4..bbea684e4 100644 --- a/src/custom/components/Popups/TransactionPopupMod.tsx +++ b/src/custom/components/Popups/TransactionPopupMod.tsx @@ -1,6 +1,6 @@ -import React, { useContext } from 'react' +import { useContext } from 'react' import { AlertCircle, CheckCircle } from 'react-feather' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { useActiveWeb3React } from 'hooks/web3' import { TYPE } from 'theme' // import { ExternalLink } from 'theme' @@ -40,7 +40,9 @@ export default function TransactionPopup({ summary )} {chainId && ( - // View on Etherscan + /* + View on Explorer + */ )} diff --git a/src/custom/components/ProgressSteps/ProgressStepsMod.tsx b/src/custom/components/ProgressSteps/ProgressStepsMod.tsx deleted file mode 100644 index ad427bb2a..000000000 --- a/src/custom/components/ProgressSteps/ProgressStepsMod.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React, { useContext } from 'react' -import styled from 'styled-components' -import { AutoColumn } from 'components/Column' -import { ThemeContext } from 'styled-components' -import { TYPE } from '../../theme' - -const Wrapper = styled(AutoColumn)` - margin-right: 8px; - height: 100%; -` - -const Grouping = styled(AutoColumn)` - width: fit-content; - padding: 4px; - /* background-color: ${({ theme }) => theme.bg2}; */ - border-radius: 16px; -` - -export const Circle = styled.div<{ confirmed?: boolean; disabled?: boolean }>` - width: 48px; - height: 48px; - background-color: ${({ theme, confirmed, disabled }) => - disabled ? theme.bg4 : confirmed ? theme.green1 : theme.primary1}; - border-radius: 50%; - color: ${({ theme }) => theme.white}; - display: flex; - align-items: center; - justify-content: center; - line-height: 8px; - font-size: 16px; - padding: 1rem; -` - -const CircleRow = styled.div` - display: flex; - flex-direction: column; - align-items: center; -` - -export interface ProgressCirclesProps { - steps: boolean[] - disabled?: boolean - CircleComponent: typeof Circle -} - -/** - * Based on array of steps, create a step counter of circles. - * A circle can be enabled, disabled, or confirmed. States are derived - * from previous step. - * - * An extra circle is added to represent the ability to swap, add, or remove. - * This step will never be marked as complete (because no 'txn done' state in body ui). - * - * @param steps array of booleans where true means step is complete - */ -export default function ProgressCircles({ steps, disabled = false, CircleComponent, ...rest }: ProgressCirclesProps) { - const theme = useContext(ThemeContext) - - return ( - - - {steps.map((step, i) => { - return ( - - - {step ? '✓' : i + 1 + '.'} - - | - - ) - })} - {steps.length + 1 + '.'} - - - ) -} diff --git a/src/custom/components/ProgressSteps/index.tsx b/src/custom/components/ProgressSteps/index.tsx deleted file mode 100644 index 9303daa25..000000000 --- a/src/custom/components/ProgressSteps/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' -import styled from 'styled-components' - -import ProgressCirclesUni, { Circle as CircleUni, ProgressCirclesProps } from './ProgressStepsMod' - -export const Circle = styled(CircleUni)` - color: ${({ theme, confirmed, disabled }) => (disabled ? theme.text1 : confirmed ? theme.white : theme.text1)}; -` - -export default function ProgressCircles(props: Omit) { - return -} diff --git a/src/custom/components/QuestionHelper/QuestionHelperMod.tsx b/src/custom/components/QuestionHelper/QuestionHelperMod.tsx index 819346b33..f891edc4b 100644 --- a/src/custom/components/QuestionHelper/QuestionHelperMod.tsx +++ b/src/custom/components/QuestionHelper/QuestionHelperMod.tsx @@ -1,4 +1,4 @@ -import React, { /* ReactNode, */ useCallback, useState } from 'react' +import { /* ReactNode, */ useCallback, useState } from 'react' import styled from 'styled-components/macro' import Tooltip from 'components/Tooltip' import { TooltipProps } from '../Tooltip/TooltipMod' diff --git a/src/custom/components/QuestionHelper/index.tsx b/src/custom/components/QuestionHelper/index.tsx index 2accb1691..eae6ba94d 100644 --- a/src/custom/components/QuestionHelper/index.tsx +++ b/src/custom/components/QuestionHelper/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import styled from 'styled-components/macro' import SVG from 'react-inlinesvg' import QuestionImage from 'assets/svg/question.svg' diff --git a/src/custom/components/ScrollToTop/index.tsx b/src/custom/components/ScrollToTop/index.tsx index 9e2759e2a..27c5c56bb 100644 --- a/src/custom/components/ScrollToTop/index.tsx +++ b/src/custom/components/ScrollToTop/index.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useState } from 'react' -import styled from 'styled-components' +import { useEffect, useState } from 'react' +import styled from 'styled-components/macro' import { ArrowUpCircle } from 'react-feather' import useDebouncedChangeHandler from 'hooks/useDebouncedChangeHandler' diff --git a/src/custom/components/SearchModal/CommonBases/CommonBasesMod.tsx b/src/custom/components/SearchModal/CommonBases/CommonBasesMod.tsx index c3613ba44..94efd0384 100644 --- a/src/custom/components/SearchModal/CommonBases/CommonBasesMod.tsx +++ b/src/custom/components/SearchModal/CommonBases/CommonBasesMod.tsx @@ -1,16 +1,14 @@ import { Trans } from '@lingui/macro' -import React from 'react' import { Text } from 'rebass' import { Currency } from '@uniswap/sdk-core' import styled from 'styled-components/macro' import { COMMON_BASES } from 'constants/routing' import { currencyId } from 'utils/currencyId' -// import { AutoColumn } from 'components/Column' import QuestionHelper from 'components/QuestionHelper' import { AutoRow } from 'components/Row' import CurrencyLogo from 'components/CurrencyLogo' -import { BaseWrapper, CommonBasesRow, CommonBasesProps, AutoColumn } from '.' // mod +import { BaseWrapper, CommonBasesRow, CommonBasesProps, MobileWrapper } from '.' // mod export const BaseWrapperMod = styled.div<{ disable?: boolean }>` // mod @@ -39,7 +37,7 @@ export default function CommonBases({ chainId, onSelect, selectedCurrency }: Com const bases = typeof chainId !== 'undefined' ? COMMON_BASES[chainId] ?? [] : [] return bases.length > 0 ? ( - + {/* Common bases */} @@ -64,6 +62,6 @@ export default function CommonBases({ chainId, onSelect, selectedCurrency }: Com ) })} - + ) : null } diff --git a/src/custom/components/SearchModal/CommonBases/index.ts b/src/custom/components/SearchModal/CommonBases/index.ts index 145888100..f76a053b3 100644 --- a/src/custom/components/SearchModal/CommonBases/index.ts +++ b/src/custom/components/SearchModal/CommonBases/index.ts @@ -28,6 +28,12 @@ export const AutoColumn = styled(AutoColumnUni)` `} ` +export const MobileWrapper = styled(AutoColumn)` + ${({ theme }) => theme.mediaWidth.upToSmall` + isplay: none; + `}; +` + export const BaseWrapper = styled(BaseWrapperMod)<{ disable?: boolean }>` color: ${({ theme, disable }) => disable && transparentize(0.7, theme.text1)}; filter: ${({ disable }) => disable && 'contrast(0.85)'}; diff --git a/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx b/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx index 23e38df1b..fedaf85b9 100644 --- a/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx +++ b/src/custom/components/SearchModal/CurrencyList/CurrencyListMod.tsx @@ -1,6 +1,6 @@ import { Trans } from '@lingui/macro' import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' -import React, { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react' +import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react' import { FixedSizeList } from 'react-window' import { Text } from 'rebass' import styled from 'styled-components/macro' @@ -112,6 +112,7 @@ function CurrencyRow({ isSelected, otherSelected, style, + showCurrencyAmount, isUnsupported, TokenTagsComponent = TokenTags, // gp-swap added BalanceComponent = Balance, // gp-swap added @@ -121,6 +122,7 @@ function CurrencyRow({ isSelected: boolean otherSelected: boolean style: CSSProperties + showCurrencyAmount?: boolean BalanceComponent?: (params: { balance: CurrencyAmount }) => JSX.Element // gp-swap added TokenTagsComponent?: (params: { currency: Currency; isUnsupported: boolean }) => JSX.Element // gp-swap added isUnsupported: boolean // gp-added @@ -156,10 +158,11 @@ function CurrencyRow({ {/* */} - - {/* {balance ? : account ? : null} */} - {balance ? : account ? : null} - + {showCurrencyAmount && ( + + {balance ? : account ? : null} + + )} ) } @@ -174,7 +177,7 @@ function BreakLineComponent({ style }: { style: CSSProperties }) { const theme = useTheme() return ( - + @@ -205,6 +208,7 @@ export default function CurrencyList({ fixedListRef, showImportView, setImportToken, + showCurrencyAmount, BalanceComponent = Balance, // gp-swap added TokenTagsComponent = TokenTags, // gp-swap added }: { @@ -217,6 +221,8 @@ export default function CurrencyList({ fixedListRef?: MutableRefObject showImportView: () => void setImportToken: (token: Token) => void + showCurrencyAmount?: boolean + disableNonToken?: boolean BalanceComponent?: (params: { balance: CurrencyAmount }) => JSX.Element // gp-swap added TokenTagsComponent?: (params: { currency: Currency; isUnsupported: boolean }) => JSX.Element // gp-swap added }) { @@ -264,6 +270,7 @@ export default function CurrencyList({ BalanceComponent={BalanceComponent} // gp-swap added TokenTagsComponent={TokenTagsComponent} // gp-swap added isUnsupported={isUnsupported} + showCurrencyAmount={showCurrencyAmount} /> ) } else { @@ -276,6 +283,7 @@ export default function CurrencyList({ otherCurrency, selectedCurrency, setImportToken, + showCurrencyAmount, showImportView, checkIsUnsupported, BalanceComponent, diff --git a/src/custom/components/SearchModal/CurrencyList/index.tsx b/src/custom/components/SearchModal/CurrencyList/index.tsx index 9c9ee1ace..79f1470eb 100644 --- a/src/custom/components/SearchModal/CurrencyList/index.tsx +++ b/src/custom/components/SearchModal/CurrencyList/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { LONG_PRECISION, UNSUPPORTED_TOKENS_FAQ_URL } from 'constants/index' import CurrencyListMod, { StyledBalanceText, Tag as TagMod, TagContainer } from './CurrencyListMod' @@ -9,7 +8,7 @@ import { RowFixed } from 'components/Row' import { LightGreyCard } from 'components/Card' import { HashLink } from 'react-router-hash-link' import { t } from '@lingui/macro' -import { TagInfo, WrappedTokenInfo } from 'state/lists/wrappedTokenInfo' +import { TagInfo /* , WrappedTokenInfo */ } from 'state/lists/wrappedTokenInfo' import { formatSmart } from 'utils/format' import Column from 'components/Column' import { MenuItem as MenuItemMod } from '@src/components/SearchModal/styleds' @@ -103,7 +102,7 @@ function TagDescriptor({ tags, bg, children }: { children?: React.ReactNode; tag ) } -function TokenTags({ currency, isUnsupported }: { currency: Currency; isUnsupported: boolean }) { +function TokenTags({ /* currency, */ isUnsupported }: { /* currency: Currency; */ isUnsupported: boolean }) { if (isUnsupported) { return ( @@ -116,14 +115,16 @@ function TokenTags({ currency, isUnsupported }: { currency: Currency; isUnsuppor ) } - if (!(currency instanceof WrappedTokenInfo)) { + return // MOD: return only UnsupportedToken tags + + /* if (!(currency instanceof WrappedTokenInfo)) { return } const tags = currency.tags if (!tags || tags.length === 0) return - return + return */ } export function Balance({ balance }: { balance: CurrencyAmount }) { diff --git a/src/custom/components/SearchModal/CurrencySearch/CurrencySearchMod.tsx b/src/custom/components/SearchModal/CurrencySearch/CurrencySearchMod.tsx index 0d81deafd..c3442712a 100644 --- a/src/custom/components/SearchModal/CurrencySearch/CurrencySearchMod.tsx +++ b/src/custom/components/SearchModal/CurrencySearch/CurrencySearchMod.tsx @@ -1,5 +1,5 @@ import { Currency, Token } from '@uniswap/sdk-core' -import React, { KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react' import ReactGA from 'react-ga' import { t, Trans } from '@lingui/macro' import { FixedSizeList } from 'react-window' @@ -51,6 +51,8 @@ export interface CurrencySearchProps { showCommonBases?: boolean showManageView: () => void showImportView: () => void + showCurrencyAmount?: boolean + disableNonToken?: boolean setImportToken: (token: Token) => void FooterButtonTextComponent: (props: { theme: DefaultTheme }) => JSX.Element // MOD } @@ -64,6 +66,7 @@ export function CurrencySearch({ isOpen, showManageView, showImportView, + showCurrencyAmount, setImportToken, FooterButtonTextComponent, // MOD }: CurrencySearchProps) { @@ -214,6 +217,7 @@ export function CurrencySearch({ fixedListRef={fixedList} showImportView={showImportView} setImportToken={setImportToken} + showCurrencyAmount={showCurrencyAmount} /> )} diff --git a/src/custom/components/SearchModal/CurrencySearch/index.tsx b/src/custom/components/SearchModal/CurrencySearch/index.tsx index 28d136388..2563669a1 100644 --- a/src/custom/components/SearchModal/CurrencySearch/index.tsx +++ b/src/custom/components/SearchModal/CurrencySearch/index.tsx @@ -1,11 +1,10 @@ -import React from 'react' import { Trans } from '@lingui/macro' import { RowFixed } from 'components/Row' import { IconWrapper, TYPE } from 'theme' import styled from 'styled-components/macro' import { Edit } from 'react-feather' import { CurrencySearch as CurrencySearchMod, CurrencySearchProps } from './CurrencySearchMod' -import { DefaultTheme } from 'styled-components' +import { DefaultTheme } from 'styled-components/macro' import { SearchInput, Separator } from '@src/components/SearchModal/styleds' import { transparentize } from 'polished' import Column from '@src/components/Column' diff --git a/src/custom/components/SearchModal/ImportList/ImportListMod.tsx b/src/custom/components/SearchModal/ImportList/ImportListMod.tsx index 4d4e64684..b5d65c932 100644 --- a/src/custom/components/SearchModal/ImportList/ImportListMod.tsx +++ b/src/custom/components/SearchModal/ImportList/ImportListMod.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from 'react' +import { useState, useCallback } from 'react' import styled from 'styled-components/macro' import ReactGA from 'react-ga' import { TYPE, CloseIcon } from 'theme' @@ -15,7 +15,7 @@ import { ExternalLink } from 'theme' import ListLogo from 'components/ListLogo' import { PaddedColumn, Checkbox, TextDot } from 'components/SearchModal/styleds' // import { TokenList } from '@uniswap/token-lists' -// import { useDispatch } from 'react-redux' + // import { AppDispatch } from 'state' import { useFetchListCallback } from 'hooks/useFetchListCallback' // import { removeList, enableList } from 'state/lists/actions' @@ -104,7 +104,7 @@ export function ImportList({ listURL, list, setModalView, onDismiss, enableList, - {list.tokens.length} tokens + {list.tokens.length} tokens @@ -152,7 +152,7 @@ export function ImportList({ listURL, list, setModalView, onDismiss, enableList, diff --git a/src/custom/components/SearchModal/ImportList/index.tsx b/src/custom/components/SearchModal/ImportList/index.tsx index 9f7ebb9fe..8337dc67f 100644 --- a/src/custom/components/SearchModal/ImportList/index.tsx +++ b/src/custom/components/SearchModal/ImportList/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react' +import { useCallback } from 'react' import { ImportList as ImportListMod } from './ImportListMod' import { enableList as enableListMod, removeList as removeListMod } from 'state/lists/actions' diff --git a/src/custom/components/SearchModal/ImportRow/ImportRowMod.tsx b/src/custom/components/SearchModal/ImportRow/ImportRowMod.tsx index 17ee0a4d1..06d038709 100644 --- a/src/custom/components/SearchModal/ImportRow/ImportRowMod.tsx +++ b/src/custom/components/SearchModal/ImportRow/ImportRowMod.tsx @@ -1,4 +1,4 @@ -import React, { CSSProperties } from 'react' +import { CSSProperties } from 'react' import { Token } from '@uniswap/sdk-core' import { AutoRow, RowFixed } from 'components/Row' import { AutoColumn } from 'components/Column' diff --git a/src/custom/components/SearchModal/ImportRow/index.tsx b/src/custom/components/SearchModal/ImportRow/index.tsx index 0d2e04de6..89c6021ff 100644 --- a/src/custom/components/SearchModal/ImportRow/index.tsx +++ b/src/custom/components/SearchModal/ImportRow/index.tsx @@ -1,4 +1,4 @@ -import React, { CSSProperties } from 'react' +import { CSSProperties } from 'react' import { Token } from '@uniswap/sdk-core' import styled from 'styled-components/macro' import ImportRowMod, { TokenSection } from './ImportRowMod' diff --git a/src/custom/components/SearchModal/ImportToken/ImportTokenMod.tsx b/src/custom/components/SearchModal/ImportToken/ImportTokenMod.tsx index bd816ea1c..dde4f7d44 100644 --- a/src/custom/components/SearchModal/ImportToken/ImportTokenMod.tsx +++ b/src/custom/components/SearchModal/ImportToken/ImportTokenMod.tsx @@ -1,5 +1,5 @@ import { TokenList } from '@uniswap/token-lists/dist/types' -import React from 'react' + import { Token, Currency } from '@uniswap/sdk-core' import styled from 'styled-components/macro' import { TYPE, CloseIcon } from 'theme' @@ -122,7 +122,7 @@ export function ImportToken({ ) : ( - + @@ -137,7 +137,7 @@ export function ImportToken({ { tokens.map((token) => addToken(token)) diff --git a/src/custom/components/SearchModal/ImportToken/index.tsx b/src/custom/components/SearchModal/ImportToken/index.tsx index 92aa9feea..e08445368 100644 --- a/src/custom/components/SearchModal/ImportToken/index.tsx +++ b/src/custom/components/SearchModal/ImportToken/index.tsx @@ -1,8 +1,7 @@ -import React from 'react' import { Token } from '@uniswap/sdk-core' import { Trans } from '@lingui/macro' import styled from 'styled-components/macro' -import { DefaultTheme } from 'styled-components' +import { DefaultTheme } from 'styled-components/macro' import { AlertCircle } from 'react-feather' import { AddressText, ImportProps, ImportToken as ImportTokenMod, WarningWrapper } from './ImportTokenMod' import Card from 'components/Card' @@ -75,7 +74,7 @@ function CardComponent({ theme, key, token, chainId, list }: CardComponentProps) ) : ( - + diff --git a/src/custom/components/SearchModal/Manage/ManageMod.tsx b/src/custom/components/SearchModal/Manage/ManageMod.tsx index 751752787..a07c02b63 100644 --- a/src/custom/components/SearchModal/Manage/ManageMod.tsx +++ b/src/custom/components/SearchModal/Manage/ManageMod.tsx @@ -1,5 +1,5 @@ import { Trans } from '@lingui/macro' -import React, { useState } from 'react' +import { useState } from 'react' import { PaddedColumn, Separator } from '@src/components/SearchModal/styleds' import { RowBetween } from 'components/Row' import { ArrowLeft } from 'react-feather' diff --git a/src/custom/components/SearchModal/Manage/index.tsx b/src/custom/components/SearchModal/Manage/index.tsx index ad44d0669..35ede3d2c 100644 --- a/src/custom/components/SearchModal/Manage/index.tsx +++ b/src/custom/components/SearchModal/Manage/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import ManageMod from './ManageMod' import styled from 'styled-components/macro' import { Token } from '@uniswap/sdk-core' @@ -12,6 +11,7 @@ export const Wrapper = styled.div` width: 100%; position: relative; padding-bottom: 80px; + overflow-y: hidden; ${SearchInput} { border: none; diff --git a/src/custom/components/SearchModal/ManageLists/ManageListsMod.tsx b/src/custom/components/SearchModal/ManageLists/ManageListsMod.tsx index 262a00d70..f2ce9f2ff 100644 --- a/src/custom/components/SearchModal/ManageLists/ManageListsMod.tsx +++ b/src/custom/components/SearchModal/ManageLists/ManageListsMod.tsx @@ -1,36 +1,34 @@ -import React, { memo, useCallback, useMemo, useRef, useState, useEffect } from 'react' -import { Settings, CheckCircle } from 'react-feather' +import { t, Trans } from '@lingui/macro' +import { TokenList } from '@uniswap/token-lists' +// import Card from 'components/Card' +// import { UNSUPPORTED_LIST_URLS } from '@src/constants/lists' +import { useListColor } from 'hooks/useColor' +import { useActiveWeb3React } from 'hooks/web3' +import { memo, useCallback, useMemo, useRef, useState, useEffect } from 'react' +import { CheckCircle, Settings } from 'react-feather' import ReactGA from 'react-ga' import { useAppDispatch, useAppSelector } from 'state/hooks' import { usePopper } from 'react-popper' import styled from 'styled-components/macro' import { useFetchListCallback } from 'hooks/useFetchListCallback' import { useOnClickOutside } from 'hooks/useOnClickOutside' -import { TokenList } from '@uniswap/token-lists' -import { t, Trans } from '@lingui/macro' - +import useTheme from 'hooks/useTheme' import useToggle from 'hooks/useToggle' -// import { acceptListUpdate, removeList, disableList, enableList } from '@src/state/lists/actions' -import { useIsListActive, useAllLists, useActiveListUrls } from 'state/lists/hooks' -import { ExternalLink, LinkStyledButton, TYPE, IconWrapper } from 'theme' +// import { acceptListUpdate, disableList, enableList, removeList } from 'state/lists/actions' +import { useActiveListUrls, useIsListActive, useAllLists } from 'state/lists/hooks' +import { ExternalLink, IconWrapper, LinkStyledButton, TYPE } from 'theme' import listVersionLabel from 'utils/listVersionLabel' import { parseENSAddress } from 'utils/parseENSAddress' import uriToHttp from 'utils/uriToHttp' import { ButtonEmpty, ButtonPrimary } from 'components/Button' - import Column, { AutoColumn } from 'components/Column' import ListLogo from 'components/ListLogo' import Row, { RowFixed, RowBetween } from 'components/Row' -import { PaddedColumn, SearchInput, Separator, SeparatorDark } from 'components/SearchModal/styleds' -import { useListColor } from 'hooks/useColor' -import useTheme from 'hooks/useTheme' import ListToggle from 'components/Toggle/ListToggle' -// import Card from 'components/Card' import { CurrencyModalView } from 'components/SearchModal/CurrencySearchModal' -// import { UNSUPPORTED_LIST_URLS } from '@src/constants/lists' +import { PaddedColumn, SearchInput, Separator, SeparatorDark } from 'components/SearchModal/styleds' // Mod: import { ListRowProps, RowWrapper, Card } from '.' // mod -import { useActiveWeb3React } from 'hooks/web3' import { DEFAULT_NETWORK_FOR_LISTS } from 'constants/lists' import { supportedChainId } from 'utils/supportedChainId' @@ -85,8 +83,9 @@ const StyledListUrlText = styled(TYPE.main)<{ active: boolean }>` color: ${({ theme, active }) => (active ? theme.white : theme.text2)}; ` -/* const RowWrapper = styled(Row)<{ bgColor: string; active: boolean }>` +/* const RowWrapper = styled(Row)<{ bgColor: string; active: boolean; hasActiveTokens: boolean }>` background-color: ${({ bgColor, active, theme }) => (active ? bgColor ?? 'transparent' : theme.bg2)}; + opacity: ${({ hasActiveTokens }) => (hasActiveTokens ? 1 : 0.4)}; transition: 200ms; align-items: center; padding: 1rem; @@ -113,6 +112,13 @@ ListRowProps & { listUrl: string }) { const dispatch = useAppDispatch() const { current: list, pendingUpdate: pending } = listsByUrl[listUrl] + const activeTokensOnThisChain = useMemo(() => { + if (!list || !chainId) { + return 0 + } + return list.tokens.reduce((acc, cur) => (cur.chainId === chainId ? acc + 1 : acc), 0) + }, [chainId, list]) + const theme = useTheme() const listColor = useListColor(list?.logoURI) const isActive = useIsListActive(listUrl) @@ -147,7 +153,7 @@ ListRowProps & { listUrl: string }) { action: 'Start Remove List', label: listUrl, }) - if (window.prompt(`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) { + if (window.prompt(t`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) { ReactGA.event({ category: 'Lists', action: 'Confirm Remove List', @@ -181,7 +187,13 @@ ListRowProps & { listUrl: string }) { if (!list) return null return ( - + 0} + bgColor={listColor} + key={listUrl} + id={listUrlRowHTMLId(listUrl)} + > {list.logoURI ? ( ) : ( @@ -250,20 +262,29 @@ export function ManageLists({ unsupportedListUrls: string[] listRowProps: ListRowProps }) { + const { chainId } = useActiveWeb3React() const theme = useTheme() const [listUrlInput, setListUrlInput] = useState('') const lists = useAllLists() + const tokenCountByListName = useMemo>( + () => + Object.values(lists).reduce((acc, { current: list }) => { + if (!list) { + return acc + } + return { + ...acc, + [list.name]: list.tokens.reduce((count: number, token) => (token.chainId === chainId ? count + 1 : count), 0), + } + }, {}), + [chainId, lists] + ) + // sort by active but only if not visible const activeListUrls = useActiveListUrls() - const [activeCopy, setActiveCopy] = useState() - useEffect(() => { - if (!activeCopy && activeListUrls) { - setActiveCopy(activeListUrls) - } - }, [activeCopy, activeListUrls]) const handleInput = useCallback((e) => { setListUrlInput(e.target.value) @@ -284,32 +305,38 @@ export function ManageLists({ // return Boolean(lists[listUrl].current) && !Boolean(UNSUPPORTED_LIST_URLS.includes(listUrl)) return Boolean(lists[listUrl].current) && !Boolean(unsupportedListUrls.includes(listUrl)) }) - .sort((u1, u2) => { - const { current: l1 } = lists[u1] - const { current: l2 } = lists[u2] + .sort((listUrlA, listUrlB) => { + const { current: listA } = lists[listUrlA] + const { current: listB } = lists[listUrlB] // first filter on active lists - if (activeCopy?.includes(u1) && !activeCopy?.includes(u2)) { + if (activeListUrls?.includes(listUrlA) && !activeListUrls?.includes(listUrlB)) { return -1 } - if (!activeCopy?.includes(u1) && activeCopy?.includes(u2)) { + if (!activeListUrls?.includes(listUrlA) && activeListUrls?.includes(listUrlB)) { return 1 } - if (l1 && l2) { - return l1.name.toLowerCase() < l2.name.toLowerCase() + if (listA && listB) { + if (tokenCountByListName[listA.name] > tokenCountByListName[listB.name]) { + return -1 + } + if (tokenCountByListName[listA.name] < tokenCountByListName[listB.name]) { + return 1 + } + return listA.name.toLowerCase() < listB.name.toLowerCase() ? -1 - : l1.name.toLowerCase() === l2.name.toLowerCase() + : listA.name.toLowerCase() === listB.name.toLowerCase() ? 0 : 1 } - if (l1) return -1 - if (l2) return 1 + if (listA) return -1 + if (listB) return 1 return 0 }) }, - // [lists, activeCopy] - [lists, unsupportedListUrls, activeCopy] + // [lists, activeListUrls, tokenCountByListName] + [lists, unsupportedListUrls, activeListUrls, tokenCountByListName] ) // temporary fetched list for import flow diff --git a/src/custom/components/SearchModal/ManageLists/index.tsx b/src/custom/components/SearchModal/ManageLists/index.tsx index 9818b6911..e5b38f226 100644 --- a/src/custom/components/SearchModal/ManageLists/index.tsx +++ b/src/custom/components/SearchModal/ManageLists/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { ManageLists as ManageListsMod, ListContainer } from './ManageListsMod' import { DEFAULT_NETWORK_FOR_LISTS, UNSUPPORTED_LIST_URLS } from 'constants/lists' import { useActiveWeb3React } from 'hooks/web3' @@ -27,8 +26,9 @@ const Wrapper = styled.div` } ` -export const RowWrapper = styled(Row)<{ bgColor: string; active: boolean }>` +export const RowWrapper = styled(Row)<{ bgColor: string; active: boolean; hasActiveTokens: boolean }>` background-color: ${({ bgColor, active, theme }) => (active ? bgColor ?? 'transparent' : theme.bg4)}; + opacity: ${({ hasActiveTokens }) => (hasActiveTokens ? 1 : 0.4)}; transition: 0.2s; align-items: center; padding: 1rem; diff --git a/src/custom/components/SearchModal/ManageTokens/ManageTokensMod.tsx b/src/custom/components/SearchModal/ManageTokens/ManageTokensMod.tsx index 8471df049..405bd88c6 100644 --- a/src/custom/components/SearchModal/ManageTokens/ManageTokensMod.tsx +++ b/src/custom/components/SearchModal/ManageTokens/ManageTokensMod.tsx @@ -1,4 +1,4 @@ -import React, { useRef, RefObject, useCallback, useState, useMemo } from 'react' +import { useRef, RefObject, useCallback, useState, useMemo } from 'react' import Column from 'components/Column' import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink' import { PaddedColumn, Separator, SearchInput } from 'components/SearchModal/styleds' diff --git a/src/custom/components/SearchModal/ManageTokens/index.tsx b/src/custom/components/SearchModal/ManageTokens/index.tsx index d644262ff..b719af1de 100644 --- a/src/custom/components/SearchModal/ManageTokens/index.tsx +++ b/src/custom/components/SearchModal/ManageTokens/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { Token } from '@uniswap/sdk-core' import styled, { DefaultTheme } from 'styled-components/macro' import Card from 'components/Card' diff --git a/src/custom/components/Settings/SettingsMod.tsx b/src/custom/components/Settings/SettingsMod.tsx index 54ae37a0b..cb1f1cf59 100644 --- a/src/custom/components/Settings/SettingsMod.tsx +++ b/src/custom/components/Settings/SettingsMod.tsx @@ -1,9 +1,9 @@ import { t, Trans } from '@lingui/macro' -import React, { useContext, useRef, useState } from 'react' +import { useContext, useRef, useState } from 'react' import { Settings, X } from 'react-feather' // import ReactGA from 'react-ga' import { Text } from 'rebass' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { useOnClickOutside } from 'hooks/useOnClickOutside' import { ApplicationModal } from 'state/application/actions' import { useModalOpen, useToggleSettingsMenu } from 'state/application/hooks' @@ -186,7 +186,7 @@ export default function SettingsTab({ className, placeholderSlippage, SettingsBu */} {open && ( - + Transaction Settings @@ -226,14 +226,20 @@ export default function SettingsTab({ className, placeholderSlippage, SettingsBu {/* - Disable Multihops + Disable Multihops - + Restricts swaps to direct pairs only.} /> (singleHopOnly ? setSingleHopOnly(false) : setSingleHopOnly(true))} + toggle={() => { + ReactGA.event({ + category: 'Routing', + action: singleHopOnly ? 'disable single hop' : 'enable single hop', + }) + setSingleHopOnly(!singleHopOnly) + }} /> */} diff --git a/src/custom/components/Settings/index.tsx b/src/custom/components/Settings/index.tsx index 269d13a9c..d007daace 100644 --- a/src/custom/components/Settings/index.tsx +++ b/src/custom/components/Settings/index.tsx @@ -1,6 +1,6 @@ import { WithClassName } from 'types' -import React from 'react' -import styled from 'styled-components' + +import styled from 'styled-components/macro' import { RowFixed } from 'components/Row' import SettingsMod, { StyledMenuButton, MenuFlyout, StyledMenuIcon, EmojiWrapper } from './SettingsMod' import { Percent } from '@uniswap/sdk-core' diff --git a/src/custom/components/SwitchLocaleLink/SwitchLocaleLinkMod.tsx b/src/custom/components/SwitchLocaleLink/SwitchLocaleLinkMod.tsx new file mode 100644 index 000000000..8ac28953d --- /dev/null +++ b/src/custom/components/SwitchLocaleLink/SwitchLocaleLinkMod.tsx @@ -0,0 +1,50 @@ +import { Trans } from '@lingui/macro' +import { useMemo } from 'react' +import styled from 'styled-components/macro' +import { DEFAULT_LOCALE, LOCALE_LABEL, SupportedLocale } from 'constants/locales' +import { navigatorLocale, useActiveLocale } from 'hooks/useActiveLocale' +import { StyledInternalLink, TYPE } from 'theme' +import { useLocationLinkProps } from 'hooks/useLocationLinkProps' + +const Container = styled(TYPE.small)` + opacity: 0.6; + :hover { + opacity: 1; + } + margin-top: 1rem !important; +` + +const useTargetLocale = (activeLocale: SupportedLocale) => { + const browserLocale = useMemo(() => navigatorLocale(), []) + + if (browserLocale && (browserLocale !== DEFAULT_LOCALE || activeLocale !== DEFAULT_LOCALE)) { + if (activeLocale === browserLocale) { + return DEFAULT_LOCALE + } else { + return browserLocale + } + } + return null +} + +// export function SwitchLocaleLink() { +export function SwitchLocaleLink({ label = 'CowSwap available in: ' }: { label?: string }) { + const activeLocale = useActiveLocale() + const targetLocale = useTargetLocale(activeLocale) + + const { to, onClick } = useLocationLinkProps(targetLocale) + + if (!targetLocale || !to) return null + + return ( + + + {/* Uniswap available in:{' '} */} + {label} + + {LOCALE_LABEL[targetLocale]} + + + + ) +} diff --git a/src/custom/components/SwitchLocaleLink/index.tsx b/src/custom/components/SwitchLocaleLink/index.tsx new file mode 100644 index 000000000..5745d78d6 --- /dev/null +++ b/src/custom/components/SwitchLocaleLink/index.tsx @@ -0,0 +1,5 @@ +import { SwitchLocaleLink as SwitchLocaleLinkMod } from '../SwitchLocaleLink' + +export function SwitchLocaleLink() { + return +} diff --git a/src/custom/components/Toggle/index.tsx b/src/custom/components/Toggle/index.tsx index 6a05507db..2c7179172 100644 --- a/src/custom/components/Toggle/index.tsx +++ b/src/custom/components/Toggle/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import ToggleUni, { ToggleProps as TogglePropsUni, ToggleElement } from '@src/components/Toggle' export type ToggleProps = TogglePropsUni diff --git a/src/custom/components/Tooltip/TooltipMod.tsx b/src/custom/components/Tooltip/TooltipMod.tsx index 5c4c8a080..31044b2b9 100644 --- a/src/custom/components/Tooltip/TooltipMod.tsx +++ b/src/custom/components/Tooltip/TooltipMod.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useCallback, useState } from 'react' +import { ReactNode, useCallback, useState } from 'react' import styled from 'styled-components/macro' import Popover, { PopoverProps } from 'components/Popover' diff --git a/src/custom/components/TransactionConfirmationModal/TransactionConfirmationModalMod.tsx b/src/custom/components/TransactionConfirmationModal/TransactionConfirmationModalMod.tsx index 80f5d0d70..90a87674b 100644 --- a/src/custom/components/TransactionConfirmationModal/TransactionConfirmationModalMod.tsx +++ b/src/custom/components/TransactionConfirmationModal/TransactionConfirmationModalMod.tsx @@ -1,16 +1,14 @@ import { Currency } from '@uniswap/sdk-core' -import React, { ReactNode, useContext } from 'react' -import styled, { ThemeContext } from 'styled-components' -// import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink' +import { ReactNode, useContext } from 'react' +import styled, { ThemeContext } from 'styled-components/macro' +import { getExplorerLink, ExplorerDataType } from 'utils/getExplorerLink' // import Modal from 'components/Modal' -// import { ExternalLink } from 'theme' +import { ExternalLink } from 'theme' import { Text } from 'rebass' import { CloseIcon, CustomLightSpinner } from 'theme' +import { RowBetween, RowFixed } from 'components/Row' import { - RowBetween, - // RowFixed -} from 'components/Row' -import { + AlertCircle, AlertTriangle, // ArrowUpCircle, // CheckCircle @@ -25,8 +23,11 @@ import Circle from 'assets/images/blue-loader.svg' import { useActiveWeb3React } from 'hooks/web3' // import useAddTokenToMetamask from 'hooks/useAddTokenToMetamask' import { Trans } from '@lingui/macro' +import { CHAIN_INFO, L2_CHAIN_IDS /* , SupportedL2ChainId */, SupportedChainId } from 'constants/chains' +import { useIsTransactionConfirmed, useTransaction } from 'state/transactions/hooks' +import Badge from 'components/Badge' +import AnimatedConfirmation from 'components/TransactionConfirmationModal/AnimatedConfirmation' // MOD -// import { getEtherscanLink, getExplorerLabel } from 'utils' import { GpModal } from 'components/Modal' import { lighten } from 'polished' import { ConfirmationModalContentProps, TransactionSubmittedContent, GPModalHeader } from '.' // mod @@ -54,11 +55,11 @@ const ConfirmedIcon = styled(ColumnCenter)<{ inline?: boolean }>` padding: ${({ inline }) => (inline ? '20px 0' : '60px 0;')}; ` -// const StyledLogo = styled.img` -// height: 16px; -// width: 16px; -// margin-left: 6px; -// ` +const StyledLogo = styled.img` + height: 16px; + width: 16px; + margin-left: 6px; +` export function ConfirmationPendingContent({ onDismiss, @@ -86,6 +87,7 @@ export function ConfirmationPendingContent({ Waiting For Confirmation + {/* */} {pendingText} @@ -135,20 +137,9 @@ export function ConfirmationPendingContent({ Transaction Submitted {chainId && hash && ( - // - - */ -{ - /* View on Etherscan */ -} -{ - /* View on Explorer */ -} -{ - /* TODO: add transaltions for this */ -} -{ - /* {getExplorerLabel(chainId, hash, 'transaction')} + + + View on Explorer )} @@ -178,7 +169,6 @@ export function ConfirmationPendingContent({ ) } */ -} export function ConfirmationModalContent({ title, @@ -241,6 +231,107 @@ export function TransactionErrorContent({ message, onDismiss }: { message: React ) } +function L2Content({ + onDismiss, + chainId, + hash, + pendingText, + inline, +}: { + onDismiss: () => void + hash: string | undefined + // chainId: number + chainId: SupportedChainId + currencyToAdd?: Currency | undefined + pendingText: ReactNode + inline?: boolean // not in modal +}) { + const theme = useContext(ThemeContext) + + const transaction = useTransaction(hash) + const confirmed = useIsTransactionConfirmed(hash) + const transactionSuccess = transaction?.receipt?.status === 1 + + // convert unix time difference to seconds + const secondsToConfirm = transaction?.confirmedTime + ? (transaction.confirmedTime - transaction.addedTime) / 1000 + : undefined + + const info = CHAIN_INFO[chainId /* as SupportedL2ChainId */] + + return ( + +
+ {!inline && ( + + + + + {info.label} + + + + + )} + + {confirmed ? ( + transactionSuccess ? ( + // + + ) : ( + + ) + ) : ( + + )} + + + + {!hash ? ( + Confirm transaction in wallet + ) : !confirmed ? ( + Transaction Submitted + ) : transactionSuccess ? ( + Success + ) : ( + Error + )} + + + {transaction?.summary ?? pendingText ?? ''} + + {chainId && hash ? ( + + + View on Explorer + + + ) : ( +
+ )} + + {!secondsToConfirm ? ( +
+ ) : ( +
+ Transaction completed in + + {secondsToConfirm} seconds 🎉 + +
+ )} +
+ + + {inline ? Return : Close} + + +
+
+
+ ) +} + interface ConfirmationModalProps { isOpen: boolean onDismiss: () => void @@ -262,12 +353,17 @@ export default function TransactionConfirmationModal({ }: ConfirmationModalProps) { const { chainId } = useActiveWeb3React() + const isL2 = Boolean(chainId && L2_CHAIN_IDS.includes(chainId)) + if (!chainId) return null // confirmation screen return ( + // - {attemptingTxn ? ( + {isL2 && (hash || attemptingTxn) ? ( + + ) : attemptingTxn ? ( ) : hash ? ( @@ -162,6 +167,7 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction bgColor={theme.bg3} color={theme.text1} text={ + // Your transaction will revert if the price changes unfavorably by more than this percentage.

Your slippage is MEV protected: all orders are submitted with tight spread (0.1%) on-chain.

@@ -230,41 +236,43 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction ) : null} - - - - Transaction deadline - - - - - - 0 - ? deadlineInput - : deadline === DEFAULT_DEADLINE_FROM_NOW - ? '' - : (deadline / 60).toString() - } - onChange={(e) => parseCustomDeadline(e.target.value)} - onBlur={() => { - setDeadlineInput('') - setDeadlineError(false) - }} - color={deadlineError ? 'red' : ''} + {showCustomDeadlineRow && ( + + + + Transaction deadline + + - - - minutes - - - + + + + 0 + ? deadlineInput + : deadline === DEFAULT_DEADLINE_FROM_NOW + ? '' + : (deadline / 60).toString() + } + onChange={(e) => parseCustomDeadline(e.target.value)} + onBlur={() => { + setDeadlineInput('') + setDeadlineError(false) + }} + color={deadlineError ? 'red' : ''} + /> + + + minutes + + + + )} ) } diff --git a/src/custom/components/TransactionSettings/index.tsx b/src/custom/components/TransactionSettings/index.tsx index 60f362821..1ae13f9f5 100644 --- a/src/custom/components/TransactionSettings/index.tsx +++ b/src/custom/components/TransactionSettings/index.tsx @@ -1,11 +1,10 @@ -import React from 'react' import SlippageTabsMod, { TransactionSettingsProps as TransactionSettingsPropsMod, FancyButton as FancyButtonUni, OptionCustom, } from './TransactionSettingsMod' import { RowFixed } from 'components/Row' -import styled from 'styled-components' +import styled from 'styled-components/macro' // TODO: option was restyled in v3, review if this change is necessary export const Option = styled(FancyButtonUni)<{ active: boolean }>` diff --git a/src/custom/components/Version/index.tsx b/src/custom/components/Version/index.tsx index 7318e024f..0afbc7961 100644 --- a/src/custom/components/Version/index.tsx +++ b/src/custom/components/Version/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { ExternalLink, TYPE } from 'theme' import { version as WEB_VERSION } from '@src/../package.json' diff --git a/src/custom/components/WalletModal/Option/OptionMod.tsx b/src/custom/components/WalletModal/Option/OptionMod.tsx index d1f1489c2..e538016a7 100644 --- a/src/custom/components/WalletModal/Option/OptionMod.tsx +++ b/src/custom/components/WalletModal/Option/OptionMod.tsx @@ -1,4 +1,3 @@ -import React from 'react' import styled from 'styled-components/macro' import { ExternalLink } from 'theme' diff --git a/src/custom/components/WalletModal/Option/index.tsx b/src/custom/components/WalletModal/Option/index.tsx index 3cc70a946..14221315a 100644 --- a/src/custom/components/WalletModal/Option/index.tsx +++ b/src/custom/components/WalletModal/Option/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { darken } from 'polished' import OptionMod, { OptionCardClickable as OptionCardClickableMod, OptionProps } from './OptionMod' diff --git a/src/custom/components/WalletModal/WalletModalMod.tsx b/src/custom/components/WalletModal/WalletModalMod.tsx index 22586e33a..03ffd6963 100644 --- a/src/custom/components/WalletModal/WalletModalMod.tsx +++ b/src/custom/components/WalletModal/WalletModalMod.tsx @@ -2,7 +2,7 @@ import { AbstractConnector } from '@web3-react/abstract-connector' import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core' import { WalletConnectConnector } from '@web3-react/walletconnect-connector' import { AutoRow } from 'components/Row' -import React, { useEffect, useState } from 'react' +import { useEffect, useState } from 'react' import { isMobile } from 'react-device-detect' import ReactGA from 'react-ga' import styled from 'styled-components/macro' diff --git a/src/custom/components/WalletModal/index.tsx b/src/custom/components/WalletModal/index.tsx index 7a7ab3a3d..af3cbc021 100644 --- a/src/custom/components/WalletModal/index.tsx +++ b/src/custom/components/WalletModal/index.tsx @@ -1,6 +1,5 @@ -import React from 'react' import { GpModal } from 'components/Modal' // mod -import styled from 'styled-components' +import styled from 'styled-components/macro' import WalletModalMod, { WalletModalProps } from './WalletModalMod' import { ExternalLink } from 'theme' import { Trans } from '@lingui/macro' diff --git a/src/custom/components/Web3Status/Web3StatusMod.tsx b/src/custom/components/Web3Status/Web3StatusMod.tsx index beca06ba0..115c0ed7a 100644 --- a/src/custom/components/Web3Status/Web3StatusMod.tsx +++ b/src/custom/components/Web3Status/Web3StatusMod.tsx @@ -1,10 +1,10 @@ import { AbstractConnector } from '@web3-react/abstract-connector' import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core' import { darken, lighten } from 'polished' -import React /*{, useMemo }*/ from 'react' +// import { useMemo } from 'react' import { Activity } from 'react-feather' import { t, Trans } from '@lingui/macro' -import styled, { css } from 'styled-components' +import styled, { css } from 'styled-components/macro' // import CoinbaseWalletIcon from 'assets/images/coinbaseWalletIcon.svg' // import FortmaticIcon from 'assets/images/fortmaticIcon.png' // import PortisIcon from 'assets/images/portisIcon.png' @@ -61,6 +61,7 @@ const Web3StatusError = styled(Web3StatusGeneric)` const Web3StatusConnect = styled(Web3StatusGeneric)<{ faded?: boolean }>` background-color: ${({ theme }) => theme.primary4}; border: none; + color: ${({ theme }) => theme.primaryText1}; font-weight: 500; diff --git a/src/custom/components/Web3Status/index.tsx b/src/custom/components/Web3Status/index.tsx index 7de200a14..6c83a1792 100644 --- a/src/custom/components/Web3Status/index.tsx +++ b/src/custom/components/Web3Status/index.tsx @@ -1,24 +1,53 @@ -import React, { useMemo } from 'react' -import styled from 'styled-components' +import { useMemo } from 'react' +import styled from 'styled-components/macro' import WalletModal from 'components/WalletModal' -import { Web3StatusInner, Web3StatusConnected } from './Web3StatusMod' +import { Web3StatusInner, Web3StatusConnected, Text } from './Web3StatusMod' import { AbstractConnector } from '@web3-react/abstract-connector' import { getStatusIcon } from 'components/AccountDetails' import useRecentActivity, { TransactionAndOrder } from 'hooks/useRecentActivity' import { useWalletInfo } from 'hooks/useWalletInfo' import { OrderStatus } from 'state/orders/actions' +import { ButtonSecondary } from 'components/Button' const Wrapper = styled.div` color: ${({ theme }) => theme.wallet?.color}; + width: 100%; + + ${ButtonSecondary} { + height: 38px; + + > p { + font-size: 15px; + + ${({ theme }) => theme.mediaWidth.upToExtraSmall` + font-size: 13px; + `}; + } + } ${Web3StatusConnected} { color: ${({ theme }) => theme.wallet?.color}; background: ${({ theme }) => theme.wallet?.background}; + height: 38px; + border: 1px solid transparent; + box-shadow: none; + transform: none; + + &:hover { + border: 1px solid ${({ theme }) => theme.primary1}; + } > div > svg > path { stroke: ${({ theme }) => theme.black}; } } + + ${Text} { + ${({ theme }) => theme.mediaWidth.upToExtraSmall` + font-size: 13px; + margin: 1px 3px; + `} + } ` const isPending = (data: TransactionAndOrder) => data.status === OrderStatus.PENDING diff --git a/src/custom/components/swap/AdvancedSwapDetails/AdvancedSwapDetailsMod.tsx b/src/custom/components/swap/AdvancedSwapDetails/AdvancedSwapDetailsMod.tsx index 9d2c38e4d..93841c149 100644 --- a/src/custom/components/swap/AdvancedSwapDetails/AdvancedSwapDetailsMod.tsx +++ b/src/custom/components/swap/AdvancedSwapDetails/AdvancedSwapDetailsMod.tsx @@ -2,7 +2,7 @@ import { Percent /*, Currency, TradeType */ } from '@uniswap/sdk-core' // import { Trade as V2Trade } from '@uniswap/v2-sdk' // import { Trade as V3Trade } from '@uniswap/v3-sdk' -import React /* , { useContext, useMemo } */ from 'react' +// import { useContext, useMemo } from 'react' // import { ThemeContext } from 'styled-components/macro' // import { TYPE } from 'theme' // import { computeRealizedLPFeePercent } from 'utils/prices' diff --git a/src/custom/components/swap/AdvancedSwapDetailsDropdown/AdvancedSwapDetailsDropdownMod.tsx b/src/custom/components/swap/AdvancedSwapDetailsDropdown/AdvancedSwapDetailsDropdownMod.tsx index 804c61820..791eb54f5 100644 --- a/src/custom/components/swap/AdvancedSwapDetailsDropdown/AdvancedSwapDetailsDropdownMod.tsx +++ b/src/custom/components/swap/AdvancedSwapDetailsDropdown/AdvancedSwapDetailsDropdownMod.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { Percent } from '@uniswap/sdk-core' import styled from 'styled-components/macro' import { useLastTruthy } from 'hooks/useLast' diff --git a/src/custom/components/swap/ConfirmSwapModal/ConfirmSwapModalMod.tsx b/src/custom/components/swap/ConfirmSwapModal/ConfirmSwapModalMod.tsx index fda1a8567..0831e4db5 100644 --- a/src/custom/components/swap/ConfirmSwapModal/ConfirmSwapModalMod.tsx +++ b/src/custom/components/swap/ConfirmSwapModal/ConfirmSwapModalMod.tsx @@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro' import { /* Currency, */ Percent /* , TradeType */ } from '@uniswap/sdk-core' // import { Trade as V2Trade } from '@uniswap/v2-sdk' // import { Trade as V3Trade } from '@uniswap/v3-sdk' -import React, { ReactNode, useCallback, useMemo } from 'react' +import { ReactNode, useCallback, useMemo } from 'react' import TransactionConfirmationModal, { ConfirmationModalContent, TransactionErrorContent, diff --git a/src/custom/components/swap/ConfirmSwapModal/index.tsx b/src/custom/components/swap/ConfirmSwapModal/index.tsx index d340cff23..28cb42430 100644 --- a/src/custom/components/swap/ConfirmSwapModal/index.tsx +++ b/src/custom/components/swap/ConfirmSwapModal/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { Trans } from '@lingui/macro' import ConfirmSwapModalMod from './ConfirmSwapModalMod' diff --git a/src/custom/components/swap/EthWethWrap/WrapCard.tsx b/src/custom/components/swap/EthWethWrap/WrapCard.tsx index ad489e200..c997a2ff8 100644 --- a/src/custom/components/swap/EthWethWrap/WrapCard.tsx +++ b/src/custom/components/swap/EthWethWrap/WrapCard.tsx @@ -1,6 +1,5 @@ -import React from 'react' import { CurrencyAmount, Currency } from '@uniswap/sdk-core' -import styled from 'styled-components' +import styled from 'styled-components/macro' import CurrencyLogo from 'components/CurrencyLogo' import { formatSmart } from 'utils/format' import { AMOUNT_PRECISION } from 'constants/index' diff --git a/src/custom/components/swap/EthWethWrap/WrappingVisualisation.tsx b/src/custom/components/swap/EthWethWrap/WrappingVisualisation.tsx index dce5e43bd..45e51b496 100644 --- a/src/custom/components/swap/EthWethWrap/WrappingVisualisation.tsx +++ b/src/custom/components/swap/EthWethWrap/WrappingVisualisation.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { ArrowRight } from 'react-feather' import { CurrencyAmount, Currency } from '@uniswap/sdk-core' import { WrapCardContainer, WrapCard } from './WrapCard' diff --git a/src/custom/components/swap/EthWethWrap/index.tsx b/src/custom/components/swap/EthWethWrap/index.tsx index 0292fc176..230f378f9 100644 --- a/src/custom/components/swap/EthWethWrap/index.tsx +++ b/src/custom/components/swap/EthWethWrap/index.tsx @@ -1,5 +1,5 @@ -import React, { useState, useCallback, useMemo, useEffect, ReactNode } from 'react' -import styled from 'styled-components' +import { useState, useCallback, useMemo, useEffect, ReactNode } from 'react' +import styled from 'styled-components/macro' import { TransactionResponse } from '@ethersproject/providers' import { AlertTriangle } from 'react-feather' import { Currency, Token, CurrencyAmount } from '@uniswap/sdk-core' diff --git a/src/custom/components/swap/FeeInformationTooltip.tsx b/src/custom/components/swap/FeeInformationTooltip.tsx index 09070e84d..1e3fc177d 100644 --- a/src/custom/components/swap/FeeInformationTooltip.tsx +++ b/src/custom/components/swap/FeeInformationTooltip.tsx @@ -1,7 +1,7 @@ -import React, { useMemo } from 'react' +import { useMemo } from 'react' import TradeGp from 'state/swap/TradeGp' import QuestionHelper from 'components/QuestionHelper' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { formatMax, formatSmart } from 'utils/format' import useTheme from 'hooks/useTheme' import { FIAT_PRECISION } from 'constants/index' diff --git a/src/custom/components/swap/SwapModalFooter/SwapModalFooterMod.tsx b/src/custom/components/swap/SwapModalFooter/SwapModalFooterMod.tsx index 0e18c3111..50cc968d8 100644 --- a/src/custom/components/swap/SwapModalFooter/SwapModalFooterMod.tsx +++ b/src/custom/components/swap/SwapModalFooter/SwapModalFooterMod.tsx @@ -1,8 +1,9 @@ import { Trans } from '@lingui/macro' +// import { Currency, TradeType } from '@uniswap/sdk-core' // import { Trade as V2Trade } from '@uniswap/v2-sdk' // import { Trade as V3Trade } from '@uniswap/v3-sdk' -import React from 'react' +// import { ReactNode } from 'react' import { Text } from 'rebass' import { ButtonError } from 'components/Button' import { AutoRow } from 'components/Row' diff --git a/src/custom/components/swap/SwapModalFooter/index.tsx b/src/custom/components/swap/SwapModalFooter/index.tsx index 784317d51..a9cf63b78 100644 --- a/src/custom/components/swap/SwapModalFooter/index.tsx +++ b/src/custom/components/swap/SwapModalFooter/index.tsx @@ -1,15 +1,8 @@ -import React from 'react' import SwapModalFooterMod, { SwapModalFooterProps } from './SwapModalFooterMod' -import { StyledBalanceMaxMini } from 'components/swap/styleds' import { RowBetween, RowFixed } from 'components/Row' -import styled from 'styled-components' +import styled from 'styled-components/macro' const Wrapper = styled.div` - ${StyledBalanceMaxMini} { - background: ${({ theme }) => theme.bg1}; - color: ${({ theme }) => theme.text1}; - } - ${RowBetween} > div, ${RowFixed} > div { color: ${({ theme }) => theme.text1}; diff --git a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx index 4ee20b4f1..ac08616c3 100644 --- a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx +++ b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx @@ -1,10 +1,10 @@ import { /* Currency, */ Percent, TradeType } from '@uniswap/sdk-core' // import { Trade as V2Trade } from '@uniswap/v2-sdk' // import { Trade as V3Trade } from '@uniswap/v3-sdk' -import React, { useState, useContext, useMemo } from 'react' +import { useContext, useState, useMemo } from 'react' import { ArrowDown, AlertTriangle } from 'react-feather' import { Text } from 'rebass' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { useHigherUSDValue /* , useUSDCValue */ } from 'hooks/useUSDCPrice' import { TYPE } from 'theme' import { ButtonPrimary } from 'components/Button' @@ -18,6 +18,7 @@ import { TruncatedText, SwapShowAcceptChanges } from 'components/swap/styleds' import { Trans } from '@lingui/macro' import { AdvancedSwapDetails } from 'components/swap/AdvancedSwapDetails' +// import { LightCard } from '../Card' // import TradePrice from 'components/swap/TradePrice' diff --git a/src/custom/components/swap/SwapModalHeader/index.tsx b/src/custom/components/swap/SwapModalHeader/index.tsx index 2bf41253b..9e60193a1 100644 --- a/src/custom/components/swap/SwapModalHeader/index.tsx +++ b/src/custom/components/swap/SwapModalHeader/index.tsx @@ -1,8 +1,7 @@ -import React from 'react' // import { computeTradePriceBreakdown } from '../TradeSummary' import SwapModalHeaderMod, { SwapModalHeaderProps } from './SwapModalHeaderMod' import { AutoColumn } from 'components/Column' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { LightCard as LightCardUni } from 'components/Card' import { darken, transparentize } from 'polished' import { AuxInformationContainer } from 'components/CurrencyInputPanel' diff --git a/src/custom/components/swap/TradePrice/TradePriceMod.tsx b/src/custom/components/swap/TradePrice/TradePriceMod.tsx index 422f61b18..a8809cdd1 100644 --- a/src/custom/components/swap/TradePrice/TradePriceMod.tsx +++ b/src/custom/components/swap/TradePrice/TradePriceMod.tsx @@ -1,8 +1,8 @@ -import React, { useCallback } from 'react' +import { useCallback } from 'react' import { Price, Currency } from '@uniswap/sdk-core' import { useContext } from 'react' import { Text } from 'rebass' -import styled, { ThemeContext } from 'styled-components' +import styled, { ThemeContext } from 'styled-components/macro' import { formatMax, formatSmart } from 'utils/format' // mod import { LightGreyText } from 'pages/Swap' diff --git a/src/custom/components/swap/TradePrice/index.tsx b/src/custom/components/swap/TradePrice/index.tsx index beb09772a..8d7a3b142 100644 --- a/src/custom/components/swap/TradePrice/index.tsx +++ b/src/custom/components/swap/TradePrice/index.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react' +import { useMemo } from 'react' import TradePriceMod, { TradePriceProps } from './TradePriceMod' import { useHigherUSDValue /* useUSDCValue */ } from 'hooks/useUSDCPrice' import { formatSmart } from 'utils/format' diff --git a/src/custom/components/swap/TradeSummary/RowFee.tsx b/src/custom/components/swap/TradeSummary/RowFee.tsx index 127ad694e..000bc0707 100644 --- a/src/custom/components/swap/TradeSummary/RowFee.tsx +++ b/src/custom/components/swap/TradeSummary/RowFee.tsx @@ -1,6 +1,6 @@ -import React, { useContext } from 'react' +import { useContext, useMemo } from 'react' import { CurrencyAmount, Currency, TradeType, Token } from '@uniswap/sdk-core' -import { ThemeContext } from 'styled-components' +import { ThemeContext } from 'styled-components/macro' import { TYPE } from 'theme' import { formatMax, formatSmart } from 'utils/format' @@ -57,7 +57,7 @@ export function RowFee({ rowHeight, }: RowFeeProps) { const theme = useContext(ThemeContext) - const { realizedFee } = React.useMemo(() => computeTradePriceBreakdown(trade), [trade]) + const { realizedFee } = useMemo(() => computeTradePriceBreakdown(trade), [trade]) // trades are null when there is a fee quote error e.g // so we can take both const feeFiatDisplay = `(≈$${formatSmart(feeFiatValue, FIAT_PRECISION)})` diff --git a/src/custom/components/swap/TradeSummary/RowReceivedAfterSlippage.tsx b/src/custom/components/swap/TradeSummary/RowReceivedAfterSlippage.tsx index d6ff83758..b42815dba 100644 --- a/src/custom/components/swap/TradeSummary/RowReceivedAfterSlippage.tsx +++ b/src/custom/components/swap/TradeSummary/RowReceivedAfterSlippage.tsx @@ -1,6 +1,6 @@ -import React, { useContext, useMemo } from 'react' +import { useContext, useMemo } from 'react' import { Percent, TradeType } from '@uniswap/sdk-core' -import { ThemeContext } from 'styled-components' +import { ThemeContext } from 'styled-components/macro' import { Trans } from '@lingui/macro' import { TYPE } from 'theme' diff --git a/src/custom/components/swap/TradeSummary/RowSlippage.tsx b/src/custom/components/swap/TradeSummary/RowSlippage.tsx index 45669fd1d..7a23ce2f0 100644 --- a/src/custom/components/swap/TradeSummary/RowSlippage.tsx +++ b/src/custom/components/swap/TradeSummary/RowSlippage.tsx @@ -1,6 +1,6 @@ -import React, { useContext } from 'react' +import { useContext } from 'react' import { Percent } from '@uniswap/sdk-core' -import { ThemeContext } from 'styled-components' +import { ThemeContext } from 'styled-components/macro' import { Trans } from '@lingui/macro' import { TYPE } from 'theme' diff --git a/src/custom/components/swap/TradeSummary/index.tsx b/src/custom/components/swap/TradeSummary/index.tsx index 1be2764e8..e2ff47a89 100644 --- a/src/custom/components/swap/TradeSummary/index.tsx +++ b/src/custom/components/swap/TradeSummary/index.tsx @@ -1,5 +1,4 @@ -import React from 'react' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { AdvancedSwapDetailsProps } from '../AdvancedSwapDetails' import { AutoColumn } from 'components/Column' diff --git a/src/custom/components/swap/UnsupportedCurrencyFooter/UnsupportedCurrencyFooterMod.tsx b/src/custom/components/swap/UnsupportedCurrencyFooter/UnsupportedCurrencyFooterMod.tsx index ec1753ba1..0102e863f 100644 --- a/src/custom/components/swap/UnsupportedCurrencyFooter/UnsupportedCurrencyFooterMod.tsx +++ b/src/custom/components/swap/UnsupportedCurrencyFooter/UnsupportedCurrencyFooterMod.tsx @@ -1,19 +1,20 @@ -import React, { ReactNode, useState } from 'react' +import { ReactNode, useState } from 'react' import styled from 'styled-components/macro' import { TYPE, CloseIcon, ExternalLink } from 'theme' import { ButtonEmpty } from 'components/Button' import Modal from 'components/Modal' import Card, { OutlineCard } from 'components/Card' -import Row, { RowBetween, AutoRow } from 'components/Row' +import { RowBetween, AutoRow } from 'components/Row' import { AutoColumn } from 'components/Column' import CurrencyLogo from 'components/CurrencyLogo' import { useActiveWeb3React } from 'hooks/web3' import { Currency /* , Token */ } from '@uniswap/sdk-core' +import { useIsUnsupportedToken } from 'state/lists/hooks' // import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' import { Trans } from '@lingui/macro' + // MOD import { getEtherscanLink } from 'utils' -import { useIsUnsupportedToken } from 'state/lists/hooks' export const DetailsFooter = styled.div<{ show: boolean }>` padding-top: calc(16px + 2rem); @@ -41,17 +42,10 @@ export const AddressText = styled(TYPE.blue)` `} ` -const FlexRow = styled.div` - display: flex; - flex-flow: row wrap; - width: 100%; - gap: 5px; -` - // MOD export interface UnsupportedCurrencyFooterParams { show: boolean - currencies: (Currency | undefined)[] + currencies: (Currency | null | undefined)[] detailsTitle?: ReactNode detailsText?: ReactNode showDetailsText?: ReactNode @@ -92,7 +86,6 @@ UnsupportedCurrencyFooterParams) { {detailsTitle} - setShowDetails(false)} /> {tokens.map((token) => { @@ -102,19 +95,27 @@ UnsupportedCurrencyFooterParams) { // Object.keys(unsupportedTokens).includes(token.address) && ( isUnsupportedToken(token.address) && ( - - + + {token.symbol} {chainId && ( - - - {token.address} - - + // + + {token.address} + + // )} - + ) ) diff --git a/src/custom/components/swap/UnsupportedCurrencyFooter/index.tsx b/src/custom/components/swap/UnsupportedCurrencyFooter/index.tsx index be67ffb22..3e588ae96 100644 --- a/src/custom/components/swap/UnsupportedCurrencyFooter/index.tsx +++ b/src/custom/components/swap/UnsupportedCurrencyFooter/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { HashLink } from 'react-router-hash-link' import UnsupportedCurrencyFooterMod, { UnsupportedCurrencyFooterParams } from './UnsupportedCurrencyFooterMod' import { UNSUPPORTED_TOKENS_FAQ_URL } from 'constants/index' diff --git a/src/custom/connectors/index.ts b/src/custom/connectors/index.ts index 6e3d83584..e755cea86 100644 --- a/src/custom/connectors/index.ts +++ b/src/custom/connectors/index.ts @@ -8,6 +8,8 @@ import { FortmaticConnector } from 'connectors/Fortmatic' import { NetworkConnector } from 'connectors/NetworkConnector' import { AbstractConnector } from '@web3-react/abstract-connector' +export * from '@src/connectors' + export const WALLET_CONNECT_BRIDGE = process.env.WALLET_CONNECT_BRIDGE || 'wss://safe-walletconnect.gnosis.io' type RpcNetworks = { [chainId: number]: string } diff --git a/src/custom/constants/addresses.ts b/src/custom/constants/addresses.ts index b59a8a87f..c95052e3f 100644 --- a/src/custom/constants/addresses.ts +++ b/src/custom/constants/addresses.ts @@ -1,9 +1,9 @@ -import { AddressMap, MULTICALL2_ADDRESSES as MULTICALL2_ADDRESSES_UNI } from '@src/constants/addresses' +import { AddressMap, MULTICALL_ADDRESS as MULTICALL2_ADDRESSES_UNI } from '@src/constants/addresses' import { SupportedChainId } from 'constants/chains' export * from '@src/constants/addresses' -export const MULTICALL2_ADDRESSES: AddressMap = { +export const MULTICALL_ADDRESS: AddressMap = { ...MULTICALL2_ADDRESSES_UNI, - [SupportedChainId.XDAI]: '0x08612d3C4A5Dfe2FaaFaFe6a4ff712C2dC675bF7', + [SupportedChainId.XDAI]: '0x0f41c16b8ad27c11f181eca85f0941868c1297af', } diff --git a/src/custom/constants/chains/chainsMod.ts b/src/custom/constants/chains/chainsMod.ts index 6f63b4e81..2256ffd26 100644 --- a/src/custom/constants/chains/chainsMod.ts +++ b/src/custom/constants/chains/chainsMod.ts @@ -1,21 +1,109 @@ +import { L1ChainInfo } from '@src/constants/chains' +import EthereumLogo from 'assets/images/ethereum-logo.png' +export * from '@src/constants/chains' + export enum SupportedChainId { MAINNET = 1, - ROPSTEN = 3, + // ROPSTEN = 3, RINKEBY = 4, - GOERLI = 5, - KOVAN = 42, + // GOERLI = 5, + // KOVAN = 42, XDAI = 100, // ARBITRUM_KOVAN = 144545313136048, // ARBITRUM_ONE = 42161, } +export type ChainInfo = { readonly [chainId in SupportedChainId]: L1ChainInfo & { logoUrl: string } } /* & { + readonly [chainId in SupportedL2ChainId]: L2ChainInfo +} & { readonly [chainId in SupportedL1ChainId]: L1ChainInfo } */ + +export const CHAIN_INFO: ChainInfo = { + /* [SupportedChainId.ARBITRUM_ONE]: { + bridge: 'https://bridge.arbitrum.io/', + docs: 'https://offchainlabs.com/', + explorer: 'https://arbiscan.io/', + infoLink: 'https://info.uniswap.org/#/arbitrum', + label: 'Arbitrum', + logoUrl: arbitrumLogoUrl, + }, + [SupportedChainId.ARBITRUM_RINKEBY]: { + bridge: 'https://bridge.arbitrum.io/', + docs: 'https://offchainlabs.com/', + explorer: 'https://rinkeby-explorer.arbitrum.io/', + infoLink: 'https://info.uniswap.org/#/arbitrum/', + label: 'Arbitrum Rinkeby', + logoUrl: arbitrumLogoUrl, + }, */ + [SupportedChainId.MAINNET]: { + docs: 'https://docs.uniswap.org/', + explorer: 'https://gnosis-protocol.io/mainnet', + infoLink: '', + label: 'Mainnet', + logoUrl: EthereumLogo, + }, + [SupportedChainId.RINKEBY]: { + docs: 'https://docs.uniswap.org/', + explorer: 'https://gnosis-protocol.io/rinkeby', + infoLink: '', + label: 'Rinkeby', + logoUrl: EthereumLogo, + }, + [SupportedChainId.XDAI]: { + docs: 'https://docs.uniswap.org/', + explorer: 'https://gnosis-protocol.io/xdai', + infoLink: '', + label: 'xDai', + logoUrl: EthereumLogo, + }, + /* [SupportedChainId.ROPSTEN]: { + docs: 'https://docs.uniswap.org/', + explorer: 'https://ropsten.etherscan.io/', + infoLink: 'https://info.uniswap.org/#/', + label: 'Ropsten', + }, + [SupportedChainId.KOVAN]: { + docs: 'https://docs.uniswap.org/', + explorer: 'https://kovan.etherscan.io/', + infoLink: 'https://info.uniswap.org/#/', + label: 'Kovan', + }, + [SupportedChainId.GOERLI]: { + docs: 'https://docs.uniswap.org/', + explorer: 'https://goerli.etherscan.io/', + infoLink: 'https://info.uniswap.org/#/', + label: 'Görli', + }, + [SupportedChainId.OPTIMISM]: { + bridge: 'https://gateway.optimism.io/', + docs: 'https://optimism.io/', + explorer: 'https://optimistic.etherscan.io/', + infoLink: 'https://info.uniswap.org/#/optimism/', + label: 'Optimism', + logoUrl: optimismLogoUrl, + }, + [SupportedChainId.OPTIMISTIC_KOVAN]: { + bridge: 'https://gateway.optimism.io/', + docs: 'https://optimism.io/', + explorer: 'https://optimistic.etherscan.io/', + infoLink: 'https://info.uniswap.org/#/optimism', + label: 'Optimistic Kovan', + logoUrl: optimismLogoUrl, + }, */ +} + export const NETWORK_LABELS: { [chainId in SupportedChainId | number]: string } = { [SupportedChainId.MAINNET]: 'Mainnet', [SupportedChainId.RINKEBY]: 'Rinkeby', - [SupportedChainId.ROPSTEN]: 'Ropsten', - [SupportedChainId.GOERLI]: 'Görli', - [SupportedChainId.KOVAN]: 'Kovan', + // [SupportedChainId.ROPSTEN]: 'Ropsten', + // [SupportedChainId.GOERLI]: 'Görli', + // [SupportedChainId.KOVAN]: 'Kovan', [SupportedChainId.XDAI]: 'XDai', // [SupportedChainId.ARBITRUM_KOVAN]: 'kArbitrum', // [SupportedChainId.ARBITRUM_ONE]: 'Arbitrum One', } + +export const ALL_SUPPORTED_CHAIN_IDS: SupportedChainId[] = [ + SupportedChainId.MAINNET, + SupportedChainId.RINKEBY, + SupportedChainId.XDAI, +] diff --git a/src/custom/constants/index.ts b/src/custom/constants/index.ts index 6c06dd871..5ee0e6bb3 100644 --- a/src/custom/constants/index.ts +++ b/src/custom/constants/index.ts @@ -57,9 +57,9 @@ export const NATIVE_CURRENCY_BUY_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeee export const NATIVE_CURRENCY_BUY_TOKEN: { [chainId in ChainId | number]: Token } = { [ChainId.MAINNET]: new Token(ChainId.MAINNET, NATIVE_CURRENCY_BUY_ADDRESS, 18, 'ETH', 'Ether'), [ChainId.RINKEBY]: new Token(ChainId.RINKEBY, NATIVE_CURRENCY_BUY_ADDRESS, 18, 'ETH', 'Ether'), - [ChainId.ROPSTEN]: new Token(ChainId.ROPSTEN, NATIVE_CURRENCY_BUY_ADDRESS, 18, 'ETH', 'Ether'), - [ChainId.GOERLI]: new Token(ChainId.GOERLI, NATIVE_CURRENCY_BUY_ADDRESS, 18, 'ETH', 'Ether'), - [ChainId.KOVAN]: new Token(ChainId.KOVAN, NATIVE_CURRENCY_BUY_ADDRESS, 18, 'ETH', 'Ether'), + // [ChainId.ROPSTEN]: new Token(ChainId.ROPSTEN, NATIVE_CURRENCY_BUY_ADDRESS, 18, 'ETH', 'Ether'), + // [ChainId.GOERLI]: new Token(ChainId.GOERLI, NATIVE_CURRENCY_BUY_ADDRESS, 18, 'ETH', 'Ether'), + // [ChainId.KOVAN]: new Token(ChainId.KOVAN, NATIVE_CURRENCY_BUY_ADDRESS, 18, 'ETH', 'Ether'), [ChainId.XDAI]: new Token(ChainId.XDAI, NATIVE_CURRENCY_BUY_ADDRESS, 18, 'xDAI', 'xDAI'), } @@ -82,17 +82,18 @@ export const CONTRACTS_CODE_LINK = 'https://github.com/gnosis/gp-v2-contracts' export const CODE_LINK = 'https://github.com/gnosis/gp-swap-ui' export const DISCORD_LINK = 'https://chat.cowswap.exchange' export const DUNE_DASHBOARD_LINK = 'https://duneanalytics.com/gnosis.protocol/Gnosis-Protocol-V2' +export const TWITTER_LINK = 'https://twitter.com/mevprotection' // 30 minutes export const GAS_PRICE_UPDATE_THRESHOLD = 30 * 60 * 1000 export const GAS_FEE_ENDPOINTS = { [ChainId.MAINNET]: 'https://safe-relay.gnosis.io/api/v1/gas-station/', // No ropsten = main - [ChainId.ROPSTEN]: 'https://safe-relay.gnosis.io/api/v1/gas-station/', + // [ChainId.ROPSTEN]: 'https://safe-relay.gnosis.io/api/v1/gas-station/', [ChainId.RINKEBY]: 'https://safe-relay.rinkeby.gnosis.io/api/v1/gas-station/', - [ChainId.GOERLI]: 'https://safe-relay.goerli.gnosis.io/api/v1/gas-station/', + // [ChainId.GOERLI]: 'https://safe-relay.goerli.gnosis.io/api/v1/gas-station/', // no kovan = main - [ChainId.KOVAN]: 'https://safe-relay.kovan.gnosis.io/api/v1/gas-station/', + // [ChainId.KOVAN]: 'https://safe-relay.kovan.gnosis.io/api/v1/gas-station/', // TODO: xdai? = main [ChainId.XDAI]: 'https://safe-relay.gnosis.io/api/v1/gas-station/', } diff --git a/src/custom/constants/lists.ts b/src/custom/constants/lists.ts index fe21e3e72..ef6df42df 100644 --- a/src/custom/constants/lists.ts +++ b/src/custom/constants/lists.ts @@ -1,6 +1,8 @@ // used to mark unsupported tokens, these are hosted lists of unsupported tokens import { SupportedChainId as ChainId } from 'constants/chains' +export * from '@src/constants/lists' + export type NetworkLists = { [chain in ChainId]: string[] } @@ -29,10 +31,10 @@ const HONEY_SWAP_XDAI = 'https://tokens.honeyswap.org' export const UNSUPPORTED_LIST_URLS: NetworkLists = { [ChainId.MAINNET]: [BA_LIST], - [ChainId.KOVAN]: [BA_LIST], + // [ChainId.KOVAN]: [BA_LIST], [ChainId.RINKEBY]: [BA_LIST], - [ChainId.ROPSTEN]: [BA_LIST], - [ChainId.GOERLI]: [BA_LIST], + // [ChainId.ROPSTEN]: [BA_LIST], + // [ChainId.GOERLI]: [BA_LIST], [ChainId.XDAI]: [BA_LIST], } @@ -61,22 +63,22 @@ export const DEFAULT_LIST_OF_LISTS_BY_NETWORK: NetworkLists = { GEMINI_LIST, ], }), - [ChainId.KOVAN]: buildNetworkDefaultLists({ - chainId: ChainId.KOVAN, - networkLists: [COMPOUND_LIST], - }), + // [ChainId.KOVAN]: buildNetworkDefaultLists({ + // chainId: ChainId.KOVAN, + // networkLists: [COMPOUND_LIST], + // }), [ChainId.RINKEBY]: buildNetworkDefaultLists({ chainId: ChainId.RINKEBY, networkLists: [RINKEBY_LIST, COMPOUND_LIST], }), - [ChainId.ROPSTEN]: buildNetworkDefaultLists({ - chainId: ChainId.ROPSTEN, - networkLists: [COMPOUND_LIST], - }), - [ChainId.GOERLI]: buildNetworkDefaultLists({ - chainId: ChainId.GOERLI, - networkLists: [COMPOUND_LIST], - }), + // [ChainId.ROPSTEN]: buildNetworkDefaultLists({ + // chainId: ChainId.ROPSTEN, + // networkLists: [COMPOUND_LIST], + // }), + // [ChainId.GOERLI]: buildNetworkDefaultLists({ + // chainId: ChainId.GOERLI, + // networkLists: [COMPOUND_LIST], + // }), [ChainId.XDAI]: buildNetworkDefaultLists({ chainId: ChainId.XDAI, networkLists: [HONEY_SWAP_XDAI], @@ -86,11 +88,11 @@ export const DEFAULT_LIST_OF_LISTS_BY_NETWORK: NetworkLists = { // default lists to be 'active' aka searched across export const DEFAULT_ACTIVE_LIST_URLS_BY_NETWORK: NetworkLists = { [ChainId.MAINNET]: [GEMINI_LIST], - [ChainId.KOVAN]: [GEMINI_LIST], + // [ChainId.KOVAN]: [GEMINI_LIST], [ChainId.RINKEBY]: [RINKEBY_LIST], - [ChainId.ROPSTEN]: [GEMINI_LIST], + // [ChainId.ROPSTEN]: [GEMINI_LIST], [ChainId.XDAI]: [HONEY_SWAP_XDAI], - [ChainId.GOERLI]: [GEMINI_LIST], + // [ChainId.GOERLI]: [GEMINI_LIST], } // Set what we want as the default list when no chain id available: default = MAINNET diff --git a/src/custom/constants/routing/routingMod.ts b/src/custom/constants/routing/routingMod.ts index 8a6fdb022..3a582f358 100644 --- a/src/custom/constants/routing/routingMod.ts +++ b/src/custom/constants/routing/routingMod.ts @@ -1,8 +1,6 @@ // a list of tokens by chain -import { USDC_XDAI, /*USDT_XDAI,*/ WBTC_XDAI, WETH_XDAI } from 'utils/xdai/constants' -import { DAI_RINKEBY, USDC_RINKEBY, USDT_RINKEBY } from 'utils/rinkeby/constants' import { Currency /* , Token */ } from '@uniswap/sdk-core' -// import { SupportedChainId } from 'constants/chains' +import { SupportedChainId } from 'constants/chains' import { // AMPL, DAI, @@ -10,7 +8,6 @@ import { // FEI, // FRAX, // FXS, - // MIR, // renBTC, // TRIBE, // UMA, @@ -21,8 +18,14 @@ import { WBTC, // ETH2X_FLI, WETH9_EXTENDED, + // DAI_OPTIMISM, + // USDT_OPTIMISM, + // WBTC_OPTIMISM, } from 'constants/tokens' +import { USDC_XDAI, /* USDT_XDAI, */ WBTC_XDAI, WETH_XDAI } from 'utils/xdai/constants' +import { DAI_RINKEBY, USDC_RINKEBY, USDT_RINKEBY } from 'utils/rinkeby/constants' + /* type ChainTokenList = { readonly [chainId: number]: Token[] } */ @@ -31,49 +34,19 @@ type ChainCurrencyList = { readonly [chainId: number]: Currency[] } -// List of all mirror's assets addresses. -// Last pulled from : https://whitelist.mirror.finance/eth/tokenlists.json -// TODO: Generate this programmatically ? -/* const mAssetsAdditionalBases: { [tokenAddress: string]: Token[] } = { - [UST.address]: [MIR], - [MIR.address]: [UST], - '0xd36932143F6eBDEDD872D5Fb0651f4B72Fd15a84': [MIR, UST], // mAAPL - '0x59A921Db27Dd6d4d974745B7FfC5c33932653442': [MIR, UST], // mGOOGL - '0x21cA39943E91d704678F5D00b6616650F066fD63': [MIR, UST], // mTSLA - '0xC8d674114bac90148d11D3C1d33C61835a0F9DCD': [MIR, UST], // mNFLX - '0x13B02c8dE71680e71F0820c996E4bE43c2F57d15': [MIR, UST], // mQQQ - '0xEdb0414627E6f1e3F082DE65cD4F9C693D78CCA9': [MIR, UST], // mTWTR - '0x41BbEDd7286dAab5910a1f15d12CBda839852BD7': [MIR, UST], // mMSFT - '0x0cae9e4d663793c2a2A0b211c1Cf4bBca2B9cAa7': [MIR, UST], // mAMZN - '0x56aA298a19C93c6801FDde870fA63EF75Cc0aF72': [MIR, UST], // mBABA - '0x1d350417d9787E000cc1b95d70E9536DcD91F373': [MIR, UST], // mIAU - '0x9d1555d8cB3C846Bb4f7D5B1B1080872c3166676': [MIR, UST], // mSLV - '0x31c63146a635EB7465e5853020b39713AC356991': [MIR, UST], // mUSO - '0xf72FCd9DCF0190923Fadd44811E240Ef4533fc86': [MIR, UST], // mVIXY -} */ -/* const WETH_ONLY: ChainTokenList = { - [SupportedChainId.MAINNET]: [WETH9_EXTENDED[SupportedChainId.MAINNET]], - [SupportedChainId.ROPSTEN]: [WETH9_EXTENDED[SupportedChainId.ROPSTEN]], - [SupportedChainId.RINKEBY]: [WETH9_EXTENDED[SupportedChainId.RINKEBY]], - [SupportedChainId.GOERLI]: [WETH9_EXTENDED[SupportedChainId.GOERLI]], - [SupportedChainId.KOVAN]: [WETH9_EXTENDED[SupportedChainId.KOVAN]], - [SupportedChainId.ARBITRUM_KOVAN]: [WETH9_EXTENDED[SupportedChainId.ARBITRUM_KOVAN]], - [SupportedChainId.ARBITRUM_ONE]: [WETH9_EXTENDED[SupportedChainId.ARBITRUM_ONE]], -} */ +/* const WETH_ONLY: ChainTokenList = Object.fromEntries( + Object.entries(WETH9_EXTENDED).map(([key, value]) => [key, [value]]) +) */ + // used to construct intermediary pairs for trading /* export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = { ...WETH_ONLY, - [1]: [...WETH_ONLY[1], DAI, USDC, USDT, WBTC], -} */ -/* export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = { - [1]: { - ...mAssetsAdditionalBases, + [SupportedChainId.MAINNET]: [...WETH_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC], + [SupportedChainId.OPTIMISM]: [...WETH_ONLY[SupportedChainId.OPTIMISM], DAI_OPTIMISM, USDT_OPTIMISM, WBTC_OPTIMISM], +} +export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = { + [SupportedChainId.MAINNET]: { '0xF16E4d813f4DcfDe4c5b44f305c908742De84eF0': [ETH2X_FLI], - '0xA948E86885e12Fb09AfEF8C52142EBDbDf73cD18': [UNI[1]], - '0x561a4717537ff4AF5c687328c0f7E90a319705C0': [UNI[1]], - '0xE0360A9e2cdd7d03B9408c7D3001E017BAc2EcD5': [UNI[1]], - '0xa6e3454fec677772dd771788a079355e43910638': [UMA], - '0xB46F57e7Ce3a284d74b70447Ef9352B5E5Df8963': [UMA], [FEI.address]: [TRIBE], [TRIBE.address]: [FEI], [FRAX.address]: [FXS], @@ -87,8 +60,8 @@ type ChainCurrencyList = { * tokens. */ /* export const CUSTOM_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = { - [1]: { - [AMPL.address]: [DAI, WETH9_EXTENDED[1]], + [SupportedChainId.MAINNET]: { + [AMPL.address]: [DAI, WETH9_EXTENDED[SupportedChainId.MAINNET]], }, } */ @@ -96,32 +69,66 @@ type ChainCurrencyList = { * Shows up in the currency select for swap and add liquidity */ export const COMMON_BASES: ChainCurrencyList = { - [1]: [/* ExtendedEther.onChain(1), */ DAI, USDC, USDT, WBTC, WETH9_EXTENDED[1]], - [3]: [/* ExtendedEther.onChain(3), */ WETH9_EXTENDED[3]], - [4]: [/* ExtendedEther.onChain(4), */ DAI_RINKEBY, USDC_RINKEBY, USDT_RINKEBY, WETH9_EXTENDED[4]], - [5]: [/* ExtendedEther.onChain(5), */ WETH9_EXTENDED[5]], - [42]: [/* ExtendedEther.onChain(42), */ WETH9_EXTENDED[42]], - [100]: [/* ExtendedEther.onChain(42), */ USDC_XDAI, /*USDT_XDAI,*/ WBTC_XDAI, WETH9_EXTENDED[100], WETH_XDAI], // mod - // [SupportedChainId.ARBITRUM_KOVAN]: [ - // ExtendedEther.onChain(SupportedChainId.ARBITRUM_KOVAN), - // WETH9_EXTENDED[SupportedChainId.ARBITRUM_KOVAN], + [SupportedChainId.MAINNET]: [ + // ExtendedEther.onChain(SupportedChainId.MAINNET), + DAI, + USDC, + USDT, + WBTC, + WETH9_EXTENDED[SupportedChainId.MAINNET], + ], + // [SupportedChainId.ROPSTEN]: [ + // // ExtendedEther.onChain(SupportedChainId.ROPSTEN), + // WETH9_EXTENDED[SupportedChainId.ROPSTEN], + // ], + [SupportedChainId.RINKEBY]: [ + // ExtendedEther.onChain(SupportedChainId.RINKEBY), + WETH9_EXTENDED[SupportedChainId.RINKEBY], + DAI_RINKEBY, + USDC_RINKEBY, + USDT_RINKEBY, + ], + // [SupportedChainId.GOERLI]: [ + // /* ExtendedEther.onChain(SupportedChainId.GOERLI), */ WETH9_EXTENDED[SupportedChainId.GOERLI], + // ], + // [SupportedChainId.KOVAN]: [ + // /* ExtendedEther.onChain(SupportedChainId.KOVAN), */ WETH9_EXTENDED[SupportedChainId.KOVAN], // ], + [SupportedChainId.XDAI]: [ + // ExtendedEther.onChain(SupportedChainId.XDA), + USDC_XDAI, + /*USDT_XDAI,*/ WBTC_XDAI, + WETH9_EXTENDED[100], + WETH_XDAI, + ], // mod // [SupportedChainId.ARBITRUM_ONE]: [ // ExtendedEther.onChain(SupportedChainId.ARBITRUM_ONE), // WETH9_EXTENDED[SupportedChainId.ARBITRUM_ONE], // ], + // [SupportedChainId.ARBITRUM_RINKEBY]: [ + // ExtendedEther.onChain(SupportedChainId.ARBITRUM_RINKEBY), + // WETH9_EXTENDED[SupportedChainId.ARBITRUM_RINKEBY], + // ], + // [SupportedChainId.OPTIMISM]: [ExtendedEther.onChain(SupportedChainId.OPTIMISM)], + // [SupportedChainId.OPTIMISTIC_KOVAN]: [ExtendedEther.onChain(SupportedChainId.OPTIMISTIC_KOVAN)], } // used to construct the list of all pairs we consider by default in the frontend /* export const BASES_TO_TRACK_LIQUIDITY_FOR: ChainTokenList = { ...WETH_ONLY, - [1]: [...WETH_ONLY[1], DAI, USDC, USDT, WBTC], + [SupportedChainId.MAINNET]: [...WETH_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC], } export const PINNED_PAIRS: { readonly [chainId: number]: [Token, Token][] } = { - [1]: [ + [SupportedChainId.MAINNET]: [ [ - new Token(1, '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643', 8, 'cDAI', 'Compound Dai'), - new Token(1, '0x39AA39c021dfbaE8faC545936693aC917d5E7563', 8, 'cUSDC', 'Compound USD Coin'), + new Token(SupportedChainId.MAINNET, '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643', 8, 'cDAI', 'Compound Dai'), + new Token( + SupportedChainId.MAINNET, + '0x39AA39c021dfbaE8faC545936693aC917d5E7563', + 8, + 'cUSDC', + 'Compound USD Coin' + ), ], [USDC, USDT], [DAI, USDT], diff --git a/src/custom/constants/tokens/tokensMod.ts b/src/custom/constants/tokens/tokensMod.ts index 8efba07aa..8f5f6b607 100644 --- a/src/custom/constants/tokens/tokensMod.ts +++ b/src/custom/constants/tokens/tokensMod.ts @@ -1,5 +1,4 @@ import { /* WETH9, */ Token, /* Ether, */ NativeCurrency, Currency } from '@uniswap/sdk-core' -import invariant from 'tiny-invariant' // import { UNI_ADDRESS } from '@src/constants/addresses' import { SupportedChainId } from 'constants/chains' @@ -9,27 +8,112 @@ import { WXDAI, XDAI_NAME, XDAI_SYMBOL } from 'utils/xdai/constants' export * from '@src/constants/tokens' -/* export const AMPL = new Token(1, '0xD46bA6D942050d489DBd938a2C909A5d5039A161', 9, 'AMPL', 'Ampleforth') -export const DAI = new Token(1, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'Dai Stablecoin') -export const USDC = new Token(1, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', 'USD//C') -export const USDT = new Token(1, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT', 'Tether USD') -export const WBTC = new Token(1, '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 8, 'WBTC', 'Wrapped BTC') -export const FEI = new Token(1, '0x956F47F50A910163D8BF957Cf5846D573E7f87CA', 18, 'FEI', 'Fei USD') -export const TRIBE = new Token(1, '0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B', 18, 'TRIBE', 'Tribe') -export const FRAX = new Token(1, '0x853d955aCEf822Db058eb8505911ED77F175b99e', 18, 'FRAX', 'Frax') -export const FXS = new Token(1, '0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0', 18, 'FXS', 'Frax Share') -export const renBTC = new Token(1, '0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D', 8, 'renBTC', 'renBTC') -export const UMA = new Token(1, '0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828', 18, 'UMA', 'UMA Voting Token v1') +/* +export const AMPL = new Token( + SupportedChainId.MAINNET, + '0xD46bA6D942050d489DBd938a2C909A5d5039A161', + 9, + 'AMPL', + 'Ampleforth' +) +export const DAI = new Token( + SupportedChainId.MAINNET, + '0x6B175474E89094C44Da98b954EedeAC495271d0F', + 18, + 'DAI', + 'Dai Stablecoin' +) +export const USDC = new Token( + SupportedChainId.MAINNET, + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + 6, + 'USDC', + 'USD//C' +) +export const USDC_ARBITRUM = new Token( + SupportedChainId.ARBITRUM_ONE, + '0xe865dF68133fcEd7c2285ff3896B406CAfAa2dB8', + 6, + 'USDC', + 'USD//C' +) +export const DAI_OPTIMISM = new Token( + SupportedChainId.OPTIMISM, + '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', + 18, + 'DAI', + 'Dai stable coin' +) +export const USDT = new Token( + SupportedChainId.MAINNET, + '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 6, + 'USDT', + 'Tether USD' +) +export const USDT_OPTIMISM = new Token( + SupportedChainId.OPTIMISM, + '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', + 6, + 'USDT', + 'Tether USD' +) +export const WBTC = new Token( + SupportedChainId.MAINNET, + '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', + 8, + 'WBTC', + 'Wrapped BTC' +) +export const WBTC_OPTIMISM = new Token( + SupportedChainId.OPTIMISM, + '0x68f180fcCe6836688e9084f035309E29Bf0A2095', + 8, + 'WBTC', + 'Wrapped BTC' +) +export const FEI = new Token( + SupportedChainId.MAINNET, + '0x956F47F50A910163D8BF957Cf5846D573E7f87CA', + 18, + 'FEI', + 'Fei USD' +) +export const TRIBE = new Token( + SupportedChainId.MAINNET, + '0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B', + 18, + 'TRIBE', + 'Tribe' +) +export const FRAX = new Token( + SupportedChainId.MAINNET, + '0x853d955aCEf822Db058eb8505911ED77F175b99e', + 18, + 'FRAX', + 'Frax' +) +export const FXS = new Token( + SupportedChainId.MAINNET, + '0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0', + 18, + 'FXS', + 'Frax Share' +) +export const renBTC = new Token( + SupportedChainId.MAINNET, + '0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D', + 8, + 'renBTC', + 'renBTC' +) export const ETH2X_FLI = new Token( - 1, + SupportedChainId.MAINNET, '0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD', 18, 'ETH2x-FLI', 'ETH 2x Flexible Leverage Index' ) -// Mirror Protocol compat. -export const UST = new Token(1, '0xa47c8bf37f92abed4a126bda807a7b7498661acd', 18, 'UST', 'Wrapped UST') -export const MIR = new Token(1, '0x09a3ecafa817268f77be1283176b946c4ff2e608', 18, 'MIR', 'Wrapped MIR') export const UNI: { [chainId: number]: Token } = { [SupportedChainId.MAINNET]: new Token(SupportedChainId.MAINNET, UNI_ADDRESS[1], 18, 'UNI', 'Uniswap'), [SupportedChainId.RINKEBY]: new Token(SupportedChainId.RINKEBY, UNI_ADDRESS[4], 18, 'UNI', 'Uniswap'), @@ -37,6 +121,7 @@ export const UNI: { [chainId: number]: Token } = { [SupportedChainId.GOERLI]: new Token(SupportedChainId.GOERLI, UNI_ADDRESS[5], 18, 'UNI', 'Uniswap'), [SupportedChainId.KOVAN]: new Token(SupportedChainId.KOVAN, UNI_ADDRESS[42], 18, 'UNI', 'Uniswap'), } */ + export const WETH9_EXTENDED: { [chainId: number]: Token } = { ...WETH9_EXTENDED_UNI, [SupportedChainId.XDAI]: WXDAI, @@ -48,9 +133,8 @@ export class GpEther extends NativeCurrency { } public get wrapped(): Token { - const weth9 = WETH9_EXTENDED[this.chainId] - invariant(!!weth9, 'WRAPPED') - return weth9 + if (this.chainId in WETH9_EXTENDED) return WETH9_EXTENDED[this.chainId] + throw new Error('Unsupported chain ID') } private static _etherCache: { [chainId: number]: GpEther } = {} diff --git a/src/custom/hooks/useApproveCallback.ts b/src/custom/hooks/useApproveCallback.ts index 8007f45f3..b2e704553 100644 --- a/src/custom/hooks/useApproveCallback.ts +++ b/src/custom/hooks/useApproveCallback.ts @@ -7,7 +7,7 @@ import { GP_VAULT_RELAYER } from 'constants/index' import TradeGp from 'state/swap/TradeGp' import { ZERO_PERCENT } from 'constants/misc' -export { ApprovalState } from '@src/hooks/useApproveCallback' +export { useApproveCallback, ApprovalState } from '@src/hooks/useApproveCallback' // export function useApproveCallbackFromTrade(trade?: Trade, allowedSlippage = 0) { export function useApproveCallbackFromTrade(trade?: TradeGp, allowedSlippage = ZERO_PERCENT) { diff --git a/src/custom/hooks/useContract.ts b/src/custom/hooks/useContract.ts index 787f032a2..1f8250454 100644 --- a/src/custom/hooks/useContract.ts +++ b/src/custom/hooks/useContract.ts @@ -21,8 +21,8 @@ export function useENSRegistrarContract(withSignerIfPossible?: boolean): Contrac if (chainId) { switch (chainId) { case ChainId.MAINNET: - case ChainId.GOERLI: - case ChainId.ROPSTEN: + // case ChainId.GOERLI: + // case ChainId.ROPSTEN: case ChainId.RINKEBY: address = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e' break diff --git a/src/custom/hooks/useLast/index.ts b/src/custom/hooks/useLast/index.ts new file mode 100644 index 000000000..788417842 --- /dev/null +++ b/src/custom/hooks/useLast/index.ts @@ -0,0 +1,15 @@ +import useLast from '@src/hooks/useLast' + +export * from '@src/hooks/useLast' + +function isDefined(x: T | null | undefined): x is T { + return x !== null && x !== undefined +} + +/** + * Returns the last truthy value of type T + * @param value changing value + */ +export function useLastTruthy(value: T | undefined | null): T | null | undefined { + return useLast(value, isDefined) +} diff --git a/src/custom/hooks/useUSDCPrice/index.ts b/src/custom/hooks/useUSDCPrice/index.ts index 7f0217318..2ba3ab1f4 100644 --- a/src/custom/hooks/useUSDCPrice/index.ts +++ b/src/custom/hooks/useUSDCPrice/index.ts @@ -1,14 +1,18 @@ import { CurrencyAmount, Currency, Price, Token } from '@uniswap/sdk-core' import { useEffect, useMemo, useState } from 'react' -import { unstable_batchedUpdates as batchedUpdate } from 'react-dom' -import { supportedChainId } from 'utils/supportedChainId' +import { SupportedChainId } from 'constants/chains' +/* import { DAI_OPTIMISM, USDC, USDC_ARBITRUM } from '../constants/tokens' +import { useV2TradeExactOut } from './useV2Trade' +import { useBestV3TradeExactOut } from './useBestV3Trade' */ import { useActiveWeb3React } from 'hooks/web3' + +import { supportedChainId } from 'utils/supportedChainId' import { getBestPrice } from 'utils/price' import { STABLECOIN_AMOUNT_OUT as STABLECOIN_AMOUNT_OUT_UNI } from 'hooks/useUSDCPrice' import { stringToCurrency } from 'state/swap/extension' -import { SupportedChainId } from 'constants/chains' import { USDC_XDAI } from 'utils/xdai/constants' import { OrderKind } from 'state/orders/actions' +import { unstable_batchedUpdates as batchedUpdate } from 'react-dom' import { getUSDPriceQuote, toPriceInformation } from 'api/coingecko' import { tryParseAmount } from 'state/swap/hooks' import { DEFAULT_NETWORK_FOR_LISTS } from 'constants/lists' @@ -19,20 +23,53 @@ export * from '@src/hooks/useUSDCPrice' const STABLECOIN_AMOUNT_OUT: { [chainId: number]: CurrencyAmount } = { ...STABLECOIN_AMOUNT_OUT_UNI, + // MOD: lowers threshold from 100k to 100 + [SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(USDC, 100e6), [SupportedChainId.XDAI]: CurrencyAmount.fromRawAmount(USDC_XDAI, 10_000e6), } +/** + * Returns the price in USDC of the input currency + * @param currency currency to compute the USDC price of + */ export default function useUSDCPrice(currency?: Currency) { const [bestUsdPrice, setBestUsdPrice] = useState | null>(null) const [error, setError] = useState(null) const { chainId, account } = useActiveWeb3React() - // USDC constants - // 100_000e6 USDC @ 6 decimals const amountOut = chainId ? STABLECOIN_AMOUNT_OUT[chainId] : undefined const stablecoin = amountOut?.currency + /* + const v2USDCTrade = useV2TradeExactOut(currency, amountOut, { + maxHops: 2, + }) + const v3USDCTrade = useBestV3TradeExactOut(currency, amountOut) + + return useMemo(() => { + if (!currency || !stablecoin) { + return undefined + } + + // handle usdc + if (currency?.wrapped.equals(stablecoin)) { + return new Price(stablecoin, stablecoin, '1', '1') + } + + // use v2 price if available, v3 as fallback + if (v2USDCTrade) { + const { numerator, denominator } = v2USDCTrade.route.midPrice + return new Price(currency, stablecoin, denominator, numerator) + } else if (v3USDCTrade.trade) { + const { numerator, denominator } = v3USDCTrade.trade.route.midPrice + return new Price(currency, stablecoin, denominator, numerator) + } + + return undefined + }, [currency, stablecoin, v2USDCTrade, v3USDCTrade.trade]) + */ + useEffect(() => { const isSupportedChain = supportedChainId(chainId) if (!isSupportedChain || !currency || !amountOut || !stablecoin) return @@ -98,13 +135,16 @@ interface GetPriceQuoteParams { // common logic for returning price quotes function useGetPriceQuote({ price, error, currencyAmount }: GetPriceQuoteParams) { return useMemo(() => { + // if (!price || !currencyAmount) return null if (!price || error || !currencyAmount) return null try { + // return price.quote(currencyAmount) return price.invert().quote(currencyAmount) } catch (error) { return null } + // }, [currencyAmount, price]) }, [currencyAmount, error, price]) } diff --git a/src/custom/i18n.tsx b/src/custom/i18n.tsx index 1ace5257a..8a7895352 100644 --- a/src/custom/i18n.tsx +++ b/src/custom/i18n.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react' +import { useEffect, useState } from 'react' import { i18n } from '@lingui/core' import { I18nProvider } from '@lingui/react' import { ReactNode } from 'react' diff --git a/src/custom/pages/About/index.tsx b/src/custom/pages/About/index.tsx index 4572928d3..4fa9c8a80 100644 --- a/src/custom/pages/About/index.tsx +++ b/src/custom/pages/About/index.tsx @@ -1,6 +1,5 @@ -import React from 'react' import Page, { Title, Content, GdocsListStyle } from 'components/Page' -import styled from 'styled-components' +import styled from 'styled-components/macro' import { Link } from 'react-router-dom' import { ExternalLink as ExternalLinkTheme } from 'theme' diff --git a/src/custom/pages/App/AppMod.tsx b/src/custom/pages/App/AppMod.tsx index 4381922af..820af7a43 100644 --- a/src/custom/pages/App/AppMod.tsx +++ b/src/custom/pages/App/AppMod.tsx @@ -1,38 +1,40 @@ -import React, { Suspense, /* PropsWithChildren, */ ReactNode, useState, useEffect } from 'react' +import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader' +import { Suspense, /* PropsWithChildren, */ ReactNode, useState, useEffect } from 'react' import { Route, Switch, useLocation } from 'react-router-dom' import styled from 'styled-components/macro' import GoogleAnalyticsReporter from 'components/analytics/GoogleAnalyticsReporter' // import AddressClaimModal from '../components/claim/AddressClaimModal' +import ErrorBoundary from 'components/ErrorBoundary' import Header from 'components/Header' import Polling from 'components/Header/Polling' -import URLWarning from 'components/Header/URLWarning' import Popups from 'components/Popups' import Web3ReactManager from 'components/Web3ReactManager' -import ErrorBoundary from 'components/ErrorBoundary' // import { ApplicationModal } from '../../state/application/actions' // import { useModalOpen, useToggleModal } from '../state/application/hooks' import DarkModeQueryParamReader from 'theme' +/* import AddLiquidity from './AddLiquidity' +import { + RedirectDuplicateTokenIds, +} from './AddLiquidity/redirects' +import { RedirectDuplicateTokenIdsV2 } from './AddLiquidityV2/redirects' +import CreateProposal from './CreateProposal' +import Earn from './Earn' +import Manage from './Earn/Manage' +import MigrateV2 from './MigrateV2' +import MigrateV2Pair from './MigrateV2/MigrateV2Pair' +import Pool from './Pool' +import { PositionPage } from './Pool/PositionPage' +import PoolV2 from './Pool/v2' +import PoolFinder from './PoolFinder' +import RemoveLiquidity from './RemoveLiquidity' +import RemoveLiquidityV3 from './RemoveLiquidity/V3' +import Swap from './Swap' +import { OpenClaimAddressModalAndRedirectToSwap, RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects' +import Vote from './Vote' +import VotePage from './Vote/VotePage' +*/ import ReferralLinkUpdater from 'state/affiliate/updater' -// import { -// RedirectDuplicateTokenIds, -// } from './AddLiquidity/redirects' -// import Earn from './Earn' -// import Manage from './Earn/Manage' -// import MigrateV2 from './MigrateV2' -// import MigrateV2Pair from './MigrateV2/MigrateV2Pair' -// import Pool from './Pool' -// import PoolV2 from './Pool/v2' -// import PoolFinder from './PoolFinder' -// import RemoveLiquidity from './RemoveLiquidity' -// import RemoveLiquidityV3 from 'pages/RemoveLiquidity/V3' -// import Swap from 'pages/Swap' -// import { OpenClaimAddressModalAndRedirectToSwap, RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects' -// import Vote from './Vote' -// import VotePage from './Vote/VotePage' -// import { RedirectDuplicateTokenIdsV2 } from './AddLiquidityV2/redirects' -// import { PositionPage } from './Pool/PositionPage' -// import AddLiquidity from './AddLiquidity' -import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader' +import URLWarning from 'components/Header/URLWarning' import Footer from 'components/Footer' import { BodyWrapper } from '.' @@ -64,30 +66,25 @@ const AppWrapper = styled.div>` ${(props) => (props.bgBlur ? '&:after {opacity: 1}' : '&:after {opacity:0}')}; ` -const HeaderWrapper = styled.div` - ${({ theme }) => theme.flexRowNoWrap} - width: 100%; - justify-content: space-between; -` - /* const BodyWrapper = styled.div` display: flex; flex-direction: column; width: 100%; - padding-top: 120px; + padding: 120px 16px 0px 16px; align-items: center; flex: 1; - overflow-y: auto; - overflow-x: hidden; - z-index: 10; + z-index: 1; ${({ theme }) => theme.mediaWidth.upToSmall` - padding: 6rem 16px 0; + padding: 6rem 16px 16px 16px; `}; - - z-index: 1; ` */ +const HeaderWrapper = styled.div` + ${({ theme }) => theme.flexRowNoWrap} + width: 100%; + justify-content: space-between; +` const FooterWrapper = styled(HeaderWrapper)` z-index: 1; ` @@ -114,50 +111,65 @@ export default function App(props?: { children?: ReactNode }) { - - - - -

- - - - {/* */} - - + + + + + +
+ + + + {/* */} + {props && props.children} - {/* + {/* + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + */} - - - - -