From 742f3e54f8f7a5d0ef1c0d59d100c345ff274303 Mon Sep 17 00:00:00 2001 From: "James Morris, MS" <96435344+james-a-morris@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:04:20 -0500 Subject: [PATCH] feat: :sparkling_heart: staking & rewards (#490) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: staking and claim pages Co-authored-by: Tulun Co-authored-by: Dong-Ha Kim * fix: connect onboard in hooks * merge: bring reward page in sync with master (#355) * remove banner (#275) Signed-off-by: Tulun Signed-off-by: Tulun * improve(storybook): add global styles to all preview tabs (#268) * improve: add global styles to all preview tabs * fix: pool removal crashing with small amounts (#274) * improve: Remove debug level logs from API (#271) These logs print every time API is requested, please comment if there are any logs worth keeping * feat: Disable Bridge when REACT_APP_DISABLE_BRIDGE is set to 1 (truthy). (#272) * error shows. Signed-off-by: Tulun * disable send tx and show disabled button. Signed-off-by: Tulun * documentation Signed-off-by: Tulun * slightly better message. Signed-off-by: Tulun * change only in useSendAction Signed-off-by: Tulun * early casting of disableBridge; EOL Signed-off-by: Tulun * force check for 'true' on env Signed-off-by: Tulun * === true Signed-off-by: Tulun Signed-off-by: Tulun * feat: additional delay calc logic configurable by REACT_APP_DEPOSIT_DELAY (#278) * Give time estimate for eth2 Signed-off-by: Tulun * 6-7 mins Signed-off-by: Tulun * fromChain added Signed-off-by: Tulun * env example Signed-off-by: Tulun * start to build func Signed-off-by: Tulun * time range calc. Signed-off-by: Tulun * Add comment. Signed-off-by: Tulun * define calc once. Signed-off-by: Tulun * refactor; adjusted incorrect times Signed-off-by: Tulun * fix wrong key Signed-off-by: Tulun * remove log Signed-off-by: Tulun * Refactor * Update bridge.ts * test log Signed-off-by: Tulun * temp log Signed-off-by: Tulun * seperate logs Signed-off-by: Tulun * refactor * Remove console.logs Signed-off-by: Tulun Co-authored-by: nicholaspai * fix: Disable all routes in API (#270) * fix: Disable all routes in API To prepare for merge, disable all API routes * Revert "fix: Disable all routes in API" This reverts commit 00c08feafc67ed4eb2afd583610ee3bd433c3c8b. * Refactor * Revert "Refactor" This reverts commit 096fd520f975358393bc1e0cc64558fe196df281. * Refactor * Revert "fix: Disable all routes in API (#270)" (#279) This reverts commit 95f0034ec00c26886b0e92359044887a0c1fad9b. * chore(package): Use Across SDK-v2 v0.1.25 (#276) This changes the behaviour of the Coingecko class, which should be primarily visible in the backend/API coingecko endpoint. * fix: port relayer fee logic to API (#256) * feat: add start of api utility to call endpoints * refactor: add fromChain to API calls * feat: add start of api utility to call endpoints * refactor: add fromChain to API calls * feat: replace sdk call with api reference * debug: add temp logging * fix: update fixed point division * feat(api): expose three new fields from response The total fees from the relayFeeCalculator are also dependent on the current price of the token. In most cases, this is a non-issue because ETH is the base price. However, in the singular case of MATIC, the total amount must also be multiplied by the price of MATIC/ETH. To prevent needing this value to be calculated on the FE, this value can be exposed in the JSON payload of /suggested-fees * feat: resolve new data fields from the API * improve: add too low data from api * fix(api): remove errant price cache Before this commit, the cached CG price was being used as a overrided value to the getRelayerfeeDetails. This works for all the chains whose basis currency is ETH. However, this will fail for MATIC, because the price in the PolygonQueries is determined by a basis currency of MATIC. * docs: add relevant comments * fix(test): Add env to mock serverless API * feat: add mock and prod api function designation * chore(across-v2): Bump SDK -> 0.1.26 (#280) - Less logging, and longer timeout on CoinGecko price lookups. * ci: :technologist: chromatic publish on specific label (#281) * feat: speed up modal (#260) * feat: speed up modal * chore: bump sdk to version with speed up support * ui tweaks * improve: hover on data row * fix: zap style tweaks * feat: add basic success content * fix: mobile friendly speed up * refactor: adjust min and max inputs * refactor: tweak input validation * fix: notify speed up on different chain * fix: stats box * fixup * fix: notify singleton usage * fix: replace notify with wait * improve: code quality - usage of closures for formatter - usage of `Boolean` instead of `!!` - usage of `else if` instead of `if` - usage of `weiSafe` * feat: add useNotify hook * fixup * refactor: improve intention fo formatWeiPct * improve: file structure * build: fix from merge commit * feat: Add Header + Footer to storybook; Include solution for react-context. (#288) * Footer story works; Header WIP. Signed-off-by: Tulun * footer only. Signed-off-by: Tulun * Header story works. Signed-off-by: Tulun * wired up controls. Signed-off-by: Tulun * improve: Abstracted useConnection into own hook as it has no ref to redux anymore. Signed-off-by: Tulun * remove ref Signed-off-by: Tulun Signed-off-by: Tulun * fix: remove max relayer fee button (#289) Remove button for now, until better suited function is found. * feat(api): Exit early from debug level logs if boolean is switched on (#291) * feat(api): Exit early from debug level logs if boolean is switched on Vercel logging can be expensive and we're not in control of when clients trigger the API that log. We should be able to turn off non-critical logs with a boolean flip easily * Update _utils.ts * Update _utils.ts * Add lazy loading routes (#259) * fix: change typing of bouncetype (#295) * Close sidebar on click outside (#262) * improve: deleted unused logos (#346) * removed a couple unused logos Signed-off-by: Tulun * more unused icons Signed-off-by: Tulun * more unnec logos Signed-off-by: Tulun Signed-off-by: Tulun * fix: Start separating functions for unit testing (#350) * toWeiSafe works Signed-off-by: Tulun * Addresses can be checked independently; odd import is screwing up other test. Signed-off-by: Tulun * save wip Signed-off-by: Tulun * address.test.ts Signed-off-by: Tulun * update imports Signed-off-by: Tulun * Fixed import issue Signed-off-by: Tulun * test env Signed-off-by: Tulun * removed unnec jest config stuff Signed-off-by: Tulun * add to GH actions Signed-off-by: Tulun * syntax Signed-off-by: Tulun Signed-off-by: Tulun * test: Adding tests for generic hooks (#351) * window size hook Signed-off-by: Tulun * usePrevious test Signed-off-by: Tulun * useScrollPosition Signed-off-by: Tulun * split test Signed-off-by: Tulun Signed-off-by: Tulun * feat: add a way to specify an override provider for the API (#352) * improve: allow canonical linking in deep-link (#336) * feat: :sparkles: crate a mapping of string to numeric chain id * feat: :sparkles: add fns to resolve canonical name * improve: enable canonical name linking in from/to * fix: remove typecast * improve: hoist undefined logic into calling fn * improve: add conditional * improve: add doc comment * fix: resolve build conflict * fix: change useConnection location Signed-off-by: Tulun Co-authored-by: Jason Kiraly Co-authored-by: David A <4429761+daywiss@users.noreply.github.com> Co-authored-by: nicholaspai <9457025+nicholaspai@users.noreply.github.com> Co-authored-by: nicholaspai Co-authored-by: Paul <108695806+pxrl@users.noreply.github.com> Co-authored-by: Dong-Ha Kim Co-authored-by: Anastasios Co-authored-by: Matt Rice * Revert "merge: bring reward page in sync with master (#355)" (#360) This reverts commit 455e95257100b0c4279e620c984ef0482f4d16e7. * fix: merge errors from conflict resolution * swap to andDown (#361) Signed-off-by: Tulun Signed-off-by: Tulun * fix: remove prelaunch tests (#366) * fix: remove prelaunch tests * fix: remove header test for airdrop * fix: remove additional test for opacity * feat: refactor referrals page and create referral section (#365) * feat: add breadcrumb component * fix: update style * style: change directory of referral to match usage * feat: add new icons * feat: add format fn which condensed to a fixed char # * feat: create copy referral link component * feat: create hook which generates a referral link * improve: finish renaming * feat: create referral section * feat: add new rewards to route * Update src/views/Referrals/Referrals.tsx Co-authored-by: Dong-Ha Kim * Update src/views/Referrals/Referrals.tsx Co-authored-by: Dong-Ha Kim * Update src/views/index.ts Co-authored-by: Dong-Ha Kim * feat: update reward cypress to referral Co-authored-by: Dong-Ha Kim * feat: add overview section to reward page (#367) * improve: update logic to a more efficient state * feat: add banners * feat: add reward assets * feat: add overview reward section * improve: include overview in reward page * feat: add API integration to referral section (#368) * fix: error on load with completed * feat: add API integration to referral slide * fix: remove log * improve: update the views of the staking page (#369) * feat: hoist sectionw rapper to general component * improve: update views * feat: use alert previously defined * improve: attempt to disable claim and test * improve: add additional features to breadcrumb * improve: integrate breadcrumb into staking exit * feat: add wrapping * fix: remove console log * feat: post launch airdrop splash and info (#371) * feat: create airdrop banner (#372) * showAirdrop Banner Signed-off-by: Tulun * feat: add animated banner to non-airdrop pages * fix: remove unnecessary query var * fix: update mobile * fix: change acx overlay * fix: update background layer * fix: remove state variable Signed-off-by: Tulun Co-authored-by: Tulun * feat: :iphone: add mobile breakpoints to rewards page (#370) * improve: add responsive UI to summary referral * improve: add breakpoints to breadcrumb * feat: add breakpoints to reward UI * improve: use Text component * improve: add mobile UI to disconnected wallet state (#373) * feat: post-launch view-only eligible flow (#374) * feat: post-launch view-only eligible flow * add mocks * review requests - use hsla instead of hex - use alias for aqua and error * improve: update layout to Figma doc (#375) * improve: update reward banner * improve: fix connected state logic * fix: remove margin * improve: moved fixed constant to constants * improve: update layout of referral page * fix: update section wrapper * fix: update queries * fix: update gap * improve: add cypress referral link * build: fix local storybook (#378) * feat: ineligible wallet flow (#376) * feat: ineligible wallet flow * add story * feat: airdrop claim scraper and contracts integration (#382) * feat: airdrop claim scraper and contracts integration * refactor: split contract read and call into separate hooks * fixup * feat: :sparkles: introduce generic staking table (#380) * improve: hoist table to component library * feat: begin adding table logic to rewards page * improve: clip overflow * fix: bold text * improve: add formatter * improve: add key * improve: update bg color * improve: update base type and style * improve: update font weight * feat: add generic staking table * feat: :sparkles: introduce generic staking table This change allows the user to add a generic table for staking pools. This component is able to be reused for both the "my pools" and the "all pools" tables. Depending on the input row, the table will display differnet buttons & indicate staking status through different font colors * fix: update bug * fix: additional bugs * improve: enforce empty message * improve: update section header * improve: convert to functional components * feat: split up staking pool hooks (#386) * feat: split up staking pool hooks * build: fix * add goerli routes for testability * add flag for determining is user staked * remove hardcoded tokenlist id * use constant hubPoolChainId * feat: create splash page for across (#389) * feat: improve routes * feat: introduce additional icons * improve: add additional global component options * feat: create splash page * fix: update e2e tests * feat: dynamic staking pool data `/rewards/staking/` (#390) * feat: dynamic staking pool data `/rewards/staking/` * fix connect wallet on staking * use nullish coalescing operator * fix: :art: correct UI bugs (#393) * fix: remove bottom margin * feat: add footer * fix: :lipstick: remove click cursor from stepper (#392) This change resolves ACX-202. When hovering the Referral steps you should just see the regular cursor (not the click cursor) * improve: static reward UI (#394) * improve: update col order & tool tips * feat: add max reward APY * feat: include new transfer & remove tooltips * improve: correct referral data * improve: update text sizing * fix: remove forced banner from demo (#396) * feat: :sparkles: create env to disable ACX token balance in navbar (#397) This change now refers to an environment variable to dynamically hide or display the ACX token balance in the navbar. * feat: dynamic staking pool tables (#399) * feat: dynamic staking pool data `/rewards/staking/` * feat: integrate staking pool tables with hooks * improve: correct mobile UI * improve: use generic StakingPool type * feat: configure max and total apy percentage * fix: use max multiplier Co-authored-by: Dong-Ha Kim * feat: claim-flow UI updates (#398) * improve: claim flow ui updates * add links * review requests * feat: include dynamic data on splash screen (#411) * feat: add numerical package * feat: add api integration for static data * feat: add human readable parser function * feat: integrate query into splash * improve: replace lorem ipsum text on splash * feat: add dynamic integration to reward-overview-segment (#415) * feat: make reward data dynamic * fix: enable loading on zero values * feat: add dynamic data to sum of lp staked values * feat: fix base rewards apy (#414) * add acx goerli to routes * add base rewards apr utility * integrate correct values to airdrop claim flow * fixup * adjust formatWeiPct * feat: claim referrals modal (#416) * refactor: use portal for `` component * feat: claim referrals modal * fixup * fix: notify on goerli * refactor: use common hook * Update .env.example Co-authored-by: James Morris <96435344+james-a-morris@users.noreply.github.com> * dynamically get copper referral rate Co-authored-by: James Morris <96435344+james-a-morris@users.noreply.github.com> * improve: align staking page with figma (#418) * feat: add dynamic data to stake page * improve: modify tooltips * improve: update to new text component * fix: update UI with mobile values * improve: refactor to use poolData * improve: update display of percent values * add new logic * feat: add dynamic data to staking page * fix: wrong network only can be true if connected * fix: additional fixed point adjustment * feat: add optional timing buffer * fix: await before ending mutation * improve: align with Figma doc * fix: errant unit test * fix: remove unneeded import * improve: resolved 86400 into a constant * docs: add comments * fix: remove provider & add setChain * fix: update warning text * fix: lodash * fix: lodash * improve: include ability to import token into wallet (#423) * fix: remove nth-child error * fix: prevent screen lock unless modal is opened * feat: create hook to import tokens into wallet * Allow user to import token in modal * fix: :bug: various reward related bugs (#428) * fix: disable input on load * fix: immediately resolve onboard at start * refactor: :fire: remove connection state * fix: disable automatic notification service by onboard * improve: allow loading state for splash * feat: trigger submission of valid input on enter * fix: remove log * feat: :sparkles: Add dynamic sub-text for user's claimable acx amount (#424) * feat: add clock image icon * feat: enable clock icon for tooltip * feat: :sparkles: Add dynamic sub-text for user's claimable acx amount This change adds a dynamic amount to the referral page which indicates how much a user has claimable to them at a given moment. * feat: include claimable rewards * nit: change variable name * fix: referral page broke for tier 5 referrals (#432) * feat: :sparkles: add claiming feature to staking (#429) * improve: add additional option to bouncing loader * fix: include warning color * feat: :sparkles: add claiming feature to staking This change enables the claiming contract call to the AcceleratingDistributor contract. This has been tested via this transaction: https://goerli.etherscan.io/tx/0xcb06d8ea5f5b1c7d68f35663ca5e3764028b758f0bd366b955e4ca0abef3e210 N/A * improve: remove closure * feat: adjust claim to new contracts + claimAndStake integration (#431) * feat: claimAndStake + new contracts * refactor: move query key to function * bump across-toke package * feat: :bug: show claim alert in all connection states (#433) This change will let the claiming alert on the staking page be visible if the user is not connected. * improve: update to staking page and header (#434) * improve: update mobile padding * fix: update mobile UI * fix: change airdrop header link to aqua * feat: enable usdc, dai, weth, and acx on goerli (#436) * improve: dynamic apy on user input and hidden pool when disconnected (#439) * fix: ways to earn link and apy (#441) * fix: ways to earn link and apy - scroll "My Pools" into view if link hash exists - remove tooltips - pass apy prop * fix: airdrop details links * fix: linting * use replaceAll * feat: :sparkles: allow pool deep linking (#440) * improve: add button links to pool with query param * feat: :sparkles: allow pool deep linking This change enables deep linking on the pool page. A user can now specify the specific symbol that will be loaded by using the ?symbol={symbol} parameter. This parameter is case-insensitive and represents a token symbol such as ETH, WETH, UDSC, ACX, etc. This check is only performed at render time and is intended to be leveraged by the Rewards page to link the `Add` button to a specific pool. Breaking Changes: N/A * fix: old layout logos (#442) * fix: :bug: show base reward if APY if user's claim is 0 (#443) * fix: :bug: show base reward if APY if user's claim is 0 This change reverts a previous change to show the base APY in the staking reward claim dialog alert. * fix: remove additional test * fix: :bug: disable acx banner link on click (#446) * improve: update splash screen copy per figma (#445) * feat: refetch pool on tx success (#448) * fix: base rewards apy calculation and test (#444) * fix: base rewards apy calculation and test * fix: use usd as APR base * refactor: use coingecko api * put acx price into variable * fix: splash page typos * feat: about accordion in sidebar (#447) * feat: group about links in sidebar * chore: remove obsolete about page * fixup * remove /about route from sitemap * fix: apy for staking pools with 0 stake (#449) * fix: age of capital with 2 decimal days (#451) * fix(claim): use max apy of all pools + external links (#450) * fix: :bug: set the pool ordering to the token list (#456) This change updates the sorting order of the "My Pools" tab such that it appears in the same order as the token list (pool page) * improve(splash): remove clipping & overlap of contour (#460) * improve: bug fixes for staking page (#462) * fix: add custom data until acx token is on CG * improve: add functionality to ignore notify errors * improve: recompute how avgDeposit time is calculated * fix: prevent rejection errors * fix: transition query from mutation to refetch * improve: update copy of alert for claim * fix: remove unneeded import * fix: remove hardcoded values * fix: :bug: disable claim referral button when user doesnt have claim (#455) This change disables the `claim rewards` button on the referral page when the user does not have any ACX referral rewards to claim Breaking Changes: N/A * feat: :art: update bridge traveler airdrop copy (#457) This change updates the bridge traveler copy on the vertical airdrop card. Breaking Changes: N/A * fix: :speech_balloon: update multiplier text (#461) This change updates the Multiplier tooltip on the staking page from the original ```The multiplier is the amount of LP tokens you get for staking.``` to the new ```Your multiple applied to the pool’s base reward APY, determined by your age of capital.``` Breaking Changes: N/A * improve: :sparkles: introduce multiple UI fixes (#464) * fix: :zap: hide specific pool table if no elements are available This change selectively hides certain pool tables if the loading has completed and there are no pool entries, the table is hidden * fix: temporarily provide a default acx price * fix: :bug: Add a decimal place to the multiplier staking field * fix: :speech_balloon: update copy on splash screen This change aligns the minimum transfer time on splash screen to the dynamic data * feat: :sparkles: preserve position size when user stakes This change allows the position size of the user's LP pool balance to remain the same even when they stake. Currently, when a user stakes with the AcceleratingDistributor contract their tokens are transferred to the contract - this reduces the number of tokens visible on the pool page. This change accounts for this difference. Breaking Changes: N/A * fix: make code context-aware of cypress * improve: bundle acxprice loading into pool query * feat: a lookup address to price goerli assets * fix: enforce a string from coingecko api * feat: accurately compute exchange price of tokens * fix: preserve EOF * fix: remove unneeded space * improve: change Discourse to Forum on sidebar (#454) * fix: :bug: revert logic to provided transitions (#468) With this change, the baseReward API is no longer changed on input * feat: add mainnet contract addresses (#469) * improve: update landing page content (#467) * fix: :bug: utilize a more accurate call to oustanding rewards (#471) This change adds an additional RPC call which resolves a user's outstanding reward balance each time. The initial usage of `getUserStake` for resolving the current user's balance was not effective because it only updated on stake action. Breaking changes: N/A * feat: :bug: update logic for staking button (#472) This change now lets the user visit the staking page if they have more than 0 acx in outstanding rewards * fix: :bug: disable remove liquidity if new amount is 0 (#474) * fix: :bug: disable remove liquidity if new amount is 0 This change disables the option to click remove liquidity when the amount to remove is zero. Breaking Changes: N/A * fix: remove additional import * fix: lint issue * improve: update splash to align with QA (#477) * improve: make nav transparent at the top of page * fix: add antialiasing to global style * fix: resolve acx banner qa * improve: add transition state to card hover * improve: set spacing and qa requirements * fix: top-align card text * fix: update tablet sizing for splash * fix: change header * improve: normalize footer (#476) * fix(api): :bug: a non-max unstake doesn't change dynamic pool data (#473) This change accounts for the fact that when a user unstakes the non-maximum value, then the values displayed on their dynamic apy data should not change on the staking page. Breaking Changes: N/A * improve: close modal when no more claim (#475) * fix: delay to remove the zeroed values on unstake * improve: close modal when no more claim * fix: prefer mutate syntax * fix(qa): update referral pages (#481) * fix: change color of loading state * fix: change background to across dark grey * fix: change casing on learn more text * fix: increase size of progress bar * fix: format staking lengths * feat: add CTA below pool table * fix: disable capitalization * fix: reduce throttle on viewpoint * fix: update padding * fix: center the items in the generic referral box * fix: hide pagination when table is empty * fix: resolve border radius * fix: allow entire area to be clickable * fix: update logo (#486) * fix: reintroduce bg mesh on splash screen (#487) * feat: display "unprofitable" for pending transactions (#485) * feat: display "unprofitable" for pending transactions * refactor: add parameterized thresholds * fix: :zap: replaces backup contract addresses with mainnet as default (#496) This change sets the mainnet contract addresses for the AD, ACX token, Merkle Distributor, and the Claim & Stake as the default addresses if a valid config is not provided. Breaking changes: this should not break anything. We have set this edge case for the event that the config file is invalid. * fix: :bug: update constants for token address (#497) Signed-off-by: Tulun Co-authored-by: Tulun Co-authored-by: Dong-Ha Kim Co-authored-by: David A <4429761+daywiss@users.noreply.github.com> Co-authored-by: nicholaspai <9457025+nicholaspai@users.noreply.github.com> Co-authored-by: nicholaspai Co-authored-by: Paul <108695806+pxrl@users.noreply.github.com> Co-authored-by: Anastasios Co-authored-by: Matt Rice --- .env.example | 15 + .storybook/main.js | 12 +- api/_utils.ts | 79 ++-- api/coingecko.ts | 19 +- cypress/e2e/bridge.cy.ts | 2 +- cypress/e2e/header.cy.ts | 21 +- cypress/e2e/prelaunch.cy.ts | 27 -- .../e2e/{rewards.cy.ts => referrals.cy.ts} | 4 +- cypress/e2e/wallet.cy.ts | 2 +- package.json | 12 +- public/index.html | 15 +- public/logos/acx-logo.svg | 6 +- public/logos/wbtc-logo.svg | 15 +- public/sitemap.xml | 1 - src/Routes.tsx | 81 ++-- src/assets/acx.svg | 4 + src/assets/airdrop-gift-bg.svg | 22 + src/assets/airdrop-waves-bg.svg | 15 + src/assets/airdrop-x.svg | 4 + src/assets/bg-banners/blue-card-banner.svg | 18 + src/assets/bg-banners/green-card-banner.svg | 18 + src/assets/bg-banners/purple-card-banner.svg | 18 + src/assets/bg-banners/stars-bg.png | Bin 0 -> 50427 bytes src/assets/check-star-ring-filled.svg | 4 + src/assets/check-star-ring.svg | 9 + src/assets/claim-heart-wave.svg | 28 ++ src/assets/claim-pie-chart-wave.svg | 27 ++ src/assets/eth-white.svg | 9 + src/assets/gem.svg | 28 ++ src/assets/icons/chevron-left-vector.svg | 3 + src/assets/icons/chevron-with-circle.svg | 18 + src/assets/icons/clock.svg | 11 + src/assets/icons/graph-24.svg | 5 + src/assets/icons/heart-crack.svg | 4 + src/assets/icons/help-24.svg | 5 + src/assets/icons/increase-24.svg | 6 + src/assets/icons/megaphone-24.svg | 4 + .../icons/rewards/graph-within-star.svg | 4 + src/assets/icons/rewards/logo-within-star.svg | 4 + .../icons/rewards/referral-within-star.svg | 7 + src/assets/icons/transfer-1-24.svg | 7 + src/assets/icons/transfer-24.svg | 6 + src/assets/icons/trophy-24.svg | 6 + src/assets/icons/usdc-24.svg | 5 + src/assets/icons/usdc-green-16.svg | 5 + src/assets/icons/wallet-24.svg | 5 + src/assets/ineligible-wallet-ring.svg | 4 + src/assets/multi-users.svg | 28 ++ src/assets/piggy-bank.svg | 16 + src/assets/plus-star-ring.svg | 11 + src/assets/pool-star-ring.svg | 4 + src/assets/referral-star-ring.svg | 7 + src/assets/shield-check.svg | 16 + src/assets/splash-mesh-bg.svg | 42 ++ src/assets/x-star-ring.svg | 4 + src/assets/zap.svg | 10 + .../ACXLiveBanner/ACXLiveBanner.tsx | 180 ++++++++ src/components/ACXLiveBanner/index.ts | 1 + src/components/Alert/Alert.styles.ts | 14 + src/components/Alert/Alert.tsx | 2 +- src/components/Banner/Banner.styles.tsx | 1 - .../BouncingDotsLoader/BouncingDotsLoader.tsx | 24 +- src/components/BreadcrumbV2/BreadcrumbV2.tsx | 92 ++++ src/components/BreadcrumbV2/index.ts | 1 + src/components/BreadcrumbV2/useBreadcrumb.ts | 26 ++ .../CopyReferralLink/CopyReferralLink.tsx | 118 ++++++ src/components/CopyReferralLink/index.ts | 1 + src/components/ExternalLink/ExternalLink.tsx | 47 +++ src/components/ExternalLink/index.ts | 1 + src/components/Footer/Footer.tsx | 2 +- src/components/GlobalStyles/GlobalStyles.tsx | 4 + src/components/Header/Header.styles.tsx | 12 + src/components/Header/Header.tsx | 12 +- src/components/IconPair/IconPair.tsx | 40 ++ src/components/IconPair/index.ts | 1 + src/components/Layout/Layout.tsx | 133 +----- src/components/LayoutV2/LayoutV2.tsx | 57 +++ src/components/LayoutV2/index.ts | 2 + src/components/Loader/Loader.tsx | 23 + src/components/Loader/index.tsx | 1 + src/components/Modal/Modal.tsx | 57 ++- src/components/PoolForm/AddLiquidityForm.tsx | 4 + src/components/PoolForm/PoolForm.tsx | 12 +- .../PoolForm/RemoveLiquidityForm.tsx | 8 +- .../PoolSelection/PoolSelection.tsx | 1 + .../RewardTable/RewardTable.tsx | 18 +- .../RewardTable/RewardTables.styles.tsx | 23 +- .../comp => components}/RewardTable/index.ts | 0 src/components/ScrollToTop/ScrollToTop.tsx | 14 + src/components/ScrollToTop/index.ts | 1 + .../SectionWrapperV2.tsx | 89 ++++ src/components/SectionTitleWrapperV2/index.ts | 1 + src/components/Sidebar/Sidebar.styles.tsx | 11 + src/components/Sidebar/Sidebar.tsx | 47 ++- src/components/Sidebar/useSidebar.ts | 120 +++--- src/components/Stepper/Stepper.styles.tsx | 2 +- src/components/Table/Table.d.ts | 1 + src/components/Table/Table.styles.tsx | 10 - src/components/Text/Text.tsx | 111 +++++ src/components/Text/index.ts | 1 + src/components/Tooltip/Tooltip.tsx | 5 +- src/components/Wallet/Wallet.tsx | 12 +- src/components/index.ts | 2 + ...6fA914353c44b2E33eBE05f21846F1048bEda.json | 4 + ...A832B994f796452e4FaF191a041F791AD8A0A.json | 60 ++- src/hooks/index.ts | 2 + src/hooks/useCoingeckoPrice.ts | 8 + src/hooks/useCurrentBreakpoint.ts | 2 +- src/hooks/useIsWrongNetwork.ts | 36 ++ src/hooks/useOnboard.tsx | 7 +- src/hooks/useReferralLink.ts | 28 ++ src/hooks/useScrollElementByHashIntoView.ts | 22 + src/hooks/useStakingPool.ts | 396 ++++++++++++++++++ src/hooks/useWalletTokenImport.ts | 74 ++++ src/sitemap-routes.js | 1 - src/stories/BouncingDotsLoader.stories.tsx | 2 +- .../IconWithCheck.stories.tsx} | 19 +- .../airdrop/NotEligibleWalletFlow.stories.tsx | 20 + src/stories/airdrop/SplashFlow.stories.tsx | 23 + .../airdrop/VerticalCardsList.stories.tsx | 68 +++ src/stories/plaap/CardContent.stories.tsx | 146 ------- src/stories/plaap/SplashFlow.stories.tsx | 51 --- src/stories/plaap/TitleSegment.stories.tsx | 33 -- src/stories/plaap/TravellerFlow.stories.tsx | 26 -- .../plaap/VerticalCardsList.stories.tsx | 73 ---- src/utils/bridge.ts | 5 +- src/utils/config.ts | 59 +++ src/utils/constants.ts | 52 ++- src/utils/convertdecimals.ts | 18 + src/utils/format.ts | 103 ++++- src/utils/index.ts | 1 + src/utils/math.ts | 46 ++ src/utils/merkle-distributor.ts | 79 ++++ src/utils/notify.ts | 39 +- src/utils/onboard.ts | 9 +- src/utils/query-keys.ts | 11 + src/utils/rewards.ts | 153 +++++++ .../serverless-api/mocked/coingecko.mocked.ts | 12 + .../mocked/get-deposit-stats.mocked.ts | 11 + src/utils/serverless-api/mocked/index.ts | 7 + src/utils/serverless-api/prod/coingecko.ts | 26 ++ .../prod/get-deposit-stats.prod.ts | 15 + src/utils/serverless-api/prod/index.ts | 6 + src/utils/serverless-api/types.ts | 13 + src/utils/ternary.ts | 24 ++ src/utils/tests/rewards.test.ts | 30 ++ src/utils/types.ts | 14 + src/views/About.tsx | 314 -------------- .../Airdrop.styles.tsx} | 57 +++ src/views/Airdrop/Airdrop.tsx | 88 ++++ src/views/Airdrop/components/BreakdownRow.tsx | 46 ++ .../Airdrop/components/BreakdownStats.tsx | 223 ++++++++++ src/views/Airdrop/components/ClaimAirdrop.tsx | 123 ++++++ .../Airdrop/components/EarnOptionCard.tsx | 107 +++++ .../Airdrop/components/EligibleWalletFlow.tsx | 200 +++++++++ .../components/IconWithCheck.tsx} | 47 +-- src/views/Airdrop/components/InfoCard.tsx | 74 ++++ src/views/Airdrop/components/InfoCardTop.tsx | 84 ++++ src/views/Airdrop/components/MoreInfoFlow.tsx | 142 +++++++ .../components/NotEligibleWalletFlow.tsx | 76 ++++ src/views/Airdrop/components/Pill.tsx | 26 ++ src/views/Airdrop/components/SplashFlow.tsx | 128 ++++++ src/views/Airdrop/components/StepCard.tsx | 155 +++++++ .../components/VerticalCardsList.tsx | 0 src/views/Airdrop/components/WalletHero.tsx | 47 +++ src/views/Airdrop/components/WaysToEarn.tsx | 42 ++ src/views/Airdrop/hooks/useAirdrop.ts | 67 +++ .../Airdrop/hooks/useAirdropRecipient.tsx | 12 + src/views/Airdrop/hooks/useClaimAndStake.tsx | 46 ++ .../Airdrop/hooks/useIsAirdropClaimed.tsx | 30 ++ src/views/Airdrop/index.ts | 1 + src/views/Claim/Claim.styles.tsx | 15 - src/views/Claim/Claim.tsx | 10 - src/views/Claim/index.ts | 1 - src/views/DiscordAuth/DiscordAuth.tsx | 2 +- src/views/Pool.tsx | 34 +- .../PreLaunchAirdrop/PreLaunchAirdrop.tsx | 85 ---- .../components/AirdropCard.tsx | 134 ------ .../components/CardContent.tsx | 162 ------- .../components/EligibilityPill.tsx | 33 -- .../components/MoreInfoFlow/MoreInfoFlow.tsx | 163 ------- .../components/MoreInfoFlow/index.ts | 1 - .../components/SplashFlow/SplashFlow.tsx | 136 ------ .../components/SplashFlow/index.ts | 1 - .../components/TitleSection.tsx | 175 -------- .../TravellerFlow/TravellerFlow.tsx | 2 +- .../components/cards/BridgeTravelerCard.tsx | 118 ------ .../components/cards/BridgeUserCard.tsx | 73 ---- .../components/cards/CommunityRewardCard.tsx | 183 -------- .../cards/LiquidityProviderCard.tsx | 94 ----- .../content/CardTextDescription.tsx | 22 - .../PreLaunchAirdrop/components/index.ts | 6 - .../hooks/useDiscordDetails.ts | 78 ---- .../PreLaunchAirdrop/hooks/useDiscordQuery.ts | 20 - .../hooks/usePreLaunchAirdrop.ts | 60 --- src/views/PreLaunchAirdrop/index.ts | 1 - src/views/Referrals/Referrals.styles.tsx | 51 +++ src/views/Referrals/Referrals.tsx | 66 +++ .../Referrals/comp/ClaimRewardsModal.tsx | 123 ++++++ .../ConnectTableOverlay.styles.tsx | 0 .../ConnectTableOverlay.tsx | 0 .../comp/ConnectTableOverlay/index.ts | 0 .../RewardBreakdown.styles.tsx | 0 .../comp/RewardBreakdown/RewardBreakdown.tsx | 0 .../comp/RewardBreakdown/index.ts | 0 .../comp/RewardHero/RewardHero.styles.tsx | 0 .../comp/RewardHero/RewardHero.tsx | 0 .../comp/RewardHero/index.ts | 0 .../RewardMediumBlock.styles.tsx | 0 .../RewardMediumBlock/RewardMediumBlock.tsx | 0 .../comp/RewardMediumBlock/index.ts | 0 .../RewardReferral/RewardReferral.styles.tsx | 329 +++++++++++++++ .../comp/RewardReferral/RewardReferral.tsx | 351 ++++++++++++++++ .../comp/RewardReferral/index.ts | 0 .../RewardTable/createMyReferralsTableJSX.tsx | 2 +- .../RewardTableWithOverlay.styles.tsx | 1 + .../RewardTableWithOverlay.tsx | 6 +- .../comp/RewardTableWithOverlay/index.ts | 0 .../StepperWithTooltips.styles.tsx | 7 +- .../StepperWithTooltips.tsx | 0 .../comp/StepperWithTooltips/index.ts | 0 .../useStepperWithTooltips.tsx | 0 .../{Rewards => Referrals}/comp/index.ts | 2 +- src/views/Referrals/hooks/useClaimModal.tsx | 16 + .../hooks/useClaimReferralRewards.tsx | 43 ++ .../hooks/useUnclaimedReferralProofs.tsx | 57 +++ src/views/Referrals/index.ts | 1 + .../useReferralsView.ts} | 5 +- src/views/Rewards/Rewards.style.tsx | 46 ++ src/views/Rewards/Rewards.styles.tsx | 104 ----- src/views/Rewards/Rewards.tsx | 125 ++++-- .../RewardReferral/RewardReferral.styles.tsx | 338 --------------- .../comp/RewardReferral/RewardReferral.tsx | 270 ------------ .../RewardTable/createAllPoolsTableJSX.tsx | 153 ------- .../RewardTable/createMyPoolsTableJSX.tsx | 154 ------- .../components/AdditionalQuestionCTA.tsx | 67 +++ .../components/ConnectedReferralBox.tsx | 217 ++++++++++ .../components/DisconnectedReferralBox.tsx | 147 +++++++ .../GenericStakingPoolFormatter.tsx | 224 ++++++++++ .../GenericStakingPoolTable.styles.tsx | 124 ++++++ .../GenericStakingPoolTable.tsx | 33 ++ .../GenericStakingPoolTable/index.ts | 1 + .../components/OverviewRewardSection.tsx | 207 +++++++++ src/views/Rewards/hooks/useRewards.ts | 68 +++ src/views/Rewards/hooks/useStakingPools.tsx | 23 + src/views/Splash/Splash.styles.tsx | 178 ++++++++ src/views/Splash/Splash.tsx | 73 ++++ src/views/Splash/components/CardBenefit.tsx | 82 ++++ .../Splash/components/NumericBenefit.tsx | 39 ++ src/views/Splash/hooks/useSplash.ts | 61 +++ .../Splash/hooks/useSplashDynamicData.ts | 22 + src/views/Splash/index.ts | 1 + src/views/Staking/Staking.styles.tsx | 54 +++ src/views/Staking/Staking.tsx | 63 +++ .../components/ConnectWalletButton.tsx | 43 ++ .../StakingExitAction.styles.tsx | 30 ++ .../StakingExitAction/StakingExitAction.tsx | 24 ++ .../components/StakingExitAction/index.tsx | 1 + .../StakingForm/StakingForm.styles.tsx | 175 ++++++++ .../components/StakingForm/StakingForm.tsx | 283 +++++++++++++ .../Staking/components/StakingForm/index.ts | 1 + .../StakingInputBlock.styles.tsx | 142 +++++++ .../StakingInputBlock/StakingInputBlock.tsx | 86 ++++ .../components/StakingInputBlock/index.ts | 1 + .../StakingReward/StakingReward.tsx | 129 ++++++ .../Staking/components/StakingReward/index.ts | 1 + src/views/Staking/components/index.ts | 3 + .../hooks/useClaimStakeRewardAction.ts | 47 +++ src/views/Staking/hooks/useStakeFormLogic.ts | 74 ++++ src/views/Staking/hooks/useStakingAction.ts | 125 ++++++ src/views/Staking/hooks/useStakingView.ts | 51 +++ src/views/Staking/index.ts | 1 + src/views/Staking/types.ts | 22 + .../components/SpeedUpModal/SpeedUpModal.tsx | 2 +- .../TransactionsTable/cells/StatusCell.tsx | 16 +- .../TransactionsTable/rows/DataRow.tsx | 6 +- .../TransactionsTable/rows/MobileDataRow.tsx | 6 +- src/views/Transactions/utils.ts | 21 +- src/views/index.ts | 6 +- yarn.lock | 312 +++++++++++++- 280 files changed, 9462 insertions(+), 3765 deletions(-) delete mode 100644 cypress/e2e/prelaunch.cy.ts rename cypress/e2e/{rewards.cy.ts => referrals.cy.ts} (84%) create mode 100644 src/assets/acx.svg create mode 100644 src/assets/airdrop-gift-bg.svg create mode 100644 src/assets/airdrop-waves-bg.svg create mode 100644 src/assets/airdrop-x.svg create mode 100644 src/assets/bg-banners/blue-card-banner.svg create mode 100644 src/assets/bg-banners/green-card-banner.svg create mode 100644 src/assets/bg-banners/purple-card-banner.svg create mode 100644 src/assets/bg-banners/stars-bg.png create mode 100644 src/assets/check-star-ring-filled.svg create mode 100644 src/assets/check-star-ring.svg create mode 100644 src/assets/claim-heart-wave.svg create mode 100644 src/assets/claim-pie-chart-wave.svg create mode 100644 src/assets/eth-white.svg create mode 100644 src/assets/gem.svg create mode 100644 src/assets/icons/chevron-left-vector.svg create mode 100644 src/assets/icons/chevron-with-circle.svg create mode 100644 src/assets/icons/clock.svg create mode 100644 src/assets/icons/graph-24.svg create mode 100644 src/assets/icons/heart-crack.svg create mode 100644 src/assets/icons/help-24.svg create mode 100644 src/assets/icons/increase-24.svg create mode 100644 src/assets/icons/megaphone-24.svg create mode 100644 src/assets/icons/rewards/graph-within-star.svg create mode 100644 src/assets/icons/rewards/logo-within-star.svg create mode 100644 src/assets/icons/rewards/referral-within-star.svg create mode 100644 src/assets/icons/transfer-1-24.svg create mode 100644 src/assets/icons/transfer-24.svg create mode 100644 src/assets/icons/trophy-24.svg create mode 100644 src/assets/icons/usdc-24.svg create mode 100644 src/assets/icons/usdc-green-16.svg create mode 100644 src/assets/icons/wallet-24.svg create mode 100644 src/assets/ineligible-wallet-ring.svg create mode 100644 src/assets/multi-users.svg create mode 100644 src/assets/piggy-bank.svg create mode 100644 src/assets/plus-star-ring.svg create mode 100644 src/assets/pool-star-ring.svg create mode 100644 src/assets/referral-star-ring.svg create mode 100644 src/assets/shield-check.svg create mode 100644 src/assets/splash-mesh-bg.svg create mode 100644 src/assets/x-star-ring.svg create mode 100644 src/assets/zap.svg create mode 100644 src/components/ACXLiveBanner/ACXLiveBanner.tsx create mode 100644 src/components/ACXLiveBanner/index.ts create mode 100644 src/components/BreadcrumbV2/BreadcrumbV2.tsx create mode 100644 src/components/BreadcrumbV2/index.ts create mode 100644 src/components/BreadcrumbV2/useBreadcrumb.ts create mode 100644 src/components/CopyReferralLink/CopyReferralLink.tsx create mode 100644 src/components/CopyReferralLink/index.ts create mode 100644 src/components/ExternalLink/ExternalLink.tsx create mode 100644 src/components/ExternalLink/index.ts create mode 100644 src/components/IconPair/IconPair.tsx create mode 100644 src/components/IconPair/index.ts create mode 100644 src/components/LayoutV2/LayoutV2.tsx create mode 100644 src/components/LayoutV2/index.ts create mode 100644 src/components/Loader/Loader.tsx create mode 100644 src/components/Loader/index.tsx rename src/{views/Rewards/comp => components}/RewardTable/RewardTable.tsx (69%) rename src/{views/Rewards/comp => components}/RewardTable/RewardTables.styles.tsx (93%) rename src/{views/Rewards/comp => components}/RewardTable/index.ts (100%) create mode 100644 src/components/ScrollToTop/ScrollToTop.tsx create mode 100644 src/components/ScrollToTop/index.ts create mode 100644 src/components/SectionTitleWrapperV2/SectionWrapperV2.tsx create mode 100644 src/components/SectionTitleWrapperV2/index.ts create mode 100644 src/components/Text/Text.tsx create mode 100644 src/components/Text/index.ts create mode 100644 src/hooks/useCoingeckoPrice.ts create mode 100644 src/hooks/useIsWrongNetwork.ts create mode 100644 src/hooks/useReferralLink.ts create mode 100644 src/hooks/useScrollElementByHashIntoView.ts create mode 100644 src/hooks/useStakingPool.ts create mode 100644 src/hooks/useWalletTokenImport.ts rename src/stories/{plaap/CardIcon.stories.tsx => airdrop/IconWithCheck.stories.tsx} (53%) create mode 100644 src/stories/airdrop/NotEligibleWalletFlow.stories.tsx create mode 100644 src/stories/airdrop/SplashFlow.stories.tsx create mode 100644 src/stories/airdrop/VerticalCardsList.stories.tsx delete mode 100644 src/stories/plaap/CardContent.stories.tsx delete mode 100644 src/stories/plaap/SplashFlow.stories.tsx delete mode 100644 src/stories/plaap/TitleSegment.stories.tsx delete mode 100644 src/stories/plaap/TravellerFlow.stories.tsx delete mode 100644 src/stories/plaap/VerticalCardsList.stories.tsx create mode 100644 src/utils/convertdecimals.ts create mode 100644 src/utils/merkle-distributor.ts create mode 100644 src/utils/rewards.ts create mode 100644 src/utils/serverless-api/mocked/coingecko.mocked.ts create mode 100644 src/utils/serverless-api/mocked/get-deposit-stats.mocked.ts create mode 100644 src/utils/serverless-api/prod/coingecko.ts create mode 100644 src/utils/serverless-api/prod/get-deposit-stats.prod.ts create mode 100644 src/utils/ternary.ts create mode 100644 src/utils/tests/rewards.test.ts create mode 100644 src/utils/types.ts delete mode 100644 src/views/About.tsx rename src/views/{PreLaunchAirdrop/PreLaunchAirdrop.styles.tsx => Airdrop/Airdrop.styles.tsx} (53%) create mode 100644 src/views/Airdrop/Airdrop.tsx create mode 100644 src/views/Airdrop/components/BreakdownRow.tsx create mode 100644 src/views/Airdrop/components/BreakdownStats.tsx create mode 100644 src/views/Airdrop/components/ClaimAirdrop.tsx create mode 100644 src/views/Airdrop/components/EarnOptionCard.tsx create mode 100644 src/views/Airdrop/components/EligibleWalletFlow.tsx rename src/views/{PreLaunchAirdrop/components/CardIcon.tsx => Airdrop/components/IconWithCheck.tsx} (76%) create mode 100644 src/views/Airdrop/components/InfoCard.tsx create mode 100644 src/views/Airdrop/components/InfoCardTop.tsx create mode 100644 src/views/Airdrop/components/MoreInfoFlow.tsx create mode 100644 src/views/Airdrop/components/NotEligibleWalletFlow.tsx create mode 100644 src/views/Airdrop/components/Pill.tsx create mode 100644 src/views/Airdrop/components/SplashFlow.tsx create mode 100644 src/views/Airdrop/components/StepCard.tsx rename src/views/{PreLaunchAirdrop => Airdrop}/components/VerticalCardsList.tsx (100%) create mode 100644 src/views/Airdrop/components/WalletHero.tsx create mode 100644 src/views/Airdrop/components/WaysToEarn.tsx create mode 100644 src/views/Airdrop/hooks/useAirdrop.ts create mode 100644 src/views/Airdrop/hooks/useAirdropRecipient.tsx create mode 100644 src/views/Airdrop/hooks/useClaimAndStake.tsx create mode 100644 src/views/Airdrop/hooks/useIsAirdropClaimed.tsx create mode 100644 src/views/Airdrop/index.ts delete mode 100644 src/views/Claim/Claim.styles.tsx delete mode 100644 src/views/Claim/Claim.tsx delete mode 100644 src/views/Claim/index.ts delete mode 100644 src/views/PreLaunchAirdrop/PreLaunchAirdrop.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/AirdropCard.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/CardContent.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/EligibilityPill.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/MoreInfoFlow/MoreInfoFlow.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/MoreInfoFlow/index.ts delete mode 100644 src/views/PreLaunchAirdrop/components/SplashFlow/SplashFlow.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/SplashFlow/index.ts delete mode 100644 src/views/PreLaunchAirdrop/components/TitleSection.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/cards/BridgeTravelerCard.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/cards/BridgeUserCard.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/cards/CommunityRewardCard.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/cards/LiquidityProviderCard.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/content/CardTextDescription.tsx delete mode 100644 src/views/PreLaunchAirdrop/components/index.ts delete mode 100644 src/views/PreLaunchAirdrop/hooks/useDiscordDetails.ts delete mode 100644 src/views/PreLaunchAirdrop/hooks/useDiscordQuery.ts delete mode 100644 src/views/PreLaunchAirdrop/hooks/usePreLaunchAirdrop.ts delete mode 100644 src/views/PreLaunchAirdrop/index.ts create mode 100644 src/views/Referrals/Referrals.styles.tsx create mode 100644 src/views/Referrals/Referrals.tsx create mode 100644 src/views/Referrals/comp/ClaimRewardsModal.tsx rename src/views/{Rewards => Referrals}/comp/ConnectTableOverlay/ConnectTableOverlay.styles.tsx (100%) rename src/views/{Rewards => Referrals}/comp/ConnectTableOverlay/ConnectTableOverlay.tsx (100%) rename src/views/{Rewards => Referrals}/comp/ConnectTableOverlay/index.ts (100%) rename src/views/{Rewards => Referrals}/comp/RewardBreakdown/RewardBreakdown.styles.tsx (100%) rename src/views/{Rewards => Referrals}/comp/RewardBreakdown/RewardBreakdown.tsx (100%) rename src/views/{Rewards => Referrals}/comp/RewardBreakdown/index.ts (100%) rename src/views/{Rewards => Referrals}/comp/RewardHero/RewardHero.styles.tsx (100%) rename src/views/{Rewards => Referrals}/comp/RewardHero/RewardHero.tsx (100%) rename src/views/{Rewards => Referrals}/comp/RewardHero/index.ts (100%) rename src/views/{Rewards => Referrals}/comp/RewardMediumBlock/RewardMediumBlock.styles.tsx (100%) rename src/views/{Rewards => Referrals}/comp/RewardMediumBlock/RewardMediumBlock.tsx (100%) rename src/views/{Rewards => Referrals}/comp/RewardMediumBlock/index.ts (100%) create mode 100644 src/views/Referrals/comp/RewardReferral/RewardReferral.styles.tsx create mode 100644 src/views/Referrals/comp/RewardReferral/RewardReferral.tsx rename src/views/{Rewards => Referrals}/comp/RewardReferral/index.ts (100%) rename src/views/{Rewards => Referrals}/comp/RewardTable/createMyReferralsTableJSX.tsx (99%) rename src/views/{Rewards => Referrals}/comp/RewardTableWithOverlay/RewardTableWithOverlay.styles.tsx (86%) rename src/views/{Rewards => Referrals}/comp/RewardTableWithOverlay/RewardTableWithOverlay.tsx (90%) rename src/views/{Rewards => Referrals}/comp/RewardTableWithOverlay/index.ts (100%) rename src/views/{Rewards => Referrals}/comp/StepperWithTooltips/StepperWithTooltips.styles.tsx (90%) rename src/views/{Rewards => Referrals}/comp/StepperWithTooltips/StepperWithTooltips.tsx (100%) rename src/views/{Rewards => Referrals}/comp/StepperWithTooltips/index.ts (100%) rename src/views/{Rewards => Referrals}/comp/StepperWithTooltips/useStepperWithTooltips.tsx (100%) rename src/views/{Rewards => Referrals}/comp/index.ts (84%) create mode 100644 src/views/Referrals/hooks/useClaimModal.tsx create mode 100644 src/views/Referrals/hooks/useClaimReferralRewards.tsx create mode 100644 src/views/Referrals/hooks/useUnclaimedReferralProofs.tsx create mode 100644 src/views/Referrals/index.ts rename src/views/{Rewards/useRewardsView.ts => Referrals/useReferralsView.ts} (81%) create mode 100644 src/views/Rewards/Rewards.style.tsx delete mode 100644 src/views/Rewards/Rewards.styles.tsx delete mode 100644 src/views/Rewards/comp/RewardReferral/RewardReferral.styles.tsx delete mode 100644 src/views/Rewards/comp/RewardReferral/RewardReferral.tsx delete mode 100644 src/views/Rewards/comp/RewardTable/createAllPoolsTableJSX.tsx delete mode 100644 src/views/Rewards/comp/RewardTable/createMyPoolsTableJSX.tsx create mode 100644 src/views/Rewards/components/AdditionalQuestionCTA.tsx create mode 100644 src/views/Rewards/components/ConnectedReferralBox.tsx create mode 100644 src/views/Rewards/components/DisconnectedReferralBox.tsx create mode 100644 src/views/Rewards/components/GenericStakingPoolTable/GenericStakingPoolFormatter.tsx create mode 100644 src/views/Rewards/components/GenericStakingPoolTable/GenericStakingPoolTable.styles.tsx create mode 100644 src/views/Rewards/components/GenericStakingPoolTable/GenericStakingPoolTable.tsx create mode 100644 src/views/Rewards/components/GenericStakingPoolTable/index.ts create mode 100644 src/views/Rewards/components/OverviewRewardSection.tsx create mode 100644 src/views/Rewards/hooks/useRewards.ts create mode 100644 src/views/Rewards/hooks/useStakingPools.tsx create mode 100644 src/views/Splash/Splash.styles.tsx create mode 100644 src/views/Splash/Splash.tsx create mode 100644 src/views/Splash/components/CardBenefit.tsx create mode 100644 src/views/Splash/components/NumericBenefit.tsx create mode 100644 src/views/Splash/hooks/useSplash.ts create mode 100644 src/views/Splash/hooks/useSplashDynamicData.ts create mode 100644 src/views/Splash/index.ts create mode 100644 src/views/Staking/Staking.styles.tsx create mode 100644 src/views/Staking/Staking.tsx create mode 100644 src/views/Staking/components/ConnectWalletButton.tsx create mode 100644 src/views/Staking/components/StakingExitAction/StakingExitAction.styles.tsx create mode 100644 src/views/Staking/components/StakingExitAction/StakingExitAction.tsx create mode 100644 src/views/Staking/components/StakingExitAction/index.tsx create mode 100644 src/views/Staking/components/StakingForm/StakingForm.styles.tsx create mode 100644 src/views/Staking/components/StakingForm/StakingForm.tsx create mode 100644 src/views/Staking/components/StakingForm/index.ts create mode 100644 src/views/Staking/components/StakingInputBlock/StakingInputBlock.styles.tsx create mode 100644 src/views/Staking/components/StakingInputBlock/StakingInputBlock.tsx create mode 100644 src/views/Staking/components/StakingInputBlock/index.ts create mode 100644 src/views/Staking/components/StakingReward/StakingReward.tsx create mode 100644 src/views/Staking/components/StakingReward/index.ts create mode 100644 src/views/Staking/components/index.ts create mode 100644 src/views/Staking/hooks/useClaimStakeRewardAction.ts create mode 100644 src/views/Staking/hooks/useStakeFormLogic.ts create mode 100644 src/views/Staking/hooks/useStakingAction.ts create mode 100644 src/views/Staking/hooks/useStakingView.ts create mode 100644 src/views/Staking/index.ts create mode 100644 src/views/Staking/types.ts diff --git a/.env.example b/.env.example index f9a207acb..e15aa8cd3 100644 --- a/.env.example +++ b/.env.example @@ -80,3 +80,18 @@ REACT_APP_MOCK_SERVERLESS= # JSON string with format: {[chainId]: numberInMinutes}, eg: { "1": 2, "10": 2, "137": 2, "288": 2, "42161": 2 } REACT_APP_DEPOSIT_DELAY= + +# Airdrop window index +REACT_APP_AIRDROP_WINDOW_INDEX= + +# Referrals claim start window index +REACT_APP_REFERRALS_START_WINDOW_INDEX= + +# Override default MerkleDistributor address +REACT_APP_MERKLE_DISTRIBUTOR_ADDRESS= + +# Enable token on navbar address +REACT_APP_SHOW_ACX_NAV_TOKEN= + +FIXED_TOKEN_PRICES={ "0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461": "0.1" } +REDIRECTED_TOKEN_PRICE_LOOKUP_ADDRESSES={"0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","0xd35CCeEAD182dcee0F148EbaC9447DA2c4D449c4":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","0x5C221E77624690fff6dd741493D735a17716c26B":"0x6B175474E89094C44Da98b954EedeAC495271d0F"} diff --git a/.storybook/main.js b/.storybook/main.js index 276f7663a..8aff3066d 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,3 +1,5 @@ +const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin"); + module.exports = { stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], addons: [ @@ -5,7 +7,6 @@ module.exports = { "@storybook/addon-essentials", "@storybook/addon-interactions", "@storybook/preset-create-react-app", - "storybook-addon-react-router-v6", ], framework: "@storybook/react", core: { @@ -26,4 +27,13 @@ module.exports = { return accumulator; }, {}), }), + webpackFinal: async (config) => { + config.resolve.plugins = [ + ...(config.resolve.plugins || []), + new TsconfigPathsPlugin({ + extensions: config.resolve.extensions, + }), + ]; + return config; + }, }; diff --git a/api/_utils.ts b/api/_utils.ts index 00db58254..00a021982 100644 --- a/api/_utils.ts +++ b/api/_utils.ts @@ -8,7 +8,8 @@ import axios from "axios"; import * as sdk from "@across-protocol/sdk-v2"; import { BigNumber, ethers, providers } from "ethers"; import { Log, Logging } from "@google-cloud/logging"; -import enabledRoutesAsJson from "../src/data/routes_1_0xc186fA914353c44b2E33eBE05f21846F1048bEda.json"; +import enabledMainnetRoutesAsJson from "../src/data/routes_1_0xc186fA914353c44b2E33eBE05f21846F1048bEda.json"; +import enabledGoerliRoutesAsJson from "../src/data/routes_5_0xA44A832B994f796452e4FaF191a041F791AD8A0A.json"; import { maxRelayFeePct, relayerFeeCapitalCostConfig } from "./_constants"; import { StaticJsonRpcProvider } from "@ethersproject/providers"; @@ -18,6 +19,7 @@ import { VercelResponse } from "@vercel/node"; type LoggingUtility = sdk.relayFeeCalculator.Logger; const { + REACT_APP_HUBPOOL_CHAINID, REACT_APP_PUBLIC_INFURA_ID, REACT_APP_COINGECKO_PRO_API_KEY, REACT_APP_GOOGLE_SERVICE_ACCOUNT, @@ -34,6 +36,13 @@ export const gasMarkup = GAS_MARKUP ? JSON.parse(GAS_MARKUP) : {}; // Default to no markup. export const DEFAULT_GAS_MARKUP = 0; +export const HUP_POOL_CHAIN_ID = Number(REACT_APP_HUBPOOL_CHAINID || 1); + +export const ENABLED_ROUTES = + HUP_POOL_CHAIN_ID === 1 + ? enabledMainnetRoutesAsJson + : enabledGoerliRoutesAsJson; + /** * Writes a log using the google cloud logging utility * @param gcpLogger A defined google cloud logging instance @@ -129,7 +138,7 @@ export const getTokenDetails = async ( chainId?: string ) => { const hubPool = HubPool__factory.connect( - "0xc186fA914353c44b2E33eBE05f21846F1048bEda", + ENABLED_ROUTES.hubPoolAddress, provider ); @@ -169,11 +178,14 @@ export class InputError extends Error {} /** * Resolves an Infura provider given the name of the ETH network - * @param name The name of an ethereum network + * @param nameOrChainId The name of an ethereum network * @returns A valid Ethers RPC provider */ -export const infuraProvider = (name: string) => { - const url = `https://${name}.infura.io/v3/${REACT_APP_PUBLIC_INFURA_ID}`; +export const infuraProvider = (nameOrChainId: providers.Networkish) => { + const url = new ethers.providers.InfuraProvider( + nameOrChainId, + REACT_APP_PUBLIC_INFURA_ID + ).connection.url; return new ethers.providers.StaticJsonRpcProvider(url); }; @@ -203,12 +215,25 @@ export const overrideProvider = ( * Generates a fixed HubPoolClientConfig object * @returns A fixed constant */ -export const makeHubPoolClientConfig = () => { +export const makeHubPoolClientConfig = (chainId = 1) => { return { - chainId: 1, - hubPoolAddress: "0xc186fA914353c44b2E33eBE05f21846F1048bEda", - wethAddress: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - configStoreAddress: "0x3B03509645713718B78951126E0A6de6f10043f5", + 1: { + chainId: 1, + hubPoolAddress: "0xc186fA914353c44b2E33eBE05f21846F1048bEda", + wethAddress: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + configStoreAddress: "0x3B03509645713718B78951126E0A6de6f10043f5", + }, + 5: { + chainId: 5, + hubPoolAddress: "0x0e2817C49698cc0874204AeDf7c72Be2Bb7fCD5d", + wethAddress: "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6", + configStoreAddress: "0x3215e3C91f87081757d0c41EF0CB77738123Be83", + }, + }[chainId] as { + chainId: number; + hubPoolAddress: string; + wethAddress: string; + configStoreAddress: string; }; }; @@ -217,11 +242,11 @@ export const makeHubPoolClientConfig = () => { * @returns A HubPool client that can query the blockchain */ export const getHubPoolClient = () => { - const hubPoolConfig = makeHubPoolClientConfig(); + const hubPoolConfig = makeHubPoolClientConfig(HUP_POOL_CHAIN_ID); return new sdk.pool.Client( hubPoolConfig, { - provider: infuraProvider("mainnet"), + provider: infuraProvider(HUP_POOL_CHAIN_ID), }, (_, __) => {} // Dummy function that does nothing and is needed to construct this client. ); @@ -244,7 +269,7 @@ export const getGasMarkup = (chainId: string | number) => { export const queries: Record QueryBase> = { 1: () => new sdk.relayFeeCalculator.EthereumQueries( - infuraProvider("mainnet"), + infuraProvider(1), undefined, undefined, undefined, @@ -255,7 +280,7 @@ export const queries: Record QueryBase> = { ), 10: () => new sdk.relayFeeCalculator.OptimismQueries( - infuraProvider("optimism-mainnet"), + infuraProvider(10), undefined, undefined, undefined, @@ -266,7 +291,7 @@ export const queries: Record QueryBase> = { ), 137: () => new sdk.relayFeeCalculator.PolygonQueries( - infuraProvider("polygon-mainnet"), + infuraProvider(137), undefined, undefined, undefined, @@ -288,7 +313,7 @@ export const queries: Record QueryBase> = { ), 42161: () => new sdk.relayFeeCalculator.ArbitrumQueries( - infuraProvider("arbitrum-mainnet"), + infuraProvider(42161), undefined, undefined, undefined, @@ -389,25 +414,7 @@ export const getProvider = (_chainId: number): providers.Provider => { if (override) { providerCache[chainId] = override; } else { - switch (chainId.toString()) { - case "1": - providerCache[chainId] = infuraProvider("mainnet"); - break; - case "10": - providerCache[chainId] = infuraProvider("optimism-mainnet"); - break; - case "137": - providerCache[chainId] = infuraProvider("polygon-mainnet"); - break; - case "288": - providerCache[chainId] = bobaProvider(); - break; - case "42161": - providerCache[chainId] = infuraProvider("arbitrum-mainnet"); - break; - default: - throw new Error(`Invalid chainId provided: ${chainId}`); - } + providerCache[chainId] = infuraProvider(_chainId); } } return providerCache[chainId]; @@ -464,7 +471,7 @@ export const isRouteEnabled = ( toChainId: number, fromToken: string ): boolean => { - const enabled = enabledRoutesAsJson.routes.some( + const enabled = ENABLED_ROUTES.routes.some( ({ fromTokenAddress, fromChain, toChain }) => fromChainId === fromChain && toChainId === toChain && diff --git a/api/coingecko.ts b/api/coingecko.ts index 0ee7fa5da..80c480223 100644 --- a/api/coingecko.ts +++ b/api/coingecko.ts @@ -9,7 +9,11 @@ import { coingecko, relayFeeCalculator } from "@across-protocol/sdk-v2"; const { Coingecko } = coingecko; const { SymbolMapping } = relayFeeCalculator; -const { REACT_APP_COINGECKO_PRO_API_KEY, FIXED_TOKEN_PRICES } = process.env; +const { + REACT_APP_COINGECKO_PRO_API_KEY, + FIXED_TOKEN_PRICES, + REDIRECTED_TOKEN_PRICE_LOOKUP_ADDRESSES, +} = process.env; // Helper function to fetch prices from coingecko. Can fetch either or both token and base currency. // Set hardcodedTokenPriceUsd to 0 to load the token price from coingecko, otherwise load only the base @@ -88,6 +92,19 @@ const handler = async ( l1Token = ethers.utils.getAddress(l1Token); + // Resolve the optional address lookup that maps one token's + // contract address to another. + const redirectLookupAddresses: Record = + REDIRECTED_TOKEN_PRICE_LOOKUP_ADDRESSES !== undefined + ? JSON.parse(REDIRECTED_TOKEN_PRICE_LOOKUP_ADDRESSES) + : {}; + + // Perform a 1-deep lookup to see if the provided l1Token is + // to be "redirected" to another provided token contract address + if (redirectLookupAddresses[l1Token]) { + l1Token = redirectLookupAddresses[l1Token]; + } + const coingeckoClient = Coingecko.get( logger, REACT_APP_COINGECKO_PRO_API_KEY diff --git a/cypress/e2e/bridge.cy.ts b/cypress/e2e/bridge.cy.ts index a4a1e7e94..84c976552 100644 --- a/cypress/e2e/bridge.cy.ts +++ b/cypress/e2e/bridge.cy.ts @@ -1,6 +1,6 @@ describe("bridge", () => { it("render in initial state", () => { - cy.visit("/"); + cy.visit("/bridge"); cy.dataCy("connect-wallet").should("be.visible"); cy.dataCy("send").should("be.disabled"); }); diff --git a/cypress/e2e/header.cy.ts b/cypress/e2e/header.cy.ts index 8a4a9b6ab..2ccb398d1 100644 --- a/cypress/e2e/header.cy.ts +++ b/cypress/e2e/header.cy.ts @@ -4,7 +4,7 @@ describe("headers", () => { cy.dataCy("primary-header").should("be.visible"); }); it("should not be transparent on any route except airdrop", () => { - const routes = ["/", "/pool", "/rewards", "/transactions"]; + const routes = ["/bridge", "/pool", "/rewards", "/transactions"]; for (const route of routes) { cy.visit(route); cy.dataCy("primary-header").should( @@ -15,23 +15,4 @@ describe("headers", () => { ); } }); - it("should be transparent on /airdrop", () => { - cy.visit("/airdrop"); - cy.dataCy("primary-header").should( - "have.css", - "background-color", - // #2d2e3300 in RGB is rgba(45, 46, 51, 0) - "rgba(45, 46, 51, 0)" - ); - }); - it("transparency should become opaque on scroll", () => { - cy.visit("/airdrop"); - cy.scrollTo("bottom"); - cy.dataCy("primary-header").should( - "have.css", - "background-color", - // ##2d2e33f0 in RGB is rgba(45,46,51,0.94) - "rgba(45, 46, 51, 0.94)" - ); - }); }); diff --git a/cypress/e2e/prelaunch.cy.ts b/cypress/e2e/prelaunch.cy.ts deleted file mode 100644 index 2032f61a4..000000000 --- a/cypress/e2e/prelaunch.cy.ts +++ /dev/null @@ -1,27 +0,0 @@ -describe("prelaunch", () => { - const cardIds = [ - "bridge-traveler-card", - "community-rewards-card", - "liquidity-provider-card", - "bridge-user-card", - ]; - - it("render in initial state", () => { - cy.visit("/airdrop"); - cy.dataCy("connect-wallet").should("be.visible"); - }); - - it("display airdrop details", () => { - cy.dataCy("airdrop-details-button").click(); - cy.dataCy("airdrop-details").should("be.visible"); - - cy.dataCy("back-button").click(); - cy.dataCy("airdrop-details-button").should("be.visible"); - }); - - it("display all cards for disconnected wallet", () => { - for (const cardId of cardIds) { - cy.dataCy(cardId).scrollIntoView().should("be.visible"); - } - }); -}); diff --git a/cypress/e2e/rewards.cy.ts b/cypress/e2e/referrals.cy.ts similarity index 84% rename from cypress/e2e/rewards.cy.ts rename to cypress/e2e/referrals.cy.ts index c9b2e4b17..7bb298225 100644 --- a/cypress/e2e/rewards.cy.ts +++ b/cypress/e2e/referrals.cy.ts @@ -1,6 +1,6 @@ -describe("rewards", () => { +describe("referrals", () => { beforeEach(() => { - cy.visit("/rewards"); + cy.visit("/rewards/referrals"); }); it("render in initial state", () => { diff --git a/cypress/e2e/wallet.cy.ts b/cypress/e2e/wallet.cy.ts index 938e9cd81..81761a0f1 100644 --- a/cypress/e2e/wallet.cy.ts +++ b/cypress/e2e/wallet.cy.ts @@ -1,6 +1,6 @@ describe("wallet", () => { it("display ACX balance for connected wallet", () => { - cy.visit("/"); + cy.visit("/bridge"); cy.connectInjectedWallet("connect-wallet"); cy.dataCy("acx-balance").should("be.visible"); diff --git a/package.json b/package.json index ffc8e675e..a806925eb 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "version": "0.1.0", "private": true, "dependencies": { - "@across-protocol/contracts-v2": "^1.0.4", + "@across-protocol/across-token": "^0.0.2", + "@across-protocol/contracts-v2": "^1.0.7", "@across-protocol/sdk-v2": "0.1.33", "@datapunt/matomo-tracker-js": "^0.5.1", "@emotion/react": "^11.4.1", @@ -43,6 +44,7 @@ "jose": "^4.9.3", "lodash-es": "^4.17.21", "luxon": "^2.3.1", + "numeral": "^2.0.6", "react": "^17.0.2", "react-dom": "^17.0.2", "react-feather": "^2.0.9", @@ -66,8 +68,8 @@ "serve": "serve -s build", "test-api": "jest test/api", "eject": "react-scripts eject", - "lint": "eslint .", - "lint:fix": "eslint --fix .", + "lint": "eslint src api cypress test", + "lint:fix": "eslint --fix src api cypress test", "predeploy": "yarn sitemap", "sitemap": "babel-node src/sitemap-generator.js", "prepare": "husky install", @@ -107,6 +109,7 @@ "@storybook/testing-library": "^0.0.13", "@testing-library/react-hooks": "^8.0.1", "@types/luxon": "^2.3.0", + "@types/numeral": "^2.0.2", "@types/react-lottie": "^1.2.6", "@types/react-slider": "^1.3.1", "@vercel/node": "^2.5.13", @@ -125,7 +128,8 @@ "prettier": "^2.4.1", "serve": "^14.0.1", "ts-jest": "^26.5.6", - "ts-node": "^10.9.1" + "ts-node": "^10.9.1", + "tsconfig-paths-webpack-plugin": "^4.0.0" }, "babel": { "env": { diff --git a/public/index.html b/public/index.html index cd173ed46..064b803ff 100644 --- a/public/index.html +++ b/public/index.html @@ -1,6 +1,6 @@ - + - Across Protocol – Transfer Assets Between Layer 2s and Mainnet + + Across Protocol – Transfer Assets Between Layer 2s and Mainnet +
+ + async + src="https://platform.twitter.com/widgets.js" + charset="utf-8" + > diff --git a/public/logos/acx-logo.svg b/public/logos/acx-logo.svg index 11d0e2e5e..e4e19df02 100644 --- a/public/logos/acx-logo.svg +++ b/public/logos/acx-logo.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/public/logos/wbtc-logo.svg b/public/logos/wbtc-logo.svg index fb148bcab..098149014 100644 --- a/public/logos/wbtc-logo.svg +++ b/public/logos/wbtc-logo.svg @@ -1 +1,14 @@ - \ No newline at end of file + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/sitemap.xml b/public/sitemap.xml index d0cbe06e9..ab304b518 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -2,6 +2,5 @@ https://across.to/ https://across.to/ - https://across.to/about https://across.to/pool \ No newline at end of file diff --git a/src/Routes.tsx b/src/Routes.tsx index b6b0a7729..b9539044f 100644 --- a/src/Routes.tsx +++ b/src/Routes.tsx @@ -10,23 +10,28 @@ import { WrongNetworkError, rewardsBannerWarning, generalMaintenanceMessage, + stringValueInArray, + getConfig, } from "utils"; import { ReactComponent as InfoLogo } from "assets/icons/info-24.svg"; import Toast from "components/Toast"; import BouncingDotsLoader from "components/BouncingDotsLoader"; import NotFound from "./views/NotFound"; +import ACXLiveBanner from "components/ACXLiveBanner/ACXLiveBanner"; +import ScrollToTop from "components/ScrollToTop"; const Pool = lazy(() => import(/* webpackChunkName: "Pool" */ "./views/Pool")); +const Referrals = lazy( + () => import(/* webpackChunkName: "Referrals" */ "./views/Referrals") +); const Rewards = lazy( () => import(/* webpackChunkName: "Rewards" */ "./views/Rewards") ); const Send = lazy(() => import(/* webpackChunkName: "Send" */ "./views/Send")); -const About = lazy( - () => import(/* webpackChunkName: "About" */ "./views/About") -); -const Claim = lazy( - () => import(/* webpackChunkName: "Claim" */ "./views/Claim") +const Splash = lazy( + () => import(/* webpackChunkName: "Splash" */ "./views/Splash") ); +const Airdrop = lazy(() => import("./views/Airdrop")); const MyTransactions = lazy( () => import( @@ -39,19 +44,8 @@ const AllTransactions = lazy( /* webpackChunkName: "AllTransactions" */ "./views/Transactions/allTransactions" ) ); - -const PreLaunchAirdrop = lazy( - () => - import( - /* webpackChunkName: "PreLaunchAirdrop" */ "./views/PreLaunchAirdrop/PreLaunchAirdrop" - ) -); - -const DiscordAuth = lazy( - () => - import( - /* webpackChunkName: "DiscordAuth" */ "./views/DiscordAuth/DiscordAuth" - ) +const Staking = lazy( + () => import(/* webpackChunkName: "RewardStaking" */ "./views/Staking") ); const warningMessage = ` @@ -61,12 +55,14 @@ const warningMessage = ` `; function useRoutes() { - const [transparentHeader, setTransparentHeader] = useState(false); const [openSidebar, setOpenSidebar] = useState(false); + const [enableACXBanner, setEnableACXBanner] = useState(true); const { provider, isContractAddress } = useConnection(); const location = useLocation(); const history = useHistory(); const { error, removeError } = useError(); + const config = getConfig(); + // force the user on /pool page if showMigrationPage is active. // This UseEffect performs the following operations: // 1. Force the user to /pool if showMigrationPage is active @@ -75,7 +71,6 @@ function useRoutes() { if (enableMigration && location.pathname !== "/pool") { history.push("/pool"); } - setTransparentHeader(location.pathname === "/airdrop"); }, [location.pathname, history]); return { @@ -85,8 +80,12 @@ function useRoutes() { error, removeError, location, - transparentHeader, + isAirdrop: location.pathname === "/airdrop", + isHomepage: location.pathname === "/", isContractAddress, + config, + enableACXBanner, + setEnableACXBanner, }; } // Need this component for useLocation hook @@ -97,8 +96,12 @@ const Routes: React.FC = () => { error, removeError, location, + config, isContractAddress, - transparentHeader, + isAirdrop, + enableACXBanner, + setEnableACXBanner, + isHomepage, } = useRoutes(); return ( @@ -130,24 +133,42 @@ const Routes: React.FC = () => { {isContractAddress && ( {warningMessage} )} + {!isAirdrop && enableACXBanner && ( + + )}
- {" "} + + }> - + - - - - - + + { + const poolIdFound = stringValueInArray( + match.params.poolId.toLowerCase(), + config.getPoolSymbols() + ); + + if (poolIdFound) { + return ; + } else { + return ; + } + }} + /> + + diff --git a/src/assets/acx.svg b/src/assets/acx.svg new file mode 100644 index 000000000..e4e19df02 --- /dev/null +++ b/src/assets/acx.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/airdrop-gift-bg.svg b/src/assets/airdrop-gift-bg.svg new file mode 100644 index 000000000..2f8a224f1 --- /dev/null +++ b/src/assets/airdrop-gift-bg.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/airdrop-waves-bg.svg b/src/assets/airdrop-waves-bg.svg new file mode 100644 index 000000000..617da149a --- /dev/null +++ b/src/assets/airdrop-waves-bg.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/assets/airdrop-x.svg b/src/assets/airdrop-x.svg new file mode 100644 index 000000000..c893ec600 --- /dev/null +++ b/src/assets/airdrop-x.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/bg-banners/blue-card-banner.svg b/src/assets/bg-banners/blue-card-banner.svg new file mode 100644 index 000000000..ce8a0cb8c --- /dev/null +++ b/src/assets/bg-banners/blue-card-banner.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/bg-banners/green-card-banner.svg b/src/assets/bg-banners/green-card-banner.svg new file mode 100644 index 000000000..be566b52b --- /dev/null +++ b/src/assets/bg-banners/green-card-banner.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/bg-banners/purple-card-banner.svg b/src/assets/bg-banners/purple-card-banner.svg new file mode 100644 index 000000000..a6b3d350a --- /dev/null +++ b/src/assets/bg-banners/purple-card-banner.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/bg-banners/stars-bg.png b/src/assets/bg-banners/stars-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..23e606bf86b5cc074b1e52981250267d9ca4bdc1 GIT binary patch literal 50427 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 + + + diff --git a/src/assets/check-star-ring.svg b/src/assets/check-star-ring.svg new file mode 100644 index 000000000..6236b4c9b --- /dev/null +++ b/src/assets/check-star-ring.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/claim-heart-wave.svg b/src/assets/claim-heart-wave.svg new file mode 100644 index 000000000..a59f15815 --- /dev/null +++ b/src/assets/claim-heart-wave.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/claim-pie-chart-wave.svg b/src/assets/claim-pie-chart-wave.svg new file mode 100644 index 000000000..7171ac583 --- /dev/null +++ b/src/assets/claim-pie-chart-wave.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/eth-white.svg b/src/assets/eth-white.svg new file mode 100644 index 000000000..56879e1f6 --- /dev/null +++ b/src/assets/eth-white.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/gem.svg b/src/assets/gem.svg new file mode 100644 index 000000000..37bec21b3 --- /dev/null +++ b/src/assets/gem.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/icons/chevron-left-vector.svg b/src/assets/icons/chevron-left-vector.svg new file mode 100644 index 000000000..0b485ef7c --- /dev/null +++ b/src/assets/icons/chevron-left-vector.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/chevron-with-circle.svg b/src/assets/icons/chevron-with-circle.svg new file mode 100644 index 000000000..39deec48c --- /dev/null +++ b/src/assets/icons/chevron-with-circle.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/icons/clock.svg b/src/assets/icons/clock.svg new file mode 100644 index 000000000..9ad314d72 --- /dev/null +++ b/src/assets/icons/clock.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/icons/graph-24.svg b/src/assets/icons/graph-24.svg new file mode 100644 index 000000000..57f893855 --- /dev/null +++ b/src/assets/icons/graph-24.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/icons/heart-crack.svg b/src/assets/icons/heart-crack.svg new file mode 100644 index 000000000..ff49e69a5 --- /dev/null +++ b/src/assets/icons/heart-crack.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/help-24.svg b/src/assets/icons/help-24.svg new file mode 100644 index 000000000..c596db96e --- /dev/null +++ b/src/assets/icons/help-24.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/icons/increase-24.svg b/src/assets/icons/increase-24.svg new file mode 100644 index 000000000..4a09eea82 --- /dev/null +++ b/src/assets/icons/increase-24.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/megaphone-24.svg b/src/assets/icons/megaphone-24.svg new file mode 100644 index 000000000..6842bfd30 --- /dev/null +++ b/src/assets/icons/megaphone-24.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/rewards/graph-within-star.svg b/src/assets/icons/rewards/graph-within-star.svg new file mode 100644 index 000000000..254c0aa7b --- /dev/null +++ b/src/assets/icons/rewards/graph-within-star.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/rewards/logo-within-star.svg b/src/assets/icons/rewards/logo-within-star.svg new file mode 100644 index 000000000..17f18a149 --- /dev/null +++ b/src/assets/icons/rewards/logo-within-star.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/rewards/referral-within-star.svg b/src/assets/icons/rewards/referral-within-star.svg new file mode 100644 index 000000000..bdf7bf57a --- /dev/null +++ b/src/assets/icons/rewards/referral-within-star.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/assets/icons/transfer-1-24.svg b/src/assets/icons/transfer-1-24.svg new file mode 100644 index 000000000..5f073ce04 --- /dev/null +++ b/src/assets/icons/transfer-1-24.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/icons/transfer-24.svg b/src/assets/icons/transfer-24.svg new file mode 100644 index 000000000..cdc5b94ce --- /dev/null +++ b/src/assets/icons/transfer-24.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/trophy-24.svg b/src/assets/icons/trophy-24.svg new file mode 100644 index 000000000..38cc78118 --- /dev/null +++ b/src/assets/icons/trophy-24.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/usdc-24.svg b/src/assets/icons/usdc-24.svg new file mode 100644 index 000000000..bac4cac14 --- /dev/null +++ b/src/assets/icons/usdc-24.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/icons/usdc-green-16.svg b/src/assets/icons/usdc-green-16.svg new file mode 100644 index 000000000..4011ca82f --- /dev/null +++ b/src/assets/icons/usdc-green-16.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/icons/wallet-24.svg b/src/assets/icons/wallet-24.svg new file mode 100644 index 000000000..881e41ab2 --- /dev/null +++ b/src/assets/icons/wallet-24.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/ineligible-wallet-ring.svg b/src/assets/ineligible-wallet-ring.svg new file mode 100644 index 000000000..25606ad15 --- /dev/null +++ b/src/assets/ineligible-wallet-ring.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/multi-users.svg b/src/assets/multi-users.svg new file mode 100644 index 000000000..7fd501e02 --- /dev/null +++ b/src/assets/multi-users.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/piggy-bank.svg b/src/assets/piggy-bank.svg new file mode 100644 index 000000000..93101b51e --- /dev/null +++ b/src/assets/piggy-bank.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/assets/plus-star-ring.svg b/src/assets/plus-star-ring.svg new file mode 100644 index 000000000..ef46ee426 --- /dev/null +++ b/src/assets/plus-star-ring.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/pool-star-ring.svg b/src/assets/pool-star-ring.svg new file mode 100644 index 000000000..f5ee6ebe2 --- /dev/null +++ b/src/assets/pool-star-ring.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/referral-star-ring.svg b/src/assets/referral-star-ring.svg new file mode 100644 index 000000000..77c9e3b63 --- /dev/null +++ b/src/assets/referral-star-ring.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/shield-check.svg b/src/assets/shield-check.svg new file mode 100644 index 000000000..da1db7407 --- /dev/null +++ b/src/assets/shield-check.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/assets/splash-mesh-bg.svg b/src/assets/splash-mesh-bg.svg new file mode 100644 index 000000000..841389796 --- /dev/null +++ b/src/assets/splash-mesh-bg.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/x-star-ring.svg b/src/assets/x-star-ring.svg new file mode 100644 index 000000000..4544b3412 --- /dev/null +++ b/src/assets/x-star-ring.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/zap.svg b/src/assets/zap.svg new file mode 100644 index 000000000..8a1b7f94d --- /dev/null +++ b/src/assets/zap.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/components/ACXLiveBanner/ACXLiveBanner.tsx b/src/components/ACXLiveBanner/ACXLiveBanner.tsx new file mode 100644 index 000000000..8de634b8c --- /dev/null +++ b/src/components/ACXLiveBanner/ACXLiveBanner.tsx @@ -0,0 +1,180 @@ +import styled from "@emotion/styled"; +import VideoBackground from "assets/prelaunch/acx-bg-video-comp.mp4"; +import { ReactComponent as ChevronRight } from "assets/icons/arrow-right-16.svg"; +import { QUERIESV2 } from "utils"; +import { Link } from "react-router-dom"; + +const ACXLiveBanner = ({ + enableHandler, +}: { + enableHandler: React.Dispatch>; +}) => ( + + + + + + + + + We're excited to announce that the{" "} + ACX token is live. + + + The ACX token is live. + + + + Check eligibility + enableHandler(false)}> + + + + + +); + +export default ACXLiveBanner; + +const DesktopAnnouncement = styled.div` + @media ${QUERIESV2.tb.andDown} { + display: none; + } +`; + +const MobileAnnouncement = styled.div` + display: none; + @media ${QUERIESV2.tb.andDown} { + display: block; + } +`; + +const AnnouncementWrapper = styled.div` + display: flex; + flex-direction: row; + align-items: center; + padding: 0px; + gap: 12px; + + color: #e0f3ff; +`; + +const ButtonWrapper = styled(AnnouncementWrapper)` + gap: 16px; + color: #e0f3ff; +`; + +const ContentWrapper = styled.div` + width: 100%; + height: 100%; + z-index: 2; + + padding: 0px 24px; + gap: 24px; + + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + + @media ${QUERIESV2.sm.andDown} { + gap: 12px; + padding: 0; + } +`; + +const Wrapper = styled.div` + position: relative; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + padding: 0px 24px; + gap: 24px; + isolation: isolate; + + height: 72px; + + background: #202024; + border-bottom: 1px solid #3e4047; + + overflow: clip; +`; + +const BackgroundLayer = styled.video` + position: absolute; + right: 0; + top: 0; + width: 100%; + height: 100%; + z-index: 0 !important; + background: linear-gradient(72.13deg, #34353b 0%, rgba(52, 53, 59, 0.75) 100%), + linear-gradient(0deg, #34353b, #34353b); + transform: matrix(-1, 0, 0, 1, 0, 0); + mix-blend-mode: luminosity !important; + object-fit: cover; +`; + +const OpacityLayer = styled.div` + position: absolute; + right: 0; + top: 0; + width: 100%; + height: 200%; + z-index: 1 !important; + background: linear-gradient( + 81deg, + #202024 11.29%, + rgba(32, 32, 36, 0.75) 97.62% + ), + url(ezgif-1-96424221d0.png), #34353b; + background-blend-mode: normal, luminosity, normal; + transform: matrix(-1, 0, 0, 1, 0, 0); + + opacity: 0.9; +`; + +const GradientText = styled.span` + background: linear-gradient(264.97deg, #6cf9d8 24.16%, #c4fff1 61.61%), + linear-gradient(0deg, #e0f3ff, #e0f3ff); + background-clip: text; + -webkit-text-fill-color: transparent; +`; + +const StyledButton = styled(Link)` + height: auto; + width: auto; + padding: 0; + + border-radius: 32px; + border: 1px solid #4c4e57; + + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + + width: 40px; + height: 40px; + + filter: drop-shadow(0px 0px 24px rgba(109, 250, 217, 0.25)); + + &:hover { + background: #6cf9d8; + box-shadow: 0px 0px 24px rgba(109, 250, 217, 0.25); + & svg path { + stroke: #2d2e33; + } + } + + @media ${QUERIESV2.tb.andDown} { + height: 32px; + width: 32px; + } +`; + +const DesktopText = styled.span` + @media ${QUERIESV2.tb.andDown} { + display: none; + } +`; diff --git a/src/components/ACXLiveBanner/index.ts b/src/components/ACXLiveBanner/index.ts new file mode 100644 index 000000000..107d55f35 --- /dev/null +++ b/src/components/ACXLiveBanner/index.ts @@ -0,0 +1 @@ +export { default } from "./ACXLiveBanner"; diff --git a/src/components/Alert/Alert.styles.ts b/src/components/Alert/Alert.styles.ts index 411fb554a..951b2c882 100644 --- a/src/components/Alert/Alert.styles.ts +++ b/src/components/Alert/Alert.styles.ts @@ -12,6 +12,11 @@ const AlertColors: Record< fontColor: "#f9d26c", borderColor: "rgba(249, 210, 108, 0.1)", }, + danger: { + bgColor: "rgba(249, 108, 108, 0.05)", + fontColor: "#f96c6c", + borderColor: "rgba(249, 108, 108, 0.1)", + }, }; type IncludeStatusType = { @@ -41,6 +46,11 @@ export const Wrapper = styled.div` font-size: 14px; line-height: 18px; } + + @media ${QUERIESV2.sm.andDown} { + padding: 12px; + gap: 12px; + } `; export const ChildrenWrapper = styled.div``; @@ -58,4 +68,8 @@ export const StyledInfoIcon = styled(InfoIcon)` height: 20px; width: 20px; } + @media ${QUERIESV2.sm.andDown} { + height: 16px; + width: 16px; + } `; diff --git a/src/components/Alert/Alert.tsx b/src/components/Alert/Alert.tsx index 7c9436747..6e9d5da54 100644 --- a/src/components/Alert/Alert.tsx +++ b/src/components/Alert/Alert.tsx @@ -1,6 +1,6 @@ import { ChildrenWrapper, StyledInfoIcon, Wrapper } from "./Alert.styles"; -export type AlertStatusType = "warn"; +export type AlertStatusType = "warn" | "danger"; type AlertProps = { status: AlertStatusType; diff --git a/src/components/Banner/Banner.styles.tsx b/src/components/Banner/Banner.styles.tsx index 0729fdda6..396f579a9 100644 --- a/src/components/Banner/Banner.styles.tsx +++ b/src/components/Banner/Banner.styles.tsx @@ -12,7 +12,6 @@ export const Wrapper = styled.div` font-size: ${16 / 16}rem; position: unset; width: 100%; - margin-bottom: 2rem; top: 0; left: 0; z-index: 1100; diff --git a/src/components/BouncingDotsLoader/BouncingDotsLoader.tsx b/src/components/BouncingDotsLoader/BouncingDotsLoader.tsx index 45d0f65e0..f620b0172 100644 --- a/src/components/BouncingDotsLoader/BouncingDotsLoader.tsx +++ b/src/components/BouncingDotsLoader/BouncingDotsLoader.tsx @@ -3,27 +3,34 @@ import styled from "@emotion/styled"; export type BounceType = "default" | "big"; +type LoaderColor = "white" | "dark-grey" | "warning"; +const LoaderColorMapping: Record = { + white: "#fff", + "dark-grey": "hsl(230deg 6% 19%);", + warning: "#f9d26c", +}; + interface Props { type?: BounceType; dataCy?: string; - whiteIcons?: boolean; + dotColor?: LoaderColor; } const BouncingDotsLoader: FC = ({ type = "default", dataCy, - whiteIcons, + dotColor, }) => { if (type === "big") return ( - +
); return ( - +
@@ -31,7 +38,7 @@ const BouncingDotsLoader: FC = ({ ); }; -const BouncingWrapper = styled.div<{ whiteIcons?: boolean }>` +const BouncingWrapper = styled.div<{ dotColor: LoaderColor }>` display: inline-flex; margin-left: 4px; @@ -40,8 +47,7 @@ const BouncingWrapper = styled.div<{ whiteIcons?: boolean }>` height: 6px; margin: 2px 4px; border-radius: 50%; - background-color: ${({ whiteIcons }) => - whiteIcons ? "#fff" : "var(--color-gray)"}; + background-color: ${({ dotColor }) => LoaderColorMapping[dotColor]}; opacity: 1; animation: bouncing-loader 0.6s infinite alternate; } @@ -52,11 +58,11 @@ const BouncingWrapper = styled.div<{ whiteIcons?: boolean }>` } } - > div:nth-child(2) { + > div:nth-of-type(2) { animation-delay: 0.2s; } - > div:nth-child(3) { + > div:nth-of-type(3) { animation-delay: 0.4s; } `; diff --git a/src/components/BreadcrumbV2/BreadcrumbV2.tsx b/src/components/BreadcrumbV2/BreadcrumbV2.tsx new file mode 100644 index 000000000..8e73238b8 --- /dev/null +++ b/src/components/BreadcrumbV2/BreadcrumbV2.tsx @@ -0,0 +1,92 @@ +import styled from "@emotion/styled"; +import { useBreadcrumb } from "./useBreadcrumb"; +import { ReactComponent as ArrowIcon } from "assets/icons/arrow-16.svg"; +import { Link } from "react-router-dom"; +import { Text } from "components/Text"; +import React from "react"; + +type BreadcrumbV2Type = { + onlyRootAncestor?: boolean; + customCurrentRoute?: string | React.ReactElement; +}; + +const BreadcrumbV2 = ({ + onlyRootAncestor, + customCurrentRoute, +}: BreadcrumbV2Type) => { + const { ancestorRoutes, currentRoute } = useBreadcrumb(); + + const definedAncestors = + onlyRootAncestor && ancestorRoutes.length > 0 + ? [ancestorRoutes[0]] + : ancestorRoutes; + + const updatedRoute = customCurrentRoute ? ( + customCurrentRoute + ) : ( + {currentRoute.name} + ); + + return ( + + + {definedAncestors.map((route) => ( + + + {route.name} + + + + ))} + {updatedRoute} + + + + ); +}; + +export default BreadcrumbV2; + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + padding: 0px; + gap: 12px; + + width: 100%; +`; + +const Divider = styled.div` + background: #34353b; + height: 1px; + width: 100%; +`; + +const BreadcrumbWrapper = styled.div` + display: flex; + flex-direction: row; + align-items: center; + padding: 0px; + gap: 8px; +`; + +const ActiveLink = styled(Link)` + color: #9daab2; + + text-transform: capitalize; + text-decoration: none; +`; + +const ActiveLinkText = styled(Text)` + color: #9daab2; +`; + +const InactiveLink = styled(Text)` + color: #e0f3ff; + text-transform: capitalize; +`; + +const StyledArrowLink = styled(ArrowIcon)` + rotate: -90deg; +`; diff --git a/src/components/BreadcrumbV2/index.ts b/src/components/BreadcrumbV2/index.ts new file mode 100644 index 000000000..0155dd6a7 --- /dev/null +++ b/src/components/BreadcrumbV2/index.ts @@ -0,0 +1 @@ +export { default } from "./BreadcrumbV2"; diff --git a/src/components/BreadcrumbV2/useBreadcrumb.ts b/src/components/BreadcrumbV2/useBreadcrumb.ts new file mode 100644 index 000000000..5d197ec16 --- /dev/null +++ b/src/components/BreadcrumbV2/useBreadcrumb.ts @@ -0,0 +1,26 @@ +import { useLocation } from "react-router"; + +export function useBreadcrumb() { + const location = useLocation(); + const routes = location.pathname + .split("/") + .filter((r) => r) + .reduce((prev, curr) => { + prev.push({ + path: `${prev[prev.length - 1]?.path ?? ""}/${curr}`, + name: curr, + }); + return prev; + }, [] as { path: string; name: string }[]); + if (routes.length === 0) { + routes.push({ path: "/", name: "Home" }); + } + const ancestorRoutes = routes.slice(0, -1); + const currentRoute = routes[routes.length - 1]; + + return { + routes, + ancestorRoutes, + currentRoute, + }; +} diff --git a/src/components/CopyReferralLink/CopyReferralLink.tsx b/src/components/CopyReferralLink/CopyReferralLink.tsx new file mode 100644 index 000000000..74e2daee8 --- /dev/null +++ b/src/components/CopyReferralLink/CopyReferralLink.tsx @@ -0,0 +1,118 @@ +import styled from "@emotion/styled"; +import { ButtonV2 } from "components/Buttons"; +import { useReferralLink } from "hooks/useReferralLink"; +import { ReactComponent as UnstyledCopyIcon } from "assets/icons/copy-16.svg"; +import { useEffect, useState } from "react"; +import useCurrentBreakpoint from "hooks/useCurrentBreakpoint"; +import { QUERIESV2 } from "utils"; +import { Text } from "components/Text"; + +type CopyReferralLinkType = { + condensed?: boolean; +}; + +const CopyReferralLink = ({ condensed: _condensed }: CopyReferralLinkType) => { + const { helpers } = useCurrentBreakpoint(); + const { referralLink, condensedReferralLink, referralLinkWithProtocol } = + useReferralLink(); + const condensed = _condensed || helpers.tabletAndDown; + const text = condensed ? condensedReferralLink : referralLink; + + const [completed, setCompleted] = useState(false); + + useEffect(() => { + if (completed) { + navigator.clipboard.writeText(referralLinkWithProtocol ?? ""); + const timeoutId = setTimeout(() => { + setCompleted(false); + }, 3000); + return () => clearTimeout(timeoutId); + } + }, [completed, referralLinkWithProtocol]); + + return ( + { + setCompleted(true); + }} + > + {text} + + {!condensed && "Copy referral link"} + + + + ); +}; + +export default CopyReferralLink; + +const Wrapper = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + gap: 16px; + padding: 20px 24px; + + width: 100%; + + background: #2d2e33; + border-top: 1px solid #3e4047; + border-radius: 0px 0px 10px 10px; + + cursor: pointer; + + @media ${QUERIESV2.sm.andDown} { + padding: 16px; + } +`; + +const LinkText = styled(Text)` + font-feature-settings: "tnum" on, "lnum" on; + color: #9daab2; + width: fit-content; +`; + +const CopyReferralButton = styled(ButtonV2)` + display: flex; + flex-direction: row; + align-items: center; + padding: 0px; + gap: 12px; + font-style: normal; + + font-weight: 500; + font-size: 16px; + line-height: 20px; + color: #e0f3ff; + + background: transparent; + + &:active::after { + border: none !important; + } +`; + +// Note: React was having an error hear related +// to the string being a boolean The way I +// removed this error was by dealing with this +// was by treating completed as a string +type StyledCopyIconType = { + completed: string; +}; +const StyledCopyIcon = styled(UnstyledCopyIcon)` + height: 24px; + width: 24px; + & path { + stroke: ${({ completed }) => + completed === "true" ? "#6cf9d8" : "#e0f3ff"}; + transition: stroke 0.25s; + } + + @media ${QUERIESV2.sm.andDown} { + height: 16px; + width: 16px; + } +`; diff --git a/src/components/CopyReferralLink/index.ts b/src/components/CopyReferralLink/index.ts new file mode 100644 index 000000000..793b416e4 --- /dev/null +++ b/src/components/CopyReferralLink/index.ts @@ -0,0 +1 @@ +export { default } from "./CopyReferralLink"; diff --git a/src/components/ExternalLink/ExternalLink.tsx b/src/components/ExternalLink/ExternalLink.tsx new file mode 100644 index 000000000..5d8eb1cfc --- /dev/null +++ b/src/components/ExternalLink/ExternalLink.tsx @@ -0,0 +1,47 @@ +import styled from "@emotion/styled"; + +import { ReactComponent as ExternalLink12Icon } from "assets/icons/external-link-12.svg"; + +type Props = { + text: string; + href: string; +}; + +export function ExternalLink(props: Props) { + return ( + + {props.text} + + ); +} + +const Link = styled.a` + display: flex; + align-items: center; + font-size: ${16 / 16}rem; + line-height: ${20 / 16}rem; + font-weight: 500; + text-decoration: none; + color: #e0f3ff; + transition: opacity 0.1s; + cursor: pointer; + + svg { + path { + fill: #e0f3ff; + } + } + + &:hover { + opacity: 0.8; + } + + @media (max-width: 428px) { + font-size: ${14 / 16}rem; + line-height: ${18 / 16}rem; + } +`; + +export const ExternalLinkIcon = styled(ExternalLink12Icon)` + margin: 2px 0 0 4px; +`; diff --git a/src/components/ExternalLink/index.ts b/src/components/ExternalLink/index.ts new file mode 100644 index 000000000..094bf8d13 --- /dev/null +++ b/src/components/ExternalLink/index.ts @@ -0,0 +1 @@ +export { ExternalLink } from "./ExternalLink"; diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index 9f8f98853..c32dfc040 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -8,7 +8,7 @@ import { import { ReactComponent as DiscordLogo } from "assets/icons/discord-24.svg"; import { ReactComponent as TwitterLogo } from "assets/icons/twitter-24.svg"; -const NAV_LINKS = [ +export const NAV_LINKS = [ { key: "faq", name: "FAQ", diff --git a/src/components/GlobalStyles/GlobalStyles.tsx b/src/components/GlobalStyles/GlobalStyles.tsx index 4019530f7..d34ef073b 100644 --- a/src/components/GlobalStyles/GlobalStyles.tsx +++ b/src/components/GlobalStyles/GlobalStyles.tsx @@ -96,6 +96,10 @@ const globalStyles = css` body { background-color: var(--color-gray); color: var(--color-white); + + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -o-font-smoothing: antialiased; } #root { min-height: 100vh; diff --git a/src/components/Header/Header.styles.tsx b/src/components/Header/Header.styles.tsx index 4840ea7b7..c5ad97f44 100644 --- a/src/components/Header/Header.styles.tsx +++ b/src/components/Header/Header.styles.tsx @@ -85,6 +85,10 @@ export const Item = styled.li` background-color: #e0f3ff; transform: translateX(-50%); } + + svg { + stroke-width: 2px; + } } :hover { @@ -117,3 +121,11 @@ export const MobileNavigation = styled(motion.nav)` margin-left: 8px; } `; + +export const TextWithIcon = styled.div` + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 8px; +`; diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index e8b719cd6..78869ad20 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -16,14 +16,22 @@ import { enableMigration } from "utils"; import { ReactComponent as Logo } from "assets/across-mobile-logo.svg"; import useScrollPosition from "hooks/useScrollPosition"; import { isChildPath } from "./utils"; +import { Text } from "components/Text"; const LINKS = !enableMigration ? [ - { href: "/", name: "Bridge" }, + { href: "/bridge", name: "Bridge" }, { href: "/pool", name: "Pool" }, { href: "/rewards", name: "Rewards" }, { href: "/transactions", name: "Transactions" }, - { href: "/airdrop", name: "Airdrop" }, + { + href: "/airdrop", + name: ( + + Airdrop + + ), + }, ] : []; diff --git a/src/components/IconPair/IconPair.tsx b/src/components/IconPair/IconPair.tsx new file mode 100644 index 000000000..959ba217f --- /dev/null +++ b/src/components/IconPair/IconPair.tsx @@ -0,0 +1,40 @@ +import styled from "@emotion/styled"; +import React from "react"; + +type Props = { + MainIcon: React.ReactElement; + SmallIcon?: React.ReactElement; +}; + +export function IconPair(props: Props) { + return ( + + {props.MainIcon} + {props.SmallIcon && ( + {props.SmallIcon} + )} + + ); +} + +const Container = styled.div` + position: relative; +`; + +const MainIconContainer = styled.div` + height: 32px; + width: 32px; +`; + +const SmallIconContainer = styled.div` + position: absolute; + right: -8px; + bottom: -1px; + height: 18px; + width: 18px; + + svg { + border: 2px solid #3e4047; + border-radius: 50%; + } +`; diff --git a/src/components/IconPair/index.ts b/src/components/IconPair/index.ts new file mode 100644 index 000000000..8b5596d24 --- /dev/null +++ b/src/components/IconPair/index.ts @@ -0,0 +1 @@ +export { IconPair } from "./IconPair"; diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index 039b2b852..5fd1c6fa0 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -1,125 +1,25 @@ import React from "react"; import styled from "@emotion/styled"; -import { COLORS, QUERIES } from "utils"; -import { ReactComponent as UnstyledUmaLogo } from "assets/Across-Powered-UMA.svg"; -import { ReactComponent as SupportLogo } from "assets/support-logo.svg"; -import { ReactComponent as GithubLogo } from "assets/github-logo.svg"; -import { ReactComponent as DocsLogo } from "assets/docs-logo.svg"; -const NAV_LINKS = [ - { - name: "Docs", - url: "https://docs.across.to", - icon: DocsLogo, - }, - { - name: "Support", - url: "https://discord.gg/across", - icon: SupportLogo, - }, - { - name: "Github", - url: "https://github.com/across-protocol", - icon: GithubLogo, - }, -]; +import { COLORS } from "utils"; + +import Footer from "../Footer/Footer"; const Layout: React.FC = ({ children }) => ( - - - {NAV_LINKS.map((link) => ( - - - {link.name} - - ))} - -
{children}
- - - - - -
+ <> + +
{children}
+
+ +