@@ -44,7 +44,7 @@ export const OtherSection: FC = ({ well }) => {
- Well LP Token - {well.lpToken?.symbol}
+ Well LP Token - {displayTokenSymbol(well.lpToken as Token)}
|
{well.address}
@@ -55,19 +55,27 @@ export const OtherSection: FC = ({ well }) => {
- {well.tokens!.map(function (token, index) {
+ {well.tokens?.map(function (token, index) {
return (
{`Token ${index + 1} - ${token.symbol}`}
|
-
+
{token.address || `-`}
-
+
{token.address.substr(0, 5) + "..." + token.address.substr(token.address.length - 5) || `-`}
@@ -97,6 +105,47 @@ export const OtherSection: FC = ({ well }) => {
);
};
+const loadingItemProps = {
+ sm: { height: 24, width: 100 },
+ lg: { height: 24, width: 200 }
+};
+
+export const OtherSection: FC<{ well: Well | undefined; loading?: boolean }> = ({ well, loading }) => {
+ if (!well || loading) {
+ return (
+
+
+
+
+ {""} |
+ {""}
+ {""}
+
+
+
+ {Array(8)
+ .fill(null)
+ .map((_, idx) => (
+
+
+
+ |
+
+
+
+
+
+
+
+ ))}
+
+
+
+ );
+ }
+ return ;
+};
+
const Detail = styled.span`
color: #4b5563;
font-weight: 600;
@@ -135,3 +184,9 @@ const MobileTh = styled(Th)`
display: none;
}
`;
+
+const LoadingRow = styled(Row)`
+ :hover {
+ cursor: default;
+ }
+`;
diff --git a/projects/dex-ui/src/components/Well/Reserves.tsx b/projects/dex-ui/src/components/Well/Reserves.tsx
index 0ee1047043..794a3c82a6 100644
--- a/projects/dex-ui/src/components/Well/Reserves.tsx
+++ b/projects/dex-ui/src/components/Well/Reserves.tsx
@@ -1,6 +1,6 @@
import React from "react";
import styled from "styled-components";
-import { BodyL, BodyXS, TextNudge } from "../Typography";
+import { BodyL, BodyS, TextNudge } from "../Typography";
import { FC } from "src/types";
import { Token, TokenValue } from "@beanstalk/sdk";
import { TokenLogo } from "../TokenLogo";
@@ -38,7 +38,7 @@ const Symbol = styled.div`
${BodyL}
color: #4B5563;
@media (max-width: ${size.mobile}) {
- ${BodyXS}
+ ${BodyS}
}
`;
const Wrapper = styled.div`
@@ -54,7 +54,7 @@ const Amount = styled.div`
text-align: right;
color: #000000;
@media (max-width: ${size.mobile}) {
- ${BodyXS}
+ ${BodyS}
font-weight: 600;
}
`;
@@ -65,6 +65,6 @@ const Percent = styled.div`
text-align: right;
color: #9ca3af;
@media (max-width: ${size.mobile}) {
- ${BodyXS}
+ ${BodyS}
}
`;
diff --git a/projects/dex-ui/src/components/Well/Table/MyWellPositionRow.tsx b/projects/dex-ui/src/components/Well/Table/MyWellPositionRow.tsx
new file mode 100644
index 0000000000..778d005e57
--- /dev/null
+++ b/projects/dex-ui/src/components/Well/Table/MyWellPositionRow.tsx
@@ -0,0 +1,270 @@
+import { TokenValue } from "@beanstalk/sdk";
+import React, { FC, ReactNode } from "react";
+import { Row, Td } from "src/components/Table";
+import { TokenLogo } from "src/components/TokenLogo";
+import styled from "styled-components";
+import { mediaQuery, size } from "src/breakpoints";
+import { displayTokenSymbol, formatNum, formatUSD } from "src/utils/format";
+import { useWellLPTokenPrice } from "src/wells/useWellLPTokenPrice";
+import { LPBalanceSummary } from "src/tokens/useLPPositionSummary";
+import { useBeanstalkSiloWhitelist } from "src/wells/useBeanstalkSiloWhitelist";
+import { Tooltip } from "src/components/Tooltip";
+import { Well } from "@beanstalk/sdk/Wells";
+import { Skeleton } from "src/components/Skeleton";
+
+import { useNavigate } from "react-router-dom";
+import { useIsMobile } from "src/utils/ui/useIsMobile";
+
+const PositionBreakdown: React.FC<{
+ items: { external: TokenValue; silo: TokenValue; internal: TokenValue; total: TokenValue };
+ isWhitelisted: boolean;
+ isLP: boolean;
+ totalDisplay: string;
+}> = ({ items, isWhitelisted, totalDisplay, isLP = true }) => {
+ const formatFn = isLP ? formatNum : formatUSD;
+ const isMobile = useIsMobile();
+
+ const getTooltipProps = () => {
+ let base = { side: "right", offsetX: 3, offsetY: -100, arrowSize: 4, arrowOffset: 40 };
+
+ if (isMobile) {
+ if (isLP) {
+ base.offsetY = -162;
+ base.arrowOffset = 67;
+ } else {
+ base.side = "left";
+ base.offsetX = -5;
+ base.offsetY = -96;
+ base.arrowOffset = 43;
+ }
+ } else if (!isMobile && !isLP) {
+ base.side = "left";
+ base.offsetX = -10;
+ base.offsetY = -100;
+ }
+
+ return base;
+ };
+
+ return isWhitelisted ? (
+
+
+ {"Wallet Balance:"}
+ {formatFn(items.external)}
+
+
+ {"Silo Deposits:"}
+ {formatFn(items.silo)}
+
+
+ {"Farm Balance:"}
+ {formatFn(items.internal)}
+
+
+ }
+ >
+ {totalDisplay}
+
+ ) : (
+ {totalDisplay}
+ );
+};
+
+export const MyWellPositionRow: FC<{
+ well: Well | undefined;
+ position: LPBalanceSummary | undefined;
+ prices: ReturnType["data"];
+}> = ({ well, position, prices }) => {
+ const navigate = useNavigate();
+ const { getIsWhitelisted } = useBeanstalkSiloWhitelist();
+
+ const lpAddress = well?.lpToken?.address;
+ const lpToken = well?.lpToken;
+
+ if (!well || !position || position.total.lte(0) || !lpAddress || !lpToken) {
+ return null;
+ }
+
+ const tokens = well.tokens || [];
+ const logos: ReactNode[] = [];
+ const symbols: string[] = [];
+ const gotoWell = () => navigate(`/wells/${well.address}`);
+
+ tokens.map((token: any) => {
+ logos.push();
+ symbols.push(token.symbol);
+ });
+
+ const lpPrice = lpAddress && lpAddress in prices ? prices[lpAddress] : undefined;
+ const whitelisted = getIsWhitelisted(well);
+
+ const positionsUSD = {
+ total: lpPrice?.mul(position.total) || TokenValue.ZERO,
+ external: lpPrice?.mul(position.external) || TokenValue.ZERO,
+ silo: lpPrice?.mul(position.silo) || TokenValue.ZERO,
+ internal: lpPrice?.mul(position.internal) || TokenValue.ZERO
+ };
+
+ return (
+
+
+
+ {logos}
+ {symbols.join("/")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {logos}
+ {symbols.join("/")}
+ {/* {deployer} */}
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export const MyWellPositionLoadingRow: FC<{}> = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+const LoadingColumn = styled.div<{ align?: "right" | "left" }>`
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ ${(props) => `
+ align-items: ${props.align === "right" ? "flex-end" : "flex-start"};
+ `}
+
+ ${mediaQuery.sm.only} {
+ gap: 4px;
+ }
+`;
+
+const TableRow = styled(Row)`
+ @media (max-width: ${size.mobile}) {
+ height: 66px;
+ }
+`;
+
+const DesktopContainer = styled(Td)`
+ @media (max-width: ${size.mobile}) {
+ display: none;
+ }
+`;
+
+const MobileContainer = styled(Td)`
+ padding: 8px 16px;
+ @media (min-width: ${size.mobile}) {
+ display: none;
+ }
+`;
+
+const WellDetail = styled.div`
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ @media (min-width: ${size.mobile}) {
+ flex-direction: column;
+ }
+`;
+
+const TokenLogos = styled.div`
+ display: flex;
+ div:not(:first-child) {
+ margin-left: -8px;
+ }
+`;
+const TokenSymbols = styled.div`
+ font-size: 20px;
+ line-height: 24px;
+ color: #1c1917;
+ @media (max-width: ${size.mobile}) {
+ font-size: 14px;
+ margin-top: 2px;
+ }
+`;
+
+const WellLPBalance = styled.div`
+ font-size: 20px;
+ line-height: 24px;
+ @media (max-width: ${size.mobile}) {
+ font-size: 14px;
+ font-weight: normal;
+ }
+`;
+
+const BalanceContainer = styled.div<{ left?: boolean }>`
+ display: flex;
+ justify-content: ${(props) => (props.left ? "flex-start" : "flex-end")};
+`;
+
+const Breakdown = styled.div`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ gap: 4px;
+ @media (max-width: ${size.mobile}) {
+ gap: 0px;
+ }
+`;
+
+const BreakdownRow = styled.div`
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ gap: 4px;
+`;
diff --git a/projects/dex-ui/src/components/Well/Table/WellDetailRow.tsx b/projects/dex-ui/src/components/Well/Table/WellDetailRow.tsx
new file mode 100644
index 0000000000..9f151385d3
--- /dev/null
+++ b/projects/dex-ui/src/components/Well/Table/WellDetailRow.tsx
@@ -0,0 +1,212 @@
+import { TokenValue } from "@beanstalk/sdk";
+import React, { FC, ReactNode } from "react";
+import { useNavigate } from "react-router-dom";
+import { Row, Td } from "src/components/Table";
+import { TokenLogo } from "src/components/TokenLogo";
+import styled from "styled-components";
+import { mediaQuery, size } from "src/breakpoints";
+import { formatNum } from "src/utils/format";
+import { Well } from "@beanstalk/sdk/Wells";
+import { Skeleton } from "src/components/Skeleton";
+
+/// format value with 2 decimals, if value is less than 1M, otherwise use short format
+const formatMayDecimals = (tv: TokenValue | undefined) => {
+ if (!tv) return "-.--";
+ if (tv.lt(1_000_000)) {
+ return formatNum(tv, { minDecimals: 2, maxDecimals: 2 });
+ }
+ return tv.toHuman("short");
+};
+
+export const WellDetailRow: FC<{
+ well: Well | undefined;
+ liquidity: TokenValue | undefined;
+ functionName: string | undefined;
+}> = ({ well, liquidity, functionName }) => {
+ const navigate = useNavigate();
+
+ if (!well) return null;
+
+ const tokens = well?.tokens || [];
+ const logos: ReactNode[] = [];
+ const smallLogos: ReactNode[] = [];
+ const symbols: string[] = [];
+ const gotoWell = () => navigate(`/wells/${well?.address}`);
+
+ tokens.map((token: any) => {
+ logos.push();
+ smallLogos.push();
+ symbols.push(token.symbol);
+ });
+
+ return (
+
+
+
+ {logos}
+ {symbols.join("/")}
+
+
+
+ {functionName || "Price Function"}
+
+
+ 0.00%
+
+
+ ${liquidity ? liquidity.toHuman("short") : "-.--"}
+
+
+
+ {{smallLogos[0]}}
+ {formatMayDecimals(well.reserves?.[0])}
+
+
+ {{smallLogos[1]}}
+ {formatMayDecimals(well.reserves?.[1])}
+
+ {well.reserves && well.reserves.length > 2 ? {`+ ${well.reserves.length - 2} MORE`} : null}
+
+
+
+ {logos}
+ {symbols.join("/")}
+
+ ${formatNum(liquidity, { minDecimals: 2 })}
+
+
+ );
+};
+
+export const WellDetailLoadingRow: FC<{}> = () => {
+ return (
+ {}}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const LoadingColumn = styled.div<{ align?: "right" | "left" }>`
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ ${(props) => `
+ align-items: ${props.align === "right" ? "flex-end" : "flex-start"};
+ `}
+
+ ${mediaQuery.sm.only} {
+ gap: 4px;
+ }
+`;
+
+const TableRow = styled(Row)`
+ @media (max-width: ${size.mobile}) {
+ height: 66px;
+ }
+`;
+
+const DesktopContainer = styled(Td)`
+ @media (max-width: ${size.mobile}) {
+ display: none;
+ }
+`;
+
+const MobileContainer = styled(Td)`
+ padding: 8px 16px;
+ @media (min-width: ${size.mobile}) {
+ display: none;
+ }
+`;
+
+const WellDetail = styled.div`
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ @media (min-width: ${size.mobile}) {
+ flex-direction: column;
+ }
+`;
+
+const TokenLogos = styled.div`
+ display: flex;
+ div:not(:first-child) {
+ margin-left: -8px;
+ }
+`;
+const TokenSymbols = styled.div`
+ font-size: 20px;
+ line-height: 24px;
+ color: #1c1917;
+ @media (max-width: ${size.mobile}) {
+ font-size: 14px;
+ margin-top: 2px;
+ }
+`;
+
+const Amount = styled.div`
+ font-weight: 500;
+ font-size: 20px;
+ line-height: 24px;
+ color: #1c1917;
+
+ @media (max-width: ${size.mobile}) {
+ font-size: 14px;
+ font-weight: normal;
+ }
+`;
+
+const Reserves = styled.div`
+ display: flex;
+ flex-direction: row;
+ justify-content flex-end;
+ align-items: center;
+ gap: 4px;
+ flex: 1;
+`;
+
+const MoreReserves = styled.div`
+ color: #9ca3af;
+`;
+
+const TradingFee = styled.div`
+ font-size: 20px;
+ line-height: 24px;
+ color: #4b5563;
+ text-transform: uppercase;
+`;
+
+const WellPricing = styled.div`
+ font-size: 20px;
+ line-height: 24px;
+ text-transform: capitalize;
+`;
+
+const TokenLogoWrapper = styled.div`
+ margin-bottom: 2px;
+`;
diff --git a/projects/dex-ui/src/pages/Home.tsx b/projects/dex-ui/src/pages/Home.tsx
index 0d3482ff32..0ce89f0893 100644
--- a/projects/dex-ui/src/pages/Home.tsx
+++ b/projects/dex-ui/src/pages/Home.tsx
@@ -1,67 +1,126 @@
/* eslint-disable jsx-a11y/accessible-emoji */
import React from "react";
-import { size } from "src/breakpoints";
-import { Link } from "react-router-dom";
-import { RightArrowCircle } from "src/components/Icons";
+import { mediaQuery } from "src/breakpoints";
+
import styled from "styled-components";
+import shapesIcons from "src/assets/images/home-banner.svg";
+import { BodyL } from "src/components/Typography";
+import { ContractInfoMarquee } from "src/components/Frame/ContractInfoMarquee";
+
+const copy = {
+ build: "Use components written, audited and deployed by other developers for your custom liquidity pool.",
+ deploy: "Liquidity pools with unique pricing functions for more granular market making.",
+ fees: "Trade assets using liquidity pools that donโt impose trading fees."
+};
+
+const links = {
+ multiFlowPump: "/multi-flow-pump.pdf",
+ whitepaper: "/basin.pdf",
+ docs: "https://docs.basin.exchange/",
+ wells: "/#/wells",
+ swap: "/#/swap"
+};
export const Home = () => {
return (
-
-
-
-
- ๐ฎ Multi-block MEV manipulation resistant oracle{" "}
-
- whitepaper
-
-
-
-
- A Composable EVM-native DEX
-
- Customizable liquidity pools with shared components.
-
- Read the whitepaper โ
-
-
-
-
-
-
- ๐ฎ
- {" "}
- Build using components
-
-
-
- โก๏ธ
- {" "}
- Deploy flexible liquidity
-
-
-
- โค๏ธ
- {" "}
- Zero-fee swaps
-
-
-
-
+ <>
+
+
+
+
+
+ Multi-Flow Pump is here!
+
+ Explore the multi-block MEV manipulation resistant Oracle framework, with easy
+ integration for everyone.
+
+
+
+ Get Started โ
+
+
+
+
+
+ A Composable EVM-native DEX
+
+ Customizable liquidity pools with shared components.
+
+ Read the whitepaper โ
+
+
+
+
+
+
+
+ ๐ฎ
+
+ Build using components
+
+ {copy.build}
+
+
+
+
+
+ โก๏ธ
+
+ Deploy flexible liquidity
+
+
+ {copy.deploy}
+
+
+
+
+
+ โค๏ธ
+
+ Zero-fee swaps
+
+
+ {copy.fees}
+
+
+
+
+
+
+
+
+ >
);
};
+const MarqueeContainer = styled.div`
+ position: fixed;
+ bottom: 72px;
+
+ ${mediaQuery.sm.only} {
+ position: fixed;
+ left: 0;
+ bottom: 0;
+ }
+`;
+
const Container = styled.div`
height: calc(100% - 24px);
- padding: 12px;
- @media (min-width: ${size.mobile}) {
- padding: 0px;
+ padding-top: 12px;
+ padding-left: 12px;
+ padding-right: 12px;
+ padding-bottom: 0px;
+
+ ${mediaQuery.sm.up} {
+ padding-top: 32px;
+ padding-left: 48px;
+ padding-right: 48px;
+
height: 100%;
width: 100%;
justify-content: center;
align-items: center;
+ box-sizing: border-box;
}
`;
@@ -70,29 +129,91 @@ const Content = styled.div`
flex-direction: column;
justify-content: space-between;
height: 100%;
- @media (min-width: ${size.mobile}) {
- gap: 48px;
- justify-content: center;
+
+ ${mediaQuery.sm.up} {
+ justify-content: space-between;
align-items: center;
}
`;
-const MevBubble = styled.div`
- display: none;
- @media (min-width: ${size.mobile}) {
- display: flex;
- box-sizing: border-box;
- flex-direction: row;
- justify-content: center;
+const MevBanner = styled.div`
+ background: #fff;
+ width: 100%;
+ border: 0.25px solid #9ca3af;
+ ${mediaQuery.sm.only} {
+ display: none;
+ }
+`;
+
+const MevBannerBG = styled.div`
+ background: url(${shapesIcons});
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: right;
+
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ height: auto;
+ padding: 24px;
+ width: 100%;
+ box-sizing: border-box;
+`;
+
+const MevInfo = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+`;
+
+const MevTitle = styled.div`
+ ${BodyL}
+`;
+
+const GetStartedContainer = styled.a`
+ :focus {
+ text-decoration: none;
+ }
+`;
+
+const GetStarted = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 12px;
+ background: #000;
+ outline: 0.5px solid #000;
+ color: #fff;
+ font-weight: 600;
+ font-size: 16px;
+ line-height: 24px;
+ letter-spacing: 0.32px;
+ white-space: nowrap;
+ cursor: pointer;
+
+ :hover {
+ outline: 2px solid #46b955;
+ }
+
+ :focus {
+ outline: 2px solid #46b955;
+ }
+`;
+
+const InfoContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ box-sizing: border-box;
+ height: 100%;
+
+ ${mediaQuery.sm.up} {
+ padding-top: min(25%, 185px);
+ justify-content: flex-start
align-items: center;
- padding: 8px;
- gap: 8px;
- height: 40px;
- line-height: 16px;
- width: 522px;
- background: #ffffff;
- border: 0.25px solid #4b5563;
- border-radius: 100px;
+ width: 100%;
+ gap: 72px;
}
`;
@@ -100,9 +221,8 @@ const TitleSubtitleContainer = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
- @media (min-width: ${size.mobile}) {
- display: flex;
- flex-direction: column;
+ ${mediaQuery.sm.up} {
+ align-items: center;
gap: 48px;
}
`;
@@ -111,10 +231,11 @@ const Title = styled.div`
font-size: 32px;
font-weight: 600;
line-height: 40px;
- @media (min-width: ${size.mobile}) {
+ ${mediaQuery.sm.up} {
font-style: normal;
font-size: 72px;
line-height: 100%;
+ text-align: center;
}
`;
@@ -128,7 +249,7 @@ const SubTitle = styled.div`
line-height: 22px;
color: #4b5563;
gap: 8px;
- @media (min-width: ${size.mobile}) {
+ ${mediaQuery.sm.up} {
flex-direction: row;
font-size: 20px;
line-height: 24px;
@@ -138,16 +259,6 @@ const SubTitle = styled.div`
}
`;
-const OracleWP = styled.a`
- color: #46b955;
- text-decoration: none;
- display: flex;
- align-items: center;
- :hover {
- text-decoration: underline;
- }
-`;
-
const WhitepaperLink = styled.a`
font-weight: 400;
font-size: 14px;
@@ -157,65 +268,129 @@ const WhitepaperLink = styled.a`
text-decoration: none;
display: flex;
align-items: center;
+ white-space: nowrap;
+ margin-left: 4px;
:hover {
text-decoration: underline;
}
- @media (min-width: ${size.mobile}) {
+ ${mediaQuery.sm.up} {
font-size: 20px;
line-height: 24px;
}
`;
-const Boxes = styled.div`
- box-sizing: border-box;
+const AccordionContainer = styled.div`
+ /// Desktop
display: flex;
- flex-direction: column;
- gap: 12px;
- justify-content: space-around;
- position: fixed;
- bottom: 12px;
- width: calc(100vw - 24px);
- @media (min-width: ${size.mobile}) {
- flex-direction: row;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ gap: 24px;
+ width: 100%;
+
+ /// Tablet
+ ${mediaQuery.md.only} {
+ width: 100%;
+ flex-direction: column;
+ gap: 12px;
+ }
+
+ /// Mobile
+ ${mediaQuery.sm.only} {
+ flex-direction: column;
position: relative;
- bottom: 0px;
- gap: 48px;
- padding: 0 48px;
- width: 100vw;
+ bottom: calc(57px + 12px); // 57px is the height of the contract info marquee
+ gap: 12px;
+ justify-content: space-around;
+ position: fixed;
+ width: calc(100vw - 24px);
}
`;
-const Box = styled(Link)`
+const Emoji = styled.span`
+ margin-right: 4px;
+`;
+
+const AccordionItem = styled.a`
display: flex;
+ flex-direction: column;
justify-content: center;
align-items: center;
-
- background: #f9f8f6;
+ background-color: #f9f9f9;
+ color: #444;
+ cursor: pointer;
+ padding: 24px;
border: 0.5px solid #4b5563;
- flex-grow: 1;
-
- font-weight: 600;
- font-size: 14px;
- line-height: 22px;
- padding: 12px;
-
+ outline: 1.5px solid white;
+ text-align: left;
+ width: 33%;
+ transition: background-color 0.3s ease;
+ overflow: hidden;
+ max-height: 113px; // Initial max-height
+ box-sizing: border-box;
text-decoration: none;
- color: black;
- :hover {
+ &:hover {
+ border: 1.5px solid #46b955;
background-color: #f0fdf4;
+ outline: 0.5px solid transparent;
+ max-height: 250px; // Adjust as needed for your content
}
- @media (min-width: ${size.mobile}) {
- padding: 0px;
- font-size: 24px;
- line-height: 32px;
- height: 80px;
+ ${mediaQuery.md.up} {
+ padding: 24px;
+ height: 100%;
+ }
+
+ ${mediaQuery.md.only} {
+ width: calc(100vw - 86px);
+ height: auto;
+ :last-child {
+ margin-bottom: 24px;
+ }
+ }
+
+ ${mediaQuery.sm.only} {
+ width: calc(100vw - 24px);
+ max-height: 80px;
+ padding: 12px;
}
`;
-const Emoji = styled.span`
- margin-right: 4px;
+const AccordionContent = styled.div`
+ overflow: hidden;
+ opacity: 0; // Initially hidden
+ transition: opacity 0.3s ease-out, max-height 0.3s ease-out;
+ max-height: 0;
+ width: 100%; // Ensure it takes full width
+
+ ${AccordionItem}:hover & {
+ padding-top: 12px;
+ opacity: 1;
+ max-height: 200px; // Adjust as needed for your content
+ }
+
+ ${mediaQuery.sm.only} {
+ display: none;
+ }
+`;
+
+const AccordionTitle = styled.div`
+ text-align: center;
+ width: 100%;
+ font-weight: 600;
+ font-size: 24px;
+ line-height: 32px;
+
+ ${mediaQuery.md.only} {
+ font-size: 20px;
+ line-height: 24px;
+ }
+
+ ${mediaQuery.sm.only} {
+ font-size: 14px;
+ line-height: 22px;
+ }
`;
diff --git a/projects/dex-ui/src/pages/Liquidity.tsx b/projects/dex-ui/src/pages/Liquidity.tsx
index 2e6cf729a2..50f29b6a45 100644
--- a/projects/dex-ui/src/pages/Liquidity.tsx
+++ b/projects/dex-ui/src/pages/Liquidity.tsx
@@ -1,6 +1,5 @@
-import React, { useCallback, useEffect, useState } from "react";
-import { useNavigate, useParams } from "react-router-dom";
-import { useWell } from "src/wells/useWell";
+import React, { useCallback, useEffect, useRef, useState } from "react";
+import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import { Page } from "src/components/Page";
import { LiquidityBox } from "src/components/Well/LiquidityBox";
@@ -16,26 +15,19 @@ import { Log } from "src/utils/logger";
import { BodyXS, TextNudge } from "src/components/Typography";
import { ImageButton } from "src/components/ImageButton";
import { ChevronDown } from "src/components/Icons";
-import { size } from "src/breakpoints";
-import { Loading } from "../components/Loading";
-import { Error } from "../components/Error";
+import { mediaQuery, size } from "src/breakpoints";
+import { Error } from "src/components/Error";
+import { LoadingItem } from "src/components/LoadingItem";
+import { useWellWithParams } from "src/wells/useWellWithParams";
export const Liquidity = () => {
- const { address: wellAddress } = useParams<"address">();
+ const { well, loading, error } = useWellWithParams();
const navigate = useNavigate();
- const { well, loading, error } = useWell(wellAddress!);
+
const [wellFunctionName, setWellFunctionName] = useState("This Well's Function");
- const [isMobile, setIsMobile] = useState(window.matchMedia(`(max-width: ${size.mobile})`).matches);
- const [tab, setTab] = useState(isMobile ? null : 0);
+ const [tab, setTab] = useState(0);
- // Media query
- useEffect(() => {
- window.matchMedia(`(max-width: ${size.mobile})`).addEventListener("change", (event) => setIsMobile(event.matches));
-
- return () => {
- window.matchMedia(`(max-width: ${size.mobile})`).removeEventListener("change", (event) => setIsMobile(event.matches));
- };
- }, []);
+ const scrollRef = useRef(null);
// Slippage-related
const [showSlippageSettings, setShowSlippageSettings] = useState(false);
@@ -49,7 +41,6 @@ export const Liquidity = () => {
Log.module("liquidity").debug(`Slippage changed: ${parseFloat(value)}`);
setSlippage(parseFloat(value));
};
- // /Slippage-related
const [open, setOpen] = useState(false);
const toggle = useCallback(() => {
@@ -66,8 +57,6 @@ export const Liquidity = () => {
run();
}, [well]);
- if (loading) return ;
-
if (error) {
return ;
}
@@ -76,58 +65,68 @@ export const Liquidity = () => {
-
-
+
+
+
-
- setTab(isMobile && tab === 0 ? null : 0)} active={tab === 0} stretch bold justify hover>
- Add Liquidity
+ setTab(0)} active={tab === 0} stretch bold justify hover>
+ {""}>}>
+ Add Liquidity
+
-
- setTab(isMobile && tab === 1 ? null : 1)} active={tab === 1} stretch bold justify hover>
- Remove Liquidity
+ setTab(1)} active={tab === 1} stretch bold justify hover>
+ {""}>}>
+ Remove Liquidity
+
{tab === 0 && (
{
)}
{tab === 1 && (
)}
-
);
};
const ContentWrapper = styled.div`
- // outline: 1px solid red;
display: flex;
flex-direction: row;
- justify-content: center;
gap: 48px;
- @media (max-width: ${size.mobile}) {
+
+ ${mediaQuery.lg.down} {
flex-direction: column;
gap: 16px;
}
+
+ ${mediaQuery.lg.only} {
+ justify-content: flex-start;
+ }
`;
const SideBar = styled.div`
- // outline: 1px solid green;
display: flex;
flex-direction: column;
width: calc(16 * 24px);
min-width: calc(16 * 24px);
gap: 24px;
- @media (max-width: ${size.mobile}) {
+
+ ${mediaQuery.lg.down} {
width: 100%;
min-width: 100%;
gap: 16px;
@@ -175,13 +177,17 @@ const SideBar = styled.div`
`;
const CenterBar = styled.div`
- // outline: 1px solid green;
display: flex;
flex-direction: column;
- width: calc(17 * 24px);
- min-width: calc(17 * 24px);
gap: 24px;
- @media (max-width: ${size.mobile}) {
+ width: 100%;
+
+ ${mediaQuery.md.up} {
+ width: calc(17 * 24px);
+ min-width: calc(17 * 24px);
+ }
+
+ ${mediaQuery.md.down} {
width: 100%;
min-width: 100%;
gap: 16px;
@@ -206,16 +212,18 @@ const LearnMoreContainer = styled.div`
gap: 16px;
order: 1;
width: 100%;
+
@media (min-width: ${size.mobile}) {
gap: 24px;
order: 0;
}
`;
const LearnMoreLabel = styled.div`
- display: flex;
- flex-direction: row;
- @media (min-width: ${size.mobile}) {
- display: none;
+ display: none;
+
+ ${mediaQuery.lg.down} {
+ display: flex;
+ flex-direction: row;
}
`;
@@ -240,11 +248,19 @@ const LearnMoreText = styled.div`
`;
const LearnMoreButtons = styled.div<{ open: boolean }>`
- ${(props) => (props.open ? "display: flex" : "display: none")};
+ display: flex;
flex-direction: column;
- gap: 16px;
- @media (min-width: ${size.mobile}) {
- display: flex;
- gap: 24px;
+ gap: 24px;
+
+ ${mediaQuery.lg.down} {
+ ${(props) => (props.open ? "display: flex" : "display: none")};
+ gap: 16px;
}
`;
+
+const EmptyLearnItem = styled.div`
+ width: 100%;
+ height: 48px;
+ border: 0.5px solid #9ca3af;
+ background: #f9f8f6;
+`;
diff --git a/projects/dex-ui/src/pages/Swap.tsx b/projects/dex-ui/src/pages/Swap.tsx
index 1d6ef27cc0..04a5ac5564 100644
--- a/projects/dex-ui/src/pages/Swap.tsx
+++ b/projects/dex-ui/src/pages/Swap.tsx
@@ -1,13 +1,42 @@
import React from "react";
+import { size } from "src/breakpoints";
+import { LoadingTemplate } from "src/components/LoadingTemplate";
import { Page } from "src/components/Page";
import { Title } from "src/components/PageComponents/Title";
import { SwapRoot } from "src/components/Swap/SwapRoot";
+import { useWellTokens } from "src/tokens/useWellTokens";
+import styled from "styled-components";
export const Swap = () => {
+ const { isLoading, data } = useWellTokens();
+
+ const loading = !data || isLoading || !data.length;
+
return (
-
-
+
+ {loading ? (
+
+
+
+
+
+
+
+ ) : (
+
+ )}
);
};
+
+const Container = styled.div`
+ width: 384px;
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+ @media (max-width: ${size.mobile}) {
+ width: 100%;
+ gap: 16px;
+ }
+`;
diff --git a/projects/dex-ui/src/pages/Well.tsx b/projects/dex-ui/src/pages/Well.tsx
index 30cc6319ef..d257382c2c 100644
--- a/projects/dex-ui/src/pages/Well.tsx
+++ b/projects/dex-ui/src/pages/Well.tsx
@@ -1,6 +1,5 @@
import React, { ReactNode, useCallback, useEffect, useRef, useState } from "react";
-import { useNavigate, useParams } from "react-router-dom";
-import { useWell } from "src/wells/useWell";
+import { useNavigate } from "react-router-dom";
import { getPrice } from "src/utils/price/usePrice";
import useSdk from "src/utils/sdk/useSdk";
import { TokenValue } from "@beanstalk/sdk";
@@ -22,15 +21,17 @@ import { OtherSection } from "src/components/Well/OtherSection";
import { WellHistory } from "src/components/Well/Activity/WellHistory";
import { ChevronDown } from "src/components/Icons";
import { ImageButton } from "src/components/ImageButton";
-import { size } from "src/breakpoints";
-import { Loading } from "src/components/Loading";
-import { Error } from "../components/Error";
+import { mediaQuery, size } from "src/breakpoints";
+import { Error } from "src/components/Error";
+import { useWellWithParams } from "src/wells/useWellWithParams";
+import { LoadingItem } from "src/components/LoadingItem";
+import { LoadingTemplate } from "src/components/LoadingTemplate";
export const Well = () => {
+ const { well, loading: loading, error } = useWellWithParams();
+
const sdk = useSdk();
const navigate = useNavigate();
- const { address: wellAddress } = useParams<"address">();
- const { well, loading, error } = useWell(wellAddress!);
const [prices, setPrices] = useState<(TokenValue | null)[]>([]);
const [wellFunctionName, setWellFunctionName] = useState("-");
@@ -46,9 +47,9 @@ export const Well = () => {
}, [open]);
useEffect(() => {
- const run = async () => {
- if (!well?.tokens) return;
+ if (!well?.tokens) return;
+ const run = async () => {
if (well.tokens) {
const prices = await Promise.all(well.tokens.map((t) => getPrice(t, sdk)));
setPrices(prices);
@@ -121,63 +122,116 @@ export const Well = () => {
);
// Code above detects if the component with the Add/Remove Liq + Swap buttons is sticky
- if (loading) return ;
-
if (error) return ;
return (
-
+
+
+ {/*
+ *Header
+ */}
- -
-
- {logos}
-
- {title}
-
-
-
-
- {wellFunctionName}
- 0.00% Trading Fee
-
+ }>
+ -
+
+ {logos}
+
+ {title}
+
+
+
+
+ {wellFunctionName}
+ 0.00% Trading Fee
+
+
+
+ {/*
+ * Reserves
+ */}
-
+ }>
+
+
-
-
-
+
+ {/*
+ * Chart Section
+ */}
+
+
+
+
+ {/*
+ * Chart Type Button Selectors
+ */}
- -
- showTab(e, 0)} active={tab === 0} stretch justify bold hover>
- Activity
-
-
- -
- showTab(e, 1)} active={tab === 1} stretch justify bold hover>
- Contract Addresses
-
-
+ }>
+ -
+ showTab(e, 0)} active={tab === 0} stretch justify bold hover>
+ Activity
+
+
+ -
+ showTab(e, 1)} active={tab === 1} stretch justify bold hover>
+ Contract Addresses
+
+
+
+
+ {/*
+ * Well History & Contract Info Tables
+ */}
- {tab === 0 && }
- {tab === 1 && }
+ {tab === 0 && }
+ {tab === 1 && }
+
+ {/*
+ * UI Helpers
+ */}
-
- -
-
-
- -
-
-
-
+
+ {/*
+ * Liquidity Swap Buttons
+ * We render both Mobile & Desktop to prevent flex order switching animations from happening on page width changes
+ */}
+
+ }>
+ -
+
+
+ -
+
+
+
+
+
+ }>
+ -
+
+
+ -
+
+
+
+
+
+ {/*
+ * Liquidity Box
+ */}
-
+
+
+ {/*
+ * Learn More
+ */}
@@ -196,9 +250,15 @@ export const Well = () => {
-
-
-
+ }>
+
+
+ }>
+
+
+ }>
+
+
@@ -209,19 +269,25 @@ export const Well = () => {
const leftColumnWidth = 940;
const rightColumnWidth = 400;
+const calcWellContentMaxWidth = `min(calc(100% - 48px - 400px), ${leftColumnWidth}px)`;
+
const ContentWrapper = styled.div`
- // outline: 1px solid red;
display: flex;
flex-flow: column wrap;
flex: auto;
justify-content: flex-start;
- align-content: center;
+ align-content: start;
gap: 24px;
- @media (min-width: ${size.mobile}) {
+ width: 100%;
+
+ ${mediaQuery.lg.only} {
height: 1400px;
}
- @media (max-width: ${size.mobile}) {
- flex-flow: column nowrap;
+
+ ${mediaQuery.between.smAndLg} {
+ max-width: ${size.mobile};
+ flex: 2;
+ align-self: center;
}
`;
@@ -236,7 +302,7 @@ const Header = styled.div`
line-height: 32px;
gap: 24px;
- @media (max-width: ${size.mobile}) {
+ ${mediaQuery.lg.down} {
font-size: 24px;
gap: 8px;
}
@@ -250,8 +316,13 @@ const TokenLogos = styled.div`
`;
const HeaderContainer = styled(Row)`
- width: ${leftColumnWidth}px;
- @media (max-width: ${size.mobile}) {
+ ${mediaQuery.lg.only} {
+ display: flex;
+ max-width: ${calcWellContentMaxWidth};
+ width: 100%;
+ }
+
+ ${mediaQuery.lg.down} {
display: flex;
width: 100%;
flex-direction: column;
@@ -259,22 +330,30 @@ const HeaderContainer = styled(Row)`
gap: 8px;
order: 0;
}
+
+ ${mediaQuery.md.up} {
+ align-item: space-between;
+ }
`;
const ReservesContainer = styled.div`
width: 100%;
order: 3;
- @media (min-width: ${size.mobile}) {
- width: ${leftColumnWidth}px;
+
+ ${mediaQuery.lg.only} {
+ max-width: ${calcWellContentMaxWidth};
+ width: 100%;
order: 0;
}
`;
-const ChartContainer = styled.div`
+const ChartSectionContainer = styled.div`
width: 100%;
order: 4;
- @media (min-width: ${size.mobile}) {
- width: ${leftColumnWidth}px;
+
+ ${mediaQuery.lg.only} {
+ display: block;
+ max-width: ${calcWellContentMaxWidth};
order: 0;
}
`;
@@ -282,8 +361,10 @@ const ChartContainer = styled.div`
const ActivityOtherButtons = styled(Row)`
width: 100%;
order: 5;
- @media (min-width: ${size.mobile}) {
- width: ${leftColumnWidth}px;
+
+ ${mediaQuery.lg.only} {
+ max-width: ${calcWellContentMaxWidth};
+ width: 100%;
order: 0;
}
`;
@@ -295,63 +376,95 @@ const StickyDetector = styled.div`
background-color: transparent;
margin-bottom: -24px;
order: 2;
- @media (min-width: ${size.mobile}) {
+
+ ${mediaQuery.sm.up} {
display: none;
}
`;
-const LiquiditySwapButtons = styled(Row)<{ sticky?: boolean }>`
- width: ${(props) => (props.sticky ? "100vw" : "100%")};
- margin-left: ${(props) => (props.sticky ? "-12px" : "0px")};
+const LiquiditySwapButtonsMobile = styled(Row)<{ sticky?: boolean }>`
+ width: 100%;
order: 2;
- position: sticky;
top: 0px;
z-index: 10;
transition: all 0.3s ease-in-out;
- @media (min-width: ${size.mobile}) {
- margin-top: 48px;
- width: ${rightColumnWidth}px;
- position: relative;
- margin-left: 0px;
- order: 0;
+ gap: 8px;
+
+ ${mediaQuery.md.only} {
+ max-width: ${size.mobile};
+ }
+
+ ${mediaQuery.sm.only} {
+ position: sticky;
+ gap: ${(props) => (props.sticky ? "0px" : "8px")};
+ margin-left: ${(props) => (props.sticky ? "-12px" : "0px")};
+ width: ${(props) => (props.sticky ? "100vw" : "100%")};
+ }
+
+ ${mediaQuery.lg.only} {
+ display: none;
+ }
+`;
+
+const LiquiditySwapButtonsDesktop = styled(Row)`
+ max-width: ${rightColumnWidth}px;
+ width: 100%;
+ order: 0;
+ margin-top: 48px;
+ position: relative;
+ margin-left: 0px;
+ transition: all 0.3s ease-in-out;
+ top: 0px;
+ z-index: 10;
+
+ ${mediaQuery.lg.down} {
+ display: none;
}
`;
const StyledItem = styled(Item)`
- @media (min-width: ${size.mobile}) {
- align-items: end;
+ align-items: flex-start;
+
+ ${mediaQuery.lg.only} {
+ align-items: flex-end;
}
`;
+
const BottomContainer = styled.div`
display: flex;
flex-direction: column;
gap: 24px;
width: 100%;
order: 6;
- @media (min-width: ${size.mobile}) {
- width: ${leftColumnWidth}px;
+
+ ${mediaQuery.lg.only} {
+ max-width: ${calcWellContentMaxWidth};
+ width: 100%;
order: 0;
}
`;
const FunctionName = styled.div`
${BodyL}
- @media (max-width: ${size.mobile}) {
+
+ ${mediaQuery.lg.down} {
${BodyS}
}
`;
const Fee = styled.div`
${BodyS}
color: #4B5563;
- @media (max-width: ${size.mobile}) {
+ ${mediaQuery.lg.down} {
${BodyXS}
}
`;
const LiquidityBoxContainer = styled.div`
- width: ${rightColumnWidth}px;
- @media (max-width: ${size.mobile}) {
- display: none;
+ display: none;
+
+ ${mediaQuery.lg.only} {
+ display: block;
+ max-width: ${rightColumnWidth}px;
}
`;
@@ -361,16 +474,17 @@ const LearnMoreContainer = styled.div`
gap: 16px;
order: 1;
width: 100%;
- @media (min-width: ${size.mobile}) {
- width: ${rightColumnWidth}px;
- gap: 24px;
+
+ ${mediaQuery.lg.only} {
+ max-width: ${rightColumnWidth}px;
order: 0;
+ gap: 24px;
}
`;
const LearnMoreLabel = styled.div`
display: flex;
flex-direction: row;
- @media (min-width: ${size.mobile}) {
+ ${mediaQuery.lg.only} {
display: none;
}
`;
@@ -399,7 +513,8 @@ const LearnMoreButtons = styled.div<{ open: boolean }>`
${(props) => (props.open ? "display: flex" : "display: none")};
flex-direction: column;
gap: 16px;
- @media (min-width: ${size.mobile}) {
+
+ ${mediaQuery.lg.only} {
display: flex;
gap: 24px;
}
@@ -407,9 +522,86 @@ const LearnMoreButtons = styled.div<{ open: boolean }>`
const ColumnBreak = styled.div`
display: none;
- @media (min-width: ${size.mobile}) {
+
+ ${mediaQuery.lg.only} {
display: block;
flex-basis: 100%;
width: 0px;
}
`;
+
+const EmptyLearnItem = styled.div`
+ width: 100%;
+ height: 48px;
+ border: 0.5px solid #9ca3af;
+ background: #f9f8f6;
+`;
+
+const MobileOnlyTokenLogoContainer = styled.div`
+ display: none;
+ ${mediaQuery.sm.only} {
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ margin-top: 6px;
+ }
+`;
+
+const NonMobileTokenLogoContainer = styled.div`
+ display: block;
+
+ ${mediaQuery.sm.only} {
+ display: none;
+ }
+`;
+
+const SkeletonHeader: React.FC<{}> = () => (
+ <>
+ -
+
+
+
+
+
+
+ >
+);
+
+const SkeletonReserves: React.FC<{}> = () => {
+ return (
+
+ {Array(2)
+ .fill(null)
+ .map((_, i) => (
+
+
+
+
+
+
+
+
+
+ ))}
+
+ );
+};
+
+const SkeletonButtonsRow: React.FC<{}> = () => (
+ <>
+ -
+
+
+ -
+
+
+ >
+);
diff --git a/projects/dex-ui/src/pages/Wells.tsx b/projects/dex-ui/src/pages/Wells.tsx
index 93ecf0c3e1..80b848139b 100644
--- a/projects/dex-ui/src/pages/Wells.tsx
+++ b/projects/dex-ui/src/pages/Wells.tsx
@@ -1,89 +1,25 @@
import { TokenValue } from "@beanstalk/sdk";
-import React, { FC, ReactNode, useMemo, useState } from "react";
-import { useNavigate } from "react-router-dom";
+import React, { useMemo, useState } from "react";
import { Item } from "src/components/Layout";
import { Page } from "src/components/Page";
import { Title } from "src/components/PageComponents/Title";
import { TabButton } from "src/components/TabButton";
-import { Row, TBody, THead, Table, Td, Th } from "src/components/Table";
+import { Row, TBody, THead, Table, Th } from "src/components/Table";
import { Row as TabRow } from "src/components/Layout";
-import { TokenLogo } from "src/components/TokenLogo";
import { getPrice } from "src/utils/price/usePrice";
import useSdk from "src/utils/sdk/useSdk";
import { useWells } from "src/wells/useWells";
import styled from "styled-components";
-import { size } from "src/breakpoints";
-import { Loading } from "../components/Loading";
-import { Error } from "../components/Error";
-import { displayTokenSymbol, formatNum, formatUSD } from "src/utils/format";
+import { mediaQuery, size } from "src/breakpoints";
+import { Error } from "src/components/Error";
import { useWellLPTokenPrice } from "src/wells/useWellLPTokenPrice";
-import { LPBalanceSummary, useLPPositionSummary } from "src/tokens/useLPPositionSummary";
-import { useBeanstalkSiloWhitelist } from "src/wells/useBeanstalkSiloWhitelist";
-import { Tooltip } from "src/components/Tooltip";
-import { Well } from "@beanstalk/sdk/Wells";
-import useIsMobile from "src/utils/ui/useIsMobile";
+import { useLPPositionSummary } from "src/tokens/useLPPositionSummary";
-const PositionBreakdown: React.FC<{
- items: { external: TokenValue; silo: TokenValue; internal: TokenValue; total: TokenValue };
- isWhitelisted: boolean;
- isLP: boolean;
- totalDisplay: string;
-}> = ({ items, isWhitelisted, totalDisplay, isLP = true }) => {
- const formatFn = isLP ? formatNum : formatUSD;
- const isMobile = useIsMobile();
-
- const getTooltipProps = () => {
- let base = { side: "right", offsetX: 3, offsetY: -100, arrowSize: 4, arrowOffset: 40 };
-
- if (isMobile) {
- if (isLP) {
- base.offsetY = -162;
- base.arrowOffset = 67;
- } else {
- base.side = "left";
- base.offsetX = -5;
- base.offsetY = -96;
- base.arrowOffset = 43;
- }
- } else if (!isMobile && !isLP) {
- base.side = "left";
- base.offsetX = -10;
- base.offsetY = -100;
- }
-
- return base;
- };
-
- return isWhitelisted ? (
-
-
- {"Wallet Balance:"}
- {formatFn(items.external)}
-
-
- {"Silo Deposits:"}
- {formatFn(items.silo)}
-
-
- {"Farm Balance:"}
- {formatFn(items.internal)}
-
-
- }
- >
- {totalDisplay}
-
- ) : (
- {totalDisplay}
- );
-};
+import { WellDetailLoadingRow, WellDetailRow } from "src/components/Well/Table/WellDetailRow";
+import { MyWellPositionLoadingRow, MyWellPositionRow } from "src/components/Well/Table/MyWellPositionRow";
export const Wells = () => {
const { data: wells, isLoading, error } = useWells();
- const navigate = useNavigate();
const sdk = useSdk();
const [wellLiquidity, setWellLiquidity] = useState<(TokenValue | undefined)[]>([]);
@@ -94,9 +30,6 @@ export const Wells = () => {
const { hasPositions, getPositionWithWell } = useLPPositionSummary();
- const { getIsWhitelisted } = useBeanstalkSiloWhitelist();
- const isMobile = useIsMobile();
-
useMemo(() => {
const run = async () => {
if (!wells || !wells.length) return;
@@ -125,147 +58,10 @@ export const Wells = () => {
run();
}, [sdk, wells]);
- if (isLoading) {
- return ;
- }
-
if (error) {
return ;
}
- const MyLiquidityRow: FC<{
- well: Well | undefined;
- position: LPBalanceSummary | undefined;
- prices: ReturnType["data"];
- }> = ({ well, position, prices }) => {
- const lpAddress = well?.lpToken?.address;
- const lpToken = well?.lpToken;
-
- if (!well || !position || position.total.lte(0) || !lpAddress || !lpToken) {
- return null;
- }
-
- const tokens = well.tokens || [];
- const logos: ReactNode[] = [];
- const symbols: string[] = [];
- const gotoWell = () => navigate(`/wells/${well.address}`);
-
- tokens.map((token: any) => {
- logos.push();
- symbols.push(token.symbol);
- });
-
- const lpPrice = lpAddress && lpAddress in prices ? prices[lpAddress] : undefined;
- const whitelisted = getIsWhitelisted(well);
-
- const positionsUSD = {
- total: lpPrice?.mul(position.total) || TokenValue.ZERO,
- external: lpPrice?.mul(position.external) || TokenValue.ZERO,
- silo: lpPrice?.mul(position.silo) || TokenValue.ZERO,
- internal: lpPrice?.mul(position.internal) || TokenValue.ZERO
- };
-
- return (
-
-
-
- {logos}
- {symbols.join("/")}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {logos}
- {symbols.join("/")}
- {/* {deployer} */}
-
-
-
-
-
-
-
-
-
-
-
- );
- };
-
- const WellRow: FC<{ well: Well | undefined; index: number }> = ({ well, index }) => {
- if (!well) return null;
-
- const tokens = well.tokens || [];
- const logos: ReactNode[] = [];
- const smallLogos: ReactNode[] = [];
- const symbols: string[] = [];
- const gotoWell = () => navigate(`/wells/${well.address}`);
-
- tokens.map((token: any) => {
- logos.push();
- smallLogos.push();
- symbols.push(token.symbol);
- });
-
- return (
-
-
-
- {logos}
- {symbols.join("/")}
-
-
-
- {wellFunctionNames[index] ? wellFunctionNames[index] : "Price Function"}
-
-
- 0.00%
-
-
- ${wellLiquidity[index] ? wellLiquidity[index]!.toHuman("short") : "-.--"}
-
-
-
- {smallLogos[0]}
- {well.reserves![0] ? well.reserves![0].toHuman("short") : "-.--"}
-
-
- {smallLogos[1]}
- {well.reserves![1] ? well.reserves![1].toHuman("short") : "-.--"}
-
- {well.reserves && well.reserves.length > 2 ? {`+ ${well.reserves.length - 2} MORE`} : null}
-
-
-
- {logos}
- {symbols.join("/")}
-
- ${formatNum(wellLiquidity[index], { minDecimals: 2 })}
-
-
- );
- };
-
return (
@@ -305,22 +101,49 @@ export const Wells = () => {
)}
- {hasPositions === false && tab === 1 ? (
+ {isLoading ? (
<>
-
- Liquidity Positions will appear here.
-
+ {Array(5)
+ .fill(null)
+ .map((_, idx) =>
+ tab === 0 ? (
+
+ ) : (
+
+ )
+ )}
>
) : (
- wells?.map((well, index) => {
- return tab === 0 ? (
+ <>
+ {hasPositions === false && tab === 1 ? (
<>
-
+
+ Liquidity Positions will appear here.
+
+
+ Liquidity Positions will appear here.
+
>
) : (
-
- );
- })
+ wells?.map((well, index) => {
+ return tab === 0 ? (
+
+ ) : (
+
+ );
+ })
+ )}
+ >
)}
@@ -338,22 +161,8 @@ const StyledRow = styled(TabRow)`
@media (max-width: ${size.mobile}) {
position: fixed;
width: 100vw;
- margin-left: -12px;
- margin-bottom: -2px;
- top: calc(100% - 40px);
- }
-`;
-
-const DesktopContainer = styled(Td)`
- @media (max-width: ${size.mobile}) {
- display: none;
- }
-`;
-
-const MobileContainer = styled(Td)`
- padding: 8px 16px;
- @media (min-width: ${size.mobile}) {
- display: none;
+ top: calc(100% - 48px);
+ left: 0;
}
`;
@@ -371,72 +180,24 @@ const DesktopHeader = styled(Th)`
}
`;
-const WellDetail = styled.div`
- display: flex;
- flex-direction: row;
- gap: 8px;
- @media (min-width: ${size.mobile}) {
- flex-direction: column;
- }
-`;
-
-const TokenLogos = styled.div`
- display: flex;
- div:not(:first-child) {
- margin-left: -8px;
- }
-`;
-const TokenSymbols = styled.div`
- font-size: 20px;
- line-height: 24px;
- margin-top: 8px;
- color: #1c1917;
- @media (max-width: ${size.mobile}) {
- font-size: 14px;
- }
-`;
-
-const Amount = styled.div`
- font-weight: 500;
- font-size: 20px;
- line-height: 24px;
- color: #1c1917;
+const NoLPRow = styled.td`
+ background-color: #fff;
+ height: 120px;
+ border-bottom: 0.5px solid #9ca3af;
- @media (max-width: ${size.mobile}) {
- font-size: 14px;
- font-weight: normal;
+ ${mediaQuery.sm.only} {
+ display: none;
}
`;
-const Reserves = styled.div`
- display: flex;
- flex-direction: row;
- justify-content flex-end;
- gap: 8px;
- flex: 1;
-`;
-
-const MoreReserves = styled.div`
- color: #9ca3af;
-`;
-
-const TradingFee = styled.div`
- font-size: 20px;
- line-height: 24px;
- color: #4b5563;
- text-transform: uppercase;
-`;
-
-const WellPricing = styled.div`
- font-size: 20px;
- line-height: 24px;
- text-transform: capitalize;
-`;
-
-const NoLPRow = styled.td`
+const NoLPRowMobile = styled.td`
background-color: #fff;
height: 120px;
border-bottom: 0.5px solid #9ca3af;
+
+ ${mediaQuery.sm.up} {
+ display: none;
+ }
`;
const NoLPMessage = styled.div`
@@ -448,34 +209,3 @@ const NoLPMessage = styled.div`
font-size: 14px;
}
`;
-
-const WellLPBalance = styled.div`
- font-size: 20px;
- line-height: 24px;
- @media (max-width: ${size.mobile}) {
- font-size: 14px;
- font-weight: normal;
- }
-`;
-
-const BalanceContainer = styled.div<{ left?: boolean }>`
- display: flex;
- justify-content: ${(props) => (props.left ? "flex-start" : "flex-end")};
-`;
-
-const Breakdown = styled.div`
- display: flex;
- flex-direction: column;
- width: 100%;
- gap: 4px;
- @media (max-width: ${size.mobile}) {
- gap: 0px;
- }
-`;
-
-const BreakdownRow = styled.div`
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- gap: 4px;
-`;
diff --git a/projects/dex-ui/src/tokens/TokenProvider.tsx b/projects/dex-ui/src/tokens/TokenProvider.tsx
index 181409b06c..ae71d6661b 100644
--- a/projects/dex-ui/src/tokens/TokenProvider.tsx
+++ b/projects/dex-ui/src/tokens/TokenProvider.tsx
@@ -3,26 +3,21 @@ import React, { createContext, useContext } from "react";
import { useWellTokens } from "src/tokens/useWellTokens";
import { images } from "src/assets/images/tokens";
-import { Loading } from "src/components/Loading";
import { Error } from "src/components/Error";
const tokenMap: Record
= {};
const TokenContext = createContext(tokenMap);
export const TokenProvider = ({ children }: { children: React.ReactNode }) => {
- const { data: tokens, isLoading, error } = useWellTokens();
-
- if (isLoading) {
- return
- }
+ const { data: tokens, error } = useWellTokens();
if (error) {
- return
+ return ;
}
const add = (token: Token) => (tokenMap[token.symbol] = token);
- for (const token of tokens! || []) {
+ for (const token of tokens || []) {
let logo = images[token.symbol] ?? images.DEFAULT;
token.setMetadata({ logo });
add(token);
diff --git a/projects/dex-ui/src/utils/ui/useIsMobile.ts b/projects/dex-ui/src/utils/ui/useIsMobile.ts
index f6b7b08daa..cfbc83d2f9 100644
--- a/projects/dex-ui/src/utils/ui/useIsMobile.ts
+++ b/projects/dex-ui/src/utils/ui/useIsMobile.ts
@@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
import { size } from "src/breakpoints";
-const useIsMobile = () => {
+export const useIsMobile = () => {
const [isMobile, setIsMobile] = useState(window.matchMedia(`(max-width: ${size.mobile})`).matches);
// Media query
useEffect(() => {
@@ -14,5 +14,3 @@ const useIsMobile = () => {
return isMobile;
};
-
-export default useIsMobile;
diff --git a/projects/dex-ui/src/wells/useBeanstalkSiloWhitelist.ts b/projects/dex-ui/src/wells/useBeanstalkSiloWhitelist.ts
index a125c52579..817f9d99cd 100644
--- a/projects/dex-ui/src/wells/useBeanstalkSiloWhitelist.ts
+++ b/projects/dex-ui/src/wells/useBeanstalkSiloWhitelist.ts
@@ -3,7 +3,7 @@ import { Well } from "@beanstalk/sdk/Wells";
const WHITELIST_MAP = {
/// BEANWETHCP2w (BEANETH LP)
- "0xBEA0e11282e2bB5893bEcE110cF199501e872bAd": {
+ "0xbea0e11282e2bb5893bece110cf199501e872bad": {
address: "0xBEA0e11282e2bB5893bEcE110cF199501e872bAd",
lpTokenAddress: "0xbea0e11282e2bb5893bece110cf199501e872bad"
}
@@ -15,7 +15,7 @@ export const useBeanstalkSiloWhitelist = () => {
const getIsWhitelisted = (well: Well | undefined) => {
if (!well) return false;
- const wellAddress = well.address;
+ const wellAddress = well.address.toLowerCase();
return wellAddress in WHITELIST_MAP;
};
diff --git a/projects/dex-ui/src/wells/useWellWithParams.tsx b/projects/dex-ui/src/wells/useWellWithParams.tsx
new file mode 100644
index 0000000000..b0dd0f5fed
--- /dev/null
+++ b/projects/dex-ui/src/wells/useWellWithParams.tsx
@@ -0,0 +1,7 @@
+import { useParams } from "react-router-dom";
+import { useWell } from "./useWell";
+
+export const useWellWithParams = () => {
+ const { address: wellAddress } = useParams<"address">();
+ return useWell(wellAddress || "");
+};