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 000000000..23e606bf8 Binary files /dev/null and b/src/assets/bg-banners/stars-bg.png differ diff --git a/src/assets/check-star-ring-filled.svg b/src/assets/check-star-ring-filled.svg new file mode 100644 index 000000000..c1acce71c --- /dev/null +++ b/src/assets/check-star-ring-filled.svg @@ -0,0 +1,4 @@ + + + + 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}
+
+ +