diff --git a/projects/dex-ui/src/components/Frame/Footer.tsx b/projects/dex-ui/src/components/Frame/Footer.tsx index 49ec96083e..ad7f0942b8 100644 --- a/projects/dex-ui/src/components/Frame/Footer.tsx +++ b/projects/dex-ui/src/components/Frame/Footer.tsx @@ -1,16 +1,24 @@ import React from "react"; import styled from "styled-components"; -import { Discord, Github, Twitter } from "../Icons"; +import { BeanstalkLogoBlack, Discord, Github, Twitter } from "../Icons"; import { size } from "src/breakpoints"; export const Footer = () => ( -
📃 Protocol Documentation
+
+ + 📃 Protocol Documentation + +
Visit the Docs →
-
👾 Basin Bug Bounty Program
+
+ + 👾 Basin Bug Bounty Program + +
Learn More →
@@ -22,6 +30,9 @@ export const Footer = () => ( + + +
); @@ -63,6 +74,7 @@ const SmallBox = styled.a` width: 64px; border-left: 1px solid black; justify-content: center; + align-items: center; :hover { background-color: #f0fdf4; } @@ -70,4 +82,4 @@ const SmallBox = styled.a` const StyledLink = styled.span` text-decoration: underline; -` +`; diff --git a/projects/dex-ui/src/components/Frame/Frame.tsx b/projects/dex-ui/src/components/Frame/Frame.tsx index 6ae25bf089..c9b41e82ad 100644 --- a/projects/dex-ui/src/components/Frame/Frame.tsx +++ b/projects/dex-ui/src/components/Frame/Frame.tsx @@ -11,7 +11,7 @@ import CustomToaster from "../TxnToast/CustomToaster"; import swapIcon from "src/assets/images/navbar/swap.svg"; import wellsIcon from "src/assets/images/navbar/wells.svg"; import { LinksNav } from "../Typography"; -import { BurgerMenuIcon, Discord, Github, Logo, Twitter, X } from "../Icons"; +import { BurgerMenuIcon, Discord, Github, Logo, Twitter, X, BeanstalkLogoBlack } from "../Icons"; import { size } from "src/breakpoints"; import { useNetwork } from "wagmi"; import { Title } from "../PageComponents/Title"; @@ -92,11 +92,24 @@ export const Frame: FC<{}> = ({ children }) => { + + + - setMobileMenuOpen(false)}> + setMobileMenuOpen(false)} + > Bug Bounty Program - setMobileMenuOpen(false)}> + setMobileMenuOpen(false)} + > Documentation @@ -104,7 +117,7 @@ export const Frame: FC<{}> = ({ children }) => { - {chain?.unsupported ? : children} + {chain?.unsupported ? <Title title="Unsupported Chain" /> : children} </Window> <Footer /> </Container> diff --git a/projects/dex-ui/src/components/Icons.tsx b/projects/dex-ui/src/components/Icons.tsx index 70863aca1b..872f3e4e4d 100644 --- a/projects/dex-ui/src/components/Icons.tsx +++ b/projects/dex-ui/src/components/Icons.tsx @@ -45,6 +45,14 @@ export const Github = ({ color = "#000", width, height }: SVGProps) => ( </svg> ); +export const BeanstalkLogoBlack = ({ color = "#000", width = 24, height = 24 }: SVGProps) => ( + <svg width={width} height={height} viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"> + <rect width="48" height="48" rx="24" fill={color} /> + <path d="M30.7438 5.05786L16.7279 42.5026C16.7279 42.5026 1.18757 15.9919 30.7438 5.05786Z" fill="white" /> + <path d="M19.9849 40.1793L29.8344 13.4126C29.8344 13.4126 47.9863 28.0973 19.9849 40.1793Z" fill="white" /> + </svg> +); + export const YieldSparkle = ({ color = "#000", width = 16, height = 16 }: SVGProps) => ( <svg xmlns="http://www.w3.org/2000/svg" width={width} height={height} fill="none"> <path @@ -209,9 +217,9 @@ export const RightArrow = ({ color = "#000", width = 24, height = 24 }: SVGProps </svg> ); -export const BurgerMenuIcon = ({ color = "#000", width = 24, height = 24}: SVGProps) => ( - <svg width={width}height={height} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> - <line x1="4" y1="7" x2="20" y2="7" stroke={color} strokeWidth="2"/> - <line x1="4" y1="15" x2="20" y2="15" stroke={color} strokeWidth="2"/> +export const BurgerMenuIcon = ({ color = "#000", width = 24, height = 24 }: SVGProps) => ( + <svg width={width} height={height} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <line x1="4" y1="7" x2="20" y2="7" stroke={color} strokeWidth="2" /> + <line x1="4" y1="15" x2="20" y2="15" stroke={color} strokeWidth="2" /> </svg> ); diff --git a/projects/dex-ui/src/components/Liquidity/AddLiquidity.tsx b/projects/dex-ui/src/components/Liquidity/AddLiquidity.tsx index 6973c90fff..6f239877ce 100644 --- a/projects/dex-ui/src/components/Liquidity/AddLiquidity.tsx +++ b/projects/dex-ui/src/components/Liquidity/AddLiquidity.tsx @@ -31,12 +31,7 @@ export type AddLiquidityQuote = { estimate: TokenValue; }; -export const AddLiquidity = ({ - well, - slippage, - slippageSettingsClickHandler, - handleSlippageValueChange -}: AddLiquidityProps) => { +export const AddLiquidity = ({ well, slippage, slippageSettingsClickHandler, handleSlippageValueChange }: AddLiquidityProps) => { const { address } = useAccount(); const [amounts, setAmounts] = useState<LiquidityAmounts>({}); const inputs = Object.values(amounts); @@ -54,7 +49,7 @@ export const AddLiquidity = ({ const [isSubmitting, setIsSubmitting] = useState(false); const [useWETH, setUseWETH] = useState(false); - + useEffect(() => { const run = async () => { if (!well?.tokens) return; @@ -85,7 +80,7 @@ export const AddLiquidity = ({ const indexWETH = useMemo(() => { if (!hasWETH || !well.tokens || well.tokens.length === 0) return null; - + let index = null; for (let i = 0; i < well.tokens.length; i++) { if (well.tokens[i].symbol === "WETH") { @@ -93,7 +88,7 @@ export const AddLiquidity = ({ } } return index; - }, [hasWETH, well.tokens]) + }, [hasWETH, well.tokens]); const useNativeETH = !useWETH && indexWETH && inputs[indexWETH] && inputs[indexWETH].gt(TokenValue.ZERO); @@ -142,9 +137,7 @@ export const AddLiquidity = ({ `Token ${token.symbol} with amount ${amounts[index].toHuman()} has approval ${tokenHasMinAllowance}` ); if (token.symbol === "WETH" && !useWETH && hasWETH) { - Log.module("AddLiquidity").debug( - `Using Native ETH, no approval needed!` - ); + Log.module("AddLiquidity").debug(`Using Native ETH, no approval needed!`); _tokenAllowance.push(true); } else { _tokenAllowance.push(tokenHasMinAllowance); @@ -184,41 +177,45 @@ export const AddLiquidity = ({ const allTokensHaveMinAllowance = useMemo(() => tokenAllowance.filter((a) => a === false).length === 0, [tokenAllowance]); - const { data: quote } = useQuery(["wells", "quote", "addliquidity", address, amounts, allTokensHaveMinAllowance], async () => { - if (!atLeastOneAmountNonZero) { - setShowQuoteDetails(false); - return null; - } + const { data: quote } = useQuery( + ["wells", "quote", "addliquidity", address, amounts, allTokensHaveMinAllowance], + async () => { + if (!atLeastOneAmountNonZero) { + setShowQuoteDetails(false); + return null; + } - try { - let quote; - let estimate; - let gas; - quote = await well.addLiquidityQuote(inputs); - if (allTokensHaveMinAllowance && tokenAllowance.length) { - if (useNativeETH) { - const addLiq = new AddLiquidityETH(sdk.wells); - estimate = await addLiq.doGasEstimate(well, inputs, quote, address); + try { + let quote; + let estimate; + let gas; + quote = await well.addLiquidityQuote(inputs); + if (allTokensHaveMinAllowance && tokenAllowance.length) { + if (useNativeETH) { + const addLiq = new AddLiquidityETH(sdk.wells); + estimate = await addLiq.doGasEstimate(well, inputs, quote, address); + } else { + estimate = await well.addLiquidityGasEstimate(inputs, quote, address); + } } else { - estimate = await well.addLiquidityGasEstimate(inputs, quote, address); + estimate = TokenValue.ZERO; } - } else { - estimate = TokenValue.ZERO; + setShowQuoteDetails(true); + gas = estimate; + return { + quote, + gas, + estimate + }; + } catch (error: any) { + Log.module("addliquidity").error("Error during quote: ", (error as Error).message); + return null; } - setShowQuoteDetails(true); - gas = estimate; - return { - quote, - gas, - estimate - }; - } catch (error: any) { - Log.module("addliquidity").error("Error during quote: ", (error as Error).message); - return null; + }, + { + enabled: !isSubmitting } - },{ - enabled: !isSubmitting - }); + ); const addLiquidityButtonClickHandler = useCallback(async () => { if (quote && address) { @@ -233,7 +230,13 @@ export const AddLiquidity = ({ let addLiquidityTxn; if (useNativeETH) { const addLiquidityNativeETH = new AddLiquidityETH(sdk.wells); - addLiquidityTxn = await addLiquidityNativeETH.addLiquidity(well, inputs, quoteAmountLessSlippage, address, quote.estimate.mul(1.2)); + addLiquidityTxn = await addLiquidityNativeETH.addLiquidity( + well, + inputs, + quoteAmountLessSlippage, + address, + quote.estimate.mul(1.2) + ); } else { addLiquidityTxn = await well.addLiquidity(inputs, quoteAmountLessSlippage, address, undefined, { gasLimit: quote.estimate.mul(1.2).toBigNumber() @@ -283,6 +286,26 @@ export const AddLiquidity = ({ [amounts, prices, well.tokens] ); + const toggleBalanceMode = useCallback(() => { + const newMode = !balancedMode; + + setBalancedMode(newMode); + + /// if we are toggling balancedMode to false, no need to handle re-balancing. + if (!newMode) return; + + if (amounts[0] && amounts[1]) { + /// If both are zero, already balanced + if (amounts[0].eq(0) && amounts[1].eq(0)) return; + + /// If amount1 is non-zero, re-balance to amount1, otherwise, re-balance to amount2 + const nonZeroValueIndex = Number(!amounts[0].gt(0)); + + /// This fires even though the value is the same, so we need to check if it's actually changed + handleBalancedInputChange(nonZeroValueIndex)(amounts[nonZeroValueIndex]); + } + }, [balancedMode, amounts, handleBalancedInputChange]); + useEffect(() => { if (!address) { return; @@ -314,10 +337,12 @@ export const AddLiquidity = ({ [address, well.tokens, amounts, useNativeETH, well.address, sdk.addresses.DEPOT.MAINNET, checkMinAllowanceForAllTokens] ); - const buttonLabel = useMemo( - () => (!atLeastOneAmountNonZero ? "Enter Amount(s)" : !hasEnoughBalance ? "Insufficient Balance" : "Add Liquidity"), - [atLeastOneAmountNonZero, hasEnoughBalance] - ); + const buttonLabel = useMemo(() => { + if (!address) return "Connect Wallet"; + if (!hasEnoughBalance) return "Insufficient Balance"; + if (!atLeastOneAmountNonZero) return "Enter Amount(s)"; + return "Add Liquidity"; + }, [atLeastOneAmountNonZero, hasEnoughBalance, address]); return ( <div> @@ -338,10 +363,8 @@ export const AddLiquidity = ({ ))} </TokenListContainer> <div> - <Checkbox label={"Add tokens in balanced proportion"} checked={balancedMode} onClick={() => setBalancedMode(!balancedMode)} /> - {hasWETH && ( - <Checkbox label={"Use Wrapped ETH"} checked={useWETH} onClick={() => setUseWETH(!useWETH)} /> - )} + <Checkbox label={"Add tokens in balanced proportion"} checked={balancedMode} onClick={() => toggleBalanceMode()} /> + {hasWETH && <Checkbox label={"Use Wrapped ETH"} checked={useWETH} onClick={() => setUseWETH(!useWETH)} />} </div> {showQuoteDetails && ( <QuoteDetails @@ -357,7 +380,8 @@ export const AddLiquidity = ({ /> )} <MediumGapContainer> - {well.tokens!.length > 0 && hasEnoughBalance && + {well.tokens!.length > 0 && + hasEnoughBalance && well.tokens!.map((token: Token, index: number) => { if (amounts[index] && amounts[index].gt(TokenValue.ZERO) && tokenAllowance[index] === false) { return ( diff --git a/projects/dex-ui/src/components/Liquidity/QuoteDetails.tsx b/projects/dex-ui/src/components/Liquidity/QuoteDetails.tsx index a04d2e03dc..7ad785ef8e 100644 --- a/projects/dex-ui/src/components/Liquidity/QuoteDetails.tsx +++ b/projects/dex-ui/src/components/Liquidity/QuoteDetails.tsx @@ -10,6 +10,7 @@ import { ImageButton } from "../ImageButton"; import { Tooltip } from "../Tooltip"; import { BodyXS } from "../Typography"; import { size } from "src/breakpoints"; +import { displayTokenSymbol } from "src/utils/format"; type QuoteDetailsProps = { type: LIQUIDITY_OPERATION_TYPE | "FORWARD_SWAP" | "REVERSE_SWAP"; @@ -96,7 +97,7 @@ const QuoteDetails = ({ if (type === LIQUIDITY_OPERATION_TYPE.ADD) { const _quoteValue = quote?.quote as TokenValue; - return `${_quoteValue.toHuman("short")} ${wellLpToken!.symbol}`; + return `${_quoteValue.toHuman("short")} ${displayTokenSymbol(wellLpToken!)}`; } if (removeLiquidityMode === REMOVE_LIQUIDITY_MODE.Custom) { diff --git a/projects/dex-ui/src/components/Liquidity/RemoveLiquidity.tsx b/projects/dex-ui/src/components/Liquidity/RemoveLiquidity.tsx index e59692a924..218ed2085a 100644 --- a/projects/dex-ui/src/components/Liquidity/RemoveLiquidity.tsx +++ b/projects/dex-ui/src/components/Liquidity/RemoveLiquidity.tsx @@ -20,6 +20,7 @@ import { getPrice } from "src/utils/price/usePrice"; import { useWellReserves } from "src/wells/useWellReserves"; import { Checkbox } from "../Checkbox"; import { size } from "src/breakpoints"; +import { displayTokenSymbol } from "src/utils/format"; type RemoveLiquidityProps = { well: Well; @@ -125,7 +126,6 @@ export const RemoveLiquidity = ({ well, slippage, slippageSettingsClickHandler, }); let removeLiquidityTxn; try { - if (removeLiquidityMode === REMOVE_LIQUIDITY_MODE.OneToken) { if (!oneTokenQuote) { return; @@ -423,7 +423,7 @@ export const RemoveLiquidity = ({ well, slippage, slippageSettingsClickHandler, <ApproveTokenButton disabled={approveButtonDisabled} loading={false} - label={`Approve ${wellLpToken.symbol}`} + label={`Approve ${displayTokenSymbol(wellLpToken)}`} onClick={approveTokenButtonClickHandler} /> </ButtonWrapper> diff --git a/projects/dex-ui/src/components/Swap/SwapRoot.tsx b/projects/dex-ui/src/components/Swap/SwapRoot.tsx index 21c689b3f6..e83d892418 100644 --- a/projects/dex-ui/src/components/Swap/SwapRoot.tsx +++ b/projects/dex-ui/src/components/Swap/SwapRoot.tsx @@ -76,7 +76,9 @@ export const SwapRoot = () => { }, [inToken, outToken, builder, account]); useEffect(() => { - readyToSwap && hasEnoughBalance && !!account && inAmount?.gt(TokenValue.ZERO) && outAmount?.gt(TokenValue.ZERO) ? setButtonEnabled(true) : setButtonEnabled(false); + readyToSwap && hasEnoughBalance && !!account && inAmount?.gt(TokenValue.ZERO) && outAmount?.gt(TokenValue.ZERO) + ? setButtonEnabled(true) + : setButtonEnabled(false); }, [readyToSwap, account, inAmount, outAmount, hasEnoughBalance]); const arrowHandler = () => { @@ -285,7 +287,7 @@ export const SwapRoot = () => { // sanity check if (recipient === NULL_ADDRESS) throw new Error("FATAL: recipient is the NULL_ADDRESS!"); const gasEstimate = quote?.gas; - const tx = await quote!.doSwap({ gasLimit: gasEstimate?.mul(1.2).toBigNumber() }); + const tx = await quote!.doSwap({ gasLimit: gasEstimate?.mul(1.2).toBigNumber() }); toast.confirming(tx); const receipt = await tx.wait(); @@ -320,12 +322,13 @@ export const SwapRoot = () => { const getLabel = useCallback(() => { if (!account) return "Connect Wallet"; if (!inAmount && !outAmount) return "Enter Amount"; + if (inToken.address === outToken.address) return "Select different output token"; if (inAmount?.eq(TokenValue.ZERO) && outAmount?.eq(TokenValue.ZERO)) return "Enter Amount"; if (!hasEnoughBalance) return "Insufficient Balance"; if (needsApproval) return "Approve"; return "Swap"; - }, [account, hasEnoughBalance, inAmount, needsApproval, outAmount]); + }, [account, hasEnoughBalance, inAmount, needsApproval, outAmount, inToken, outToken]); if (Object.keys(tokens).length === 0) return <Container>There are no tokens. Please check you are connected to the right network.</Container>; diff --git a/projects/dex-ui/src/components/Swap/TokenPicker.tsx b/projects/dex-ui/src/components/Swap/TokenPicker.tsx index 51cf14fe34..d758709777 100644 --- a/projects/dex-ui/src/components/Swap/TokenPicker.tsx +++ b/projects/dex-ui/src/components/Swap/TokenPicker.tsx @@ -12,6 +12,7 @@ import { ChevronDown } from "../Icons"; import { BottomDrawer } from "../BottomDrawer"; import { BodyS } from "../Typography"; import { size } from "src/breakpoints"; +import { displayTokenSymbol } from "src/utils/format"; type Props = { token: Token; @@ -58,7 +59,7 @@ export const TokenPicker: FC<Props> = ({ token, excludeToken, editable = true, o {token ? ( <> <TokenLogo token={token} size={16} /> - <TokenSymbol>{token.symbol}</TokenSymbol> + <TokenSymbol>{displayTokenSymbol(token)}</TokenSymbol> </> ) : ( <div>Select a Token</div> @@ -187,7 +188,7 @@ const Symbol = styled.div` font-size: 16px; line-height: 20px; @media (max-width: ${size.mobile}) { - line-height: 16px; + line-height: 16px; } `; const Name = styled.div` @@ -196,7 +197,7 @@ const Name = styled.div` line-height: 20px; color: #9e9e9e; @media (max-width: ${size.mobile}) { - line-height: 14px; + line-height: 14px; } `; const Balance = styled.div` diff --git a/projects/dex-ui/src/components/Tooltip.tsx b/projects/dex-ui/src/components/Tooltip.tsx index b3a4ea6ba6..016eae8a30 100644 --- a/projects/dex-ui/src/components/Tooltip.tsx +++ b/projects/dex-ui/src/components/Tooltip.tsx @@ -79,7 +79,7 @@ const TooltipBox = styled.div<TooltipProps>` left: ${props.arrowOffset}%;` : props.side === "left" ? `left: calc(100% - ${props.arrowSize}px); - top: ${props.arrowOffset}&;` + top: ${props.arrowOffset}%;` : props.side === "right" ? `right: calc(100% - ${props.arrowSize}px);; top: ${props.arrowOffset}%;` diff --git a/projects/dex-ui/src/components/Well/Activity/WellHistory.tsx b/projects/dex-ui/src/components/Well/Activity/WellHistory.tsx index c8edd8fbb2..05ee8ad726 100644 --- a/projects/dex-ui/src/components/Well/Activity/WellHistory.tsx +++ b/projects/dex-ui/src/components/Well/Activity/WellHistory.tsx @@ -23,15 +23,18 @@ export const WellHistory = ({ well, tokenPrices, reservesUSD }: WellHistoryProps const totalEvents = events?.length || 0; const totalPages = Math.ceil(totalEvents / eventsPerPage); const [currentPage, setCurrentPage] = useState(1); - const newestEventOnPage = (eventsPerPage * currentPage) - eventsPerPage; - const oldestEventOnPage = (eventsPerPage * currentPage) - 1; - + const newestEventOnPage = eventsPerPage * currentPage - eventsPerPage; + const oldestEventOnPage = eventsPerPage * currentPage - 1; + const lpTokenSupply = useTokenSupply(well.lpToken!); - const lpTokenPrice = lpTokenSupply.totalSupply ? reservesUSD.div(lpTokenSupply.totalSupply) : TokenValue.ZERO; + const isNonEmptyWell = lpTokenSupply.totalSupply && lpTokenSupply.totalSupply.gt(0); + const lpTokenPrice = lpTokenSupply.totalSupply && isNonEmptyWell ? reservesUSD.div(lpTokenSupply.totalSupply) : TokenValue.ZERO; const eventRows: JSX.Element[] = (events || []) .filter((e: WellEvent) => filter === null || e.type == filter) - .map<ReactElement>((e, index): any => (index >= newestEventOnPage && index <= oldestEventOnPage) && renderEvent(e, well, tokenPrices, lpTokenPrice)); + .map<ReactElement>( + (e, index): any => index >= newestEventOnPage && index <= oldestEventOnPage && renderEvent(e, well, tokenPrices, lpTokenPrice) + ); return ( <WellHistoryContainer> @@ -53,25 +56,61 @@ export const WellHistory = ({ well, tokenPrices, reservesUSD }: WellHistoryProps </Row> </THead> <TBody> - {eventRows} - <MobilePageSelector> - <PageSelector colSpan={2}> - <SelectorContainer> - <StyledTabButton active pageLimit={currentPage === 1} onClick={() => setCurrentPage(currentPage > 1 ? currentPage - 1 : 1)}>←</StyledTabButton> - {`Page ${currentPage} of ${totalPages}`} - <StyledTabButton active pageLimit={currentPage === totalPages} onClick={() => setCurrentPage(currentPage < totalPages ? currentPage + 1 : totalPages)}>→</StyledTabButton> - </SelectorContainer> - </PageSelector> - </MobilePageSelector> - <DesktopPageSelector> - <PageSelector colSpan={4}> - <SelectorContainer> - <StyledTabButton active pageLimit={currentPage === 1} onClick={() => setCurrentPage(currentPage > 1 ? currentPage - 1 : 1)}>←</StyledTabButton> - {`Page ${currentPage} of ${totalPages}`} - <StyledTabButton active pageLimit={currentPage === totalPages} onClick={() => setCurrentPage(currentPage < totalPages ? currentPage + 1 : totalPages)}>→</StyledTabButton> - </SelectorContainer> - </PageSelector> - </DesktopPageSelector> + {eventRows.length ? ( + eventRows + ) : ( + <> + <NoEventsRow colSpan={4}> + <NoEventsData>No events to show</NoEventsData> + </NoEventsRow> + </> + )} + {isNonEmptyWell ? ( + <> + <MobilePageSelector> + <PageSelector colSpan={2}> + <SelectorContainer> + <StyledTabButton + active + pageLimit={currentPage === 1} + onClick={() => setCurrentPage(currentPage > 1 ? currentPage - 1 : 1)} + > + ← + </StyledTabButton> + {`Page ${currentPage} of ${totalPages}`} + <StyledTabButton + active + pageLimit={currentPage === totalPages} + onClick={() => setCurrentPage(currentPage < totalPages ? currentPage + 1 : totalPages)} + > + → + </StyledTabButton> + </SelectorContainer> + </PageSelector> + </MobilePageSelector> + <DesktopPageSelector> + <PageSelector colSpan={4}> + <SelectorContainer> + <StyledTabButton + active + pageLimit={currentPage === 1} + onClick={() => setCurrentPage(currentPage > 1 ? currentPage - 1 : 1)} + > + ← + </StyledTabButton> + {`Page ${currentPage} of ${totalPages}`} + <StyledTabButton + active + pageLimit={currentPage === totalPages} + onClick={() => setCurrentPage(currentPage < totalPages ? currentPage + 1 : totalPages)} + > + → + </StyledTabButton> + </SelectorContainer> + </PageSelector> + </DesktopPageSelector> + </> + ) : null} </TBody> </Table> </> @@ -111,16 +150,32 @@ const SelectorContainer = styled.div` align-items: center; font-weight: 600; gap: 8px; - background-color: #F9F8F6; -` + background-color: #f9f8f6; +`; -const StyledTabButton = styled(TabButton)<{pageLimit: boolean}>` - background-color: #F9F8F6; +const StyledTabButton = styled(TabButton)<{ pageLimit: boolean }>` + background-color: #f9f8f6; outline: 0px; - ${({pageLimit}) => pageLimit && 'color: #9CA3AF;'} -` + ${({ pageLimit }) => pageLimit && "color: #9CA3AF;"} +`; const PageSelector = styled(Td)` padding: 0px; text-align: end; -` +`; + +const NoEventsRow = styled.td` + background-color: #fff; + height: 120px; + border-bottom: 0.5px solid #9ca3af; +`; + +const NoEventsData = styled.div` + display: flex; + justify-content: center; + color: #4b5563; + + @media (max-width: ${size.mobile}) { + font-size: 14px; + } +`; diff --git a/projects/dex-ui/src/components/Well/LiquidityBox.tsx b/projects/dex-ui/src/components/Well/LiquidityBox.tsx index 7644cdf676..52ad1254f5 100644 --- a/projects/dex-ui/src/components/Well/LiquidityBox.tsx +++ b/projects/dex-ui/src/components/Well/LiquidityBox.tsx @@ -1,21 +1,55 @@ -import React from "react"; +import React, { useMemo } from "react"; import styled from "styled-components"; + +import { TokenValue } from "@beanstalk/sdk"; +import { Well } from "@beanstalk/sdk/Wells"; + +import { size } from "src/breakpoints"; +import { BodyCaps, BodyXS, LinksButtonText, TextNudge } from "src/components/Typography"; import { InfoBox } from "src/components/InfoBox"; -import { BodyCaps, BodyXS, LinksButtonText, TextNudge } from "../Typography"; -import { TokenLogo } from "../TokenLogo"; +import { TokenLogo } from "src/components/TokenLogo"; +import { Tooltip } from "src/components/Tooltip"; import { FC } from "src/types"; -import { Token } from "@beanstalk/sdk"; -import { useTokenBalance } from "src/tokens/useTokenBalance"; -import { size } from "src/breakpoints"; -import { useSiloBalance } from "src/tokens/useSiloBalance"; +import { formatUSD } from "src/utils/format"; + +import { useWellLPTokenPrice } from "src/wells/useWellLPTokenPrice"; +import { useLPPositionSummary } from "src/tokens/useLPPositionSummary"; +import { useBeanstalkSiloWhitelist } from "src/wells/useBeanstalkSiloWhitelist"; type Props = { - lpToken: Token; + well: Well | undefined; }; -export const LiquidityBox: FC<Props> = ({ lpToken }) => { - const { data: balance } = useTokenBalance(lpToken); - const { data: siloBalance } = useSiloBalance(lpToken); +const tooltipProps = { + offsetX: -20, + offsetY: 375, + arrowSize: 4, + arrowOffset: 95, + side: "top", + width: 175 +} as const; + +const displayTV = (value?: TokenValue) => (value?.gt(0) ? value.toHuman("short") : "-"); + +export const LiquidityBox: FC<Props> = (props) => { + const well = useMemo(() => props.well, [props.well]); + + const { getPositionWithWell } = useLPPositionSummary(); + const { getIsWhitelisted } = useBeanstalkSiloWhitelist(); + + const position = getPositionWithWell(well); + const isWhitelisted = getIsWhitelisted(well); + + const { data: lpTokenPriceMap } = useWellLPTokenPrice(well); + + const lpAddress = well?.lpToken?.address; + const lpTokenPrice = lpAddress && lpAddress in lpTokenPriceMap ? lpTokenPriceMap[lpAddress] : TokenValue.ZERO; + + const siloUSD = position?.silo.mul(lpTokenPrice) || TokenValue.ZERO; + const externalUSD = position?.external.mul(lpTokenPrice) || TokenValue.ZERO; + const internalUSD = position?.internal.mul(lpTokenPrice) || TokenValue.ZERO; + + const USDTotal = siloUSD.add(externalUSD).add(internalUSD); return ( <InfoBox> @@ -24,20 +58,58 @@ export const LiquidityBox: FC<Props> = ({ lpToken }) => { <BoxHeader>My Liquidity</BoxHeader> </TextNudge> <BoxHeaderAmount> - <TokenLogo token={lpToken} size={16} mobileSize={16} isLP /> - <TextNudge amount={1.5}>{balance ? balance[lpToken.symbol].toHuman("short") : "-"}</TextNudge> + <TokenLogo token={well!.lpToken} size={16} mobileSize={16} isLP /> + <TextNudge amount={1.5}>{displayTV(position?.total)}</TextNudge> </BoxHeaderAmount> </InfoBox.Header> <InfoBox.Body> <InfoBox.Row> <InfoBox.Key>In my Wallet</InfoBox.Key> - <InfoBox.Value>{balance ? balance[lpToken.symbol].toHuman("short") : "-"}</InfoBox.Value> - </InfoBox.Row> - <InfoBox.Row> - <InfoBox.Key>Deposited in the Silo</InfoBox.Key> - <InfoBox.Value>{siloBalance ? siloBalance.toHuman("short") : "-"}</InfoBox.Value> + <InfoBox.Value>{displayTV(position?.external)}</InfoBox.Value> </InfoBox.Row> + {isWhitelisted ? ( + <> + <InfoBox.Row> + <InfoBox.Key>Deposited in the Silo</InfoBox.Key> + <InfoBox.Value>{displayTV(position?.silo)}</InfoBox.Value> + </InfoBox.Row> + <InfoBox.Row> + <InfoBox.Key>In my Farm Balance</InfoBox.Key> + <InfoBox.Value>{displayTV(position?.internal)}</InfoBox.Value> + </InfoBox.Row> + </> + ) : null} </InfoBox.Body> + <InfoBox.Footer> + <USDWrapper> + {isWhitelisted ? ( + <Tooltip + {...tooltipProps} + content={ + <Breakdown> + <BreakdownRow> + {"Wallet: "} + <div>${externalUSD.toHuman("short")}</div> + </BreakdownRow> + + <BreakdownRow> + {"Silo Deposits: "} + <div>${siloUSD.toHuman("short")}</div> + </BreakdownRow> + <BreakdownRow> + {"Farm Balance: "} + <div>${internalUSD.toHuman("short")}</div> + </BreakdownRow> + </Breakdown> + } + > + USD TOTAL: {formatUSD(USDTotal)} + </Tooltip> + ) : ( + <>USD TOTAL: {formatUSD(USDTotal)}</> + )} + </USDWrapper> + </InfoBox.Footer> </InfoBox> ); }; @@ -54,3 +126,25 @@ const BoxHeaderAmount = styled.div` gap: 4px; ${LinksButtonText} `; + +const USDWrapper = styled.div` + display: flex; + flex: 2; + justify-content: flex-end; + gap: 8px; + color: #4b5563; + cursor: pointer; +`; + +const Breakdown = styled.div` + display: flex; + flex-direction: column; + gap: 4px; +`; + +const BreakdownRow = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + gap: 4px; +`; diff --git a/projects/dex-ui/src/pages/Liquidity.tsx b/projects/dex-ui/src/pages/Liquidity.tsx index 4119021bc7..2e6cf729a2 100644 --- a/projects/dex-ui/src/pages/Liquidity.tsx +++ b/projects/dex-ui/src/pages/Liquidity.tsx @@ -69,7 +69,7 @@ export const Liquidity = () => { if (loading) return <Loading spinnerOnly />; if (error) { - return <Error message={error?.message} errorOnly /> + return <Error message={error?.message} errorOnly />; } return ( @@ -85,7 +85,7 @@ export const Liquidity = () => { /> {(tab === null && isMobile) || !isMobile ? ( <> - <LiquidityBox lpToken={well?.lpToken!} /> + <LiquidityBox well={well} /> <LearnMoreContainer> <LearnMoreLabel onClick={toggle}> <LearnMoreLine /> diff --git a/projects/dex-ui/src/pages/Well.tsx b/projects/dex-ui/src/pages/Well.tsx index d6d75c368b..30cc6319ef 100644 --- a/projects/dex-ui/src/pages/Well.tsx +++ b/projects/dex-ui/src/pages/Well.tsx @@ -111,23 +111,19 @@ export const Well = () => { } observer.current.observe(node); - }, []); - useEffect(() => () => { - if (observer.current) observer.current.disconnect(); - }, []) + useEffect( + () => () => { + if (observer.current) observer.current.disconnect(); + }, + [] + ); // Code above detects if the component with the Add/Remove Liq + Swap buttons is sticky - if (loading) - return ( - <Loading spinnerOnly /> - ); + if (loading) return <Loading spinnerOnly />; - if (error) - return ( - <Error message={error?.message} errorOnly /> - ); + if (error) return <Error message={error?.message} errorOnly />; return ( <Page> @@ -180,7 +176,7 @@ export const Well = () => { </Item> </LiquiditySwapButtons> <LiquidityBoxContainer> - <LiquidityBox lpToken={well?.lpToken!} /> + <LiquidityBox well={well} /> </LiquidityBoxContainer> <LearnMoreContainer> <LearnMoreLabel onClick={toggle}> diff --git a/projects/dex-ui/src/pages/Wells.tsx b/projects/dex-ui/src/pages/Wells.tsx index 867aa6ad30..93ecf0c3e1 100644 --- a/projects/dex-ui/src/pages/Wells.tsx +++ b/projects/dex-ui/src/pages/Wells.tsx @@ -1,5 +1,5 @@ import { TokenValue } from "@beanstalk/sdk"; -import React, { ReactNode, useMemo, useState } from "react"; +import React, { FC, ReactNode, useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; import { Item } from "src/components/Layout"; import { Page } from "src/components/Page"; @@ -12,21 +12,91 @@ 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 { useAccount } from "wagmi"; import { size } from "src/breakpoints"; import { Loading } from "../components/Loading"; import { Error } from "../components/Error"; +import { displayTokenSymbol, formatNum, formatUSD } from "src/utils/format"; +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"; + +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 ? ( + <Tooltip + {...getTooltipProps()} + content={ + <Breakdown> + <BreakdownRow> + {"Wallet Balance:"} + <span>{formatFn(items.external)}</span> + </BreakdownRow> + <BreakdownRow> + {"Silo Deposits:"} + <span>{formatFn(items.silo)}</span> + </BreakdownRow> + <BreakdownRow> + {"Farm Balance:"} + <span>{formatFn(items.internal)}</span> + </BreakdownRow> + </Breakdown> + } + > + <WellLPBalance>{totalDisplay}</WellLPBalance> + </Tooltip> + ) : ( + <WellLPBalance>{totalDisplay}</WellLPBalance> + ); +}; export const Wells = () => { const { data: wells, isLoading, error } = useWells(); const navigate = useNavigate(); const sdk = useSdk(); - const { address } = useAccount(); + const [wellLiquidity, setWellLiquidity] = useState<(TokenValue | undefined)[]>([]); const [wellFunctionNames, setWellFunctionNames] = useState<string[]>([]); - const [wellLpBalances, setWellLpBalances] = useState<(TokenValue | undefined)[]>([]); const [tab, showTab] = useState<number>(0); + const { data: lpTokenPrices } = useWellLPTokenPrice(wells); + + const { hasPositions, getPositionWithWell } = useLPPositionSummary(); + + const { getIsWhitelisted } = useBeanstalkSiloWhitelist(); + const isMobile = useIsMobile(); + useMemo(() => { const run = async () => { if (!wells || !wells.length) return; @@ -50,41 +120,51 @@ export const Wells = () => { _wellsFunctionNames[i] = _wellName; } setWellFunctionNames(_wellsFunctionNames); - - let _wellsLpBalances = []; - for (let i = 0; i < wells.length; i++) { - if (!address || !wells[i].lpToken) return; - const _lpBalance = await wells[i].lpToken?.getBalance(address); - _wellsLpBalances[i] = _lpBalance; - } - setWellLpBalances(_wellsLpBalances); }; run(); - }, [sdk, wells, address]); + }, [sdk, wells]); if (isLoading) { - return <Loading spinnerOnly /> + return <Loading spinnerOnly />; } if (error) { - return <Error message={error?.message} errorOnly /> + return <Error message={error?.message} errorOnly />; } - function WellRow(well: any, index: any) { - if (!well) return; + const MyLiquidityRow: FC<{ + well: Well | undefined; + position: LPBalanceSummary | undefined; + prices: ReturnType<typeof useWellLPTokenPrice>["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 smallLogos: ReactNode[] = []; const symbols: string[] = []; const gotoWell = () => navigate(`/wells/${well.address}`); tokens.map((token: any) => { logos.push(<TokenLogo token={token} size={25} key={token.symbol} />); - smallLogos.push(<TokenLogo token={token} size={16} key={token.symbol} />); 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 ( <TableRow key={well.address} onClick={gotoWell}> <DesktopContainer> @@ -93,46 +173,57 @@ export const Wells = () => { <TokenSymbols>{symbols.join("/")}</TokenSymbols> </WellDetail> </DesktopContainer> - <DesktopContainer> - <WellPricing>{wellFunctionNames[index] ? wellFunctionNames[index] : "Price Function"}</WellPricing> - </DesktopContainer> <DesktopContainer align="right"> - <TradingFee>0.00%</TradingFee> + <BalanceContainer> + <PositionBreakdown + isWhitelisted={whitelisted} + items={position} + totalDisplay={`${position?.total.toHuman("short") || "-"} ${displayTokenSymbol(lpToken)}`} + isLP + /> + </BalanceContainer> </DesktopContainer> <DesktopContainer align="right"> - <Amount>${wellLiquidity[index] ? wellLiquidity[index]!.toHuman("short") : "-.--"}</Amount> - </DesktopContainer> - <DesktopContainer align="right"> - <Reserves> - {smallLogos[0]} - {well.reserves![0] ? well.reserves![0].toHuman("short") : "-.--"} - </Reserves> - <Reserves> - {smallLogos[1]} - {well.reserves![1] ? well.reserves![1].toHuman("short") : "-.--"} - </Reserves> - {well.reserves && well.reserves.length > 2 ? <MoreReserves>{`+ ${well.reserves.length - 2} MORE`}</MoreReserves> : null} + <BalanceContainer> + <PositionBreakdown isWhitelisted={whitelisted} items={positionsUSD} totalDisplay={formatUSD(positionsUSD.total)} isLP={false} /> + </BalanceContainer> </DesktopContainer> <MobileContainer> <WellDetail> <TokenLogos>{logos}</TokenLogos> <TokenSymbols>{symbols.join("/")}</TokenSymbols> + {/* <Deployer>{deployer}</Deployer> */} </WellDetail> - <Amount>${wellLiquidity[index] ? Number(wellLiquidity[index]!.toHuman()).toFixed(2) : "-.--"}</Amount> + <BalanceContainer left={true}> + <PositionBreakdown + items={position} + isWhitelisted={whitelisted} + totalDisplay={`${position?.total.toHuman("short") || "-"} ${displayTokenSymbol(lpToken)}`} + isLP + /> + </BalanceContainer> + </MobileContainer> + <MobileContainer align="right"> + <BalanceContainer> + <PositionBreakdown items={positionsUSD} isWhitelisted={whitelisted} totalDisplay={formatUSD(positionsUSD.total)} isLP={false} /> + </BalanceContainer> </MobileContainer> </TableRow> ); - } + }; + + const WellRow: FC<{ well: Well | undefined; index: number }> = ({ well, index }) => { + if (!well) return null; - function MyLPsRow(well: any, index: any) { - if (!well || !wellLpBalances || !wellLpBalances[index] || wellLpBalances[index]!.eq(TokenValue.ZERO)) return; 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(<TokenLogo token={token} size={25} key={token.symbol} />); + smallLogos.push(<TokenLogo token={token} size={16} key={token.symbol} />); symbols.push(token.symbol); }); @@ -144,26 +235,36 @@ export const Wells = () => { <TokenSymbols>{symbols.join("/")}</TokenSymbols> </WellDetail> </DesktopContainer> + <DesktopContainer> + <WellPricing>{wellFunctionNames[index] ? wellFunctionNames[index] : "Price Function"}</WellPricing> + </DesktopContainer> <DesktopContainer align="right"> - <WellLPBalance>{`${wellLpBalances[index]!.toHuman("short")} ${well.lpToken.symbol}`}</WellLPBalance> + <TradingFee>0.00%</TradingFee> + </DesktopContainer> + <DesktopContainer align="right"> + <Amount>${wellLiquidity[index] ? wellLiquidity[index]!.toHuman("short") : "-.--"}</Amount> + </DesktopContainer> + <DesktopContainer align="right"> + <Reserves> + {smallLogos[0]} + {well.reserves![0] ? well.reserves![0].toHuman("short") : "-.--"} + </Reserves> + <Reserves> + {smallLogos[1]} + {well.reserves![1] ? well.reserves![1].toHuman("short") : "-.--"} + </Reserves> + {well.reserves && well.reserves.length > 2 ? <MoreReserves>{`+ ${well.reserves.length - 2} MORE`}</MoreReserves> : null} </DesktopContainer> <MobileContainer> <WellDetail> <TokenLogos>{logos}</TokenLogos> <TokenSymbols>{symbols.join("/")}</TokenSymbols> - {/* <Deployer>{deployer}</Deployer> */} </WellDetail> - <WellLPBalance>{`${wellLpBalances[index]!.toHuman("short")} ${well.lpToken.symbol}`}</WellLPBalance> + <Amount>${formatNum(wellLiquidity[index], { minDecimals: 2 })}</Amount> </MobileContainer> </TableRow> ); - } - - const rows = wells?.map((well, index) => { - return tab === 0 ? WellRow(well, index) : MyLPsRow(well, index); - }); - - const anyLpPositions = rows ? !rows.every((row) => row === undefined) : false; + }; return ( <Page> @@ -197,22 +298,29 @@ export const Wells = () => { <TableRow> <DesktopHeader>My Positions</DesktopHeader> <DesktopHeader align="right">My Liquidity</DesktopHeader> + <DesktopHeader align="right">USD Value</DesktopHeader> <MobileHeader>My Liquidity Positions</MobileHeader> + <MobileHeader align="right">USD Value</MobileHeader> </TableRow> </THead> )} <TBody> - {anyLpPositions === false && tab === 1 ? ( + {hasPositions === false && tab === 1 ? ( <> - <NoLPRow colSpan={2}> + <NoLPRow colSpan={isMobile ? 2 : 3}> <NoLPMessage>Liquidity Positions will appear here.</NoLPMessage> </NoLPRow> - <NoLPRowMobile> - <NoLPMessage>Liquidity Positions will appear here.</NoLPMessage> - </NoLPRowMobile> </> ) : ( - rows + wells?.map((well, index) => { + return tab === 0 ? ( + <> + <WellRow well={well} index={index} key={well.address} /> + </> + ) : ( + <MyLiquidityRow well={well} position={getPositionWithWell(well)} prices={lpTokenPrices} key={well.address} /> + ); + }) )} </TBody> </Table> @@ -329,18 +437,6 @@ const NoLPRow = styled.td` background-color: #fff; height: 120px; border-bottom: 0.5px solid #9ca3af; - @media (max-width: ${size.mobile}) { - display: none; - } -`; - -const NoLPRowMobile = styled.td` - background-color: #fff; - height: 120px; - border-bottom: 0.5px solid #9ca3af; - @media (min-width: ${size.mobile}) { - display: none; - } `; const NoLPMessage = styled.div` @@ -361,3 +457,25 @@ const WellLPBalance = styled.div` 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/useLPPositionSummary.tsx b/projects/dex-ui/src/tokens/useLPPositionSummary.tsx new file mode 100644 index 0000000000..30afa56358 --- /dev/null +++ b/projects/dex-ui/src/tokens/useLPPositionSummary.tsx @@ -0,0 +1,199 @@ +import { Token, TokenValue } from "@beanstalk/sdk"; +import { Well } from "@beanstalk/sdk/Wells"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { erc20ABI, useAccount, useQueryClient } from "wagmi"; + +import useSdk from "src/utils/sdk/useSdk"; +import { Log } from "src/utils/logger"; +import { useQuery } from "@tanstack/react-query"; +import { BigNumber as EthersBN } from "ethers"; +import { multicall } from "@wagmi/core"; +import BEANSTALK_ABI from "@beanstalk/protocol/abi/Beanstalk.json"; +import { useSiloBalanceMany } from "./useSiloBalance"; +import { useWells } from "src/wells/useWells"; + +export type LPBalanceSummary = { + silo: TokenValue; + external: TokenValue; + internal: TokenValue; + total: TokenValue; +}; + +type TokenMap<T> = { [tokenSymbol: string]: T }; + +export const useLPPositionSummary = () => { + const queryClient = useQueryClient(); + + const { data: wells } = useWells(); + const { address } = useAccount(); + const sdk = useSdk(); + + const [positions, setPositions] = useState<TokenMap<LPBalanceSummary>>({}); + + // Array of LP tokens for each well + const lpTokens = useMemo(() => { + const tokens: Token[] = []; + if (!wells) { + return tokens; + } else if (wells instanceof Well) { + wells.lpToken && tokens.push(wells.lpToken); + } else { + wells.forEach((well) => { + well?.lpToken && tokens.push(well.lpToken); + }); + } + + return tokens; + }, [wells]); + + /** + * Silo Balances + */ + const { data: siloBalances, ...siloBalanceRest } = useSiloBalanceMany(lpTokens); + + /** + * Contract calls to fetch internal & external balances + * Only fetch balances for wells with a defined LP Token + */ + const calls = useMemo(() => { + const contractCalls: any[] = []; + if (!address) return contractCalls; + Log.module("useLPPositionSummary").debug( + `Fetching internal & external token balances for ${lpTokens.length} lp tokens for address ${address}` + ); + + for (const t of lpTokens) { + contractCalls.push({ + address: t.address as `0x{string}`, + abi: erc20ABI, + functionName: "balanceOf", + args: [address] + }); + contractCalls.push({ + address: sdk.contracts.beanstalk.address as `0x{string}`, + abi: BEANSTALK_ABI, + functionName: "getInternalBalance", + args: [address, t.address] + }); + } + + return contractCalls; + }, [address, lpTokens, sdk]); + + /** + * Fetch external & internal balances + */ + const { data: balanceData, ...balanceRest } = useQuery<Record<string, Omit<LPBalanceSummary, "silo">>, Error>( + ["token", "lpSummary", ...lpTokens], + async () => { + /** + * TODO: check if there are any cached balances. + * If so, return those instead of fetching + */ + const balances: Record<string, Omit<LPBalanceSummary, "silo">> = {}; + if (!address || !lpTokens.length) return balances; + + const res = (await multicall({ + contracts: calls, + allowFailure: true + })) as unknown as EthersBN[]; + + for (let i = 0; i < res.length; i++) { + const lpTokenIndex = Math.floor(i / 2); + const lpToken = lpTokens[lpTokenIndex]; + let balance = balances?.[lpToken.symbol] || { + external: TokenValue.ZERO, + internal: TokenValue.ZERO + }; + + /// update the cache object & update useQuery cache + if (i % 2 === 0) { + balance.external = lpTokens[lpTokenIndex].fromBlockchain(res[i]); + queryClient.setQueryData(["token", "balance", lpToken.symbol], { [lpToken.symbol]: balance.external }); + } else { + balance.internal = lpTokens[lpTokenIndex].fromBlockchain(res[i]); + queryClient.setQueryData(["token", "internalBalance", lpToken.symbol], { [lpToken.symbol]: balance.internal }); + } + queryClient.setQueryData(["token", "balance"], (oldData: undefined | void | Record<string, TokenValue>) => { + if (!oldData) return { [lpToken.symbol]: balance.external }; + return { ...oldData, [lpToken.symbol]: balance.external }; + }); + + balances[lpToken.symbol] = balance; + } + + return balances; + }, + { + /** + * Token balances are cached for 30 seconds, refetch value every 30 seconds, + * when the window is hidden/not visible, stop background refresh, + * when the window gains focus, force a refresh even if cache is not stale * + */ + staleTime: 1000 * 30, + refetchInterval: 1000 * 30, + refetchIntervalInBackground: false, + refetchOnWindowFocus: "always" + } + ); + + // Combine silo, internal & external balances & update state + useEffect(() => { + if (!lpTokens.length || !balanceData || !siloBalances) return; + + const map = lpTokens.reduce<TokenMap<LPBalanceSummary>>((memo, curr) => { + const siloBalance = siloBalances?.[curr.symbol] || TokenValue.ZERO; + const internalExternal = balanceData?.[curr.symbol] || { + external: TokenValue.ZERO, + internal: TokenValue.ZERO + }; + + memo[curr.symbol] = { + silo: siloBalance, + internal: internalExternal.internal, + external: internalExternal.external, + total: siloBalance.add(internalExternal.internal).add(internalExternal.external) + }; + + return memo; + }, {}); + + setPositions(map); + }, [balanceData, lpTokens, siloBalances]); + + /** + * Refetch balances. Handle refetching both silo & external/internal balances + */ + const refetch = useCallback(async () => { + await Promise.all([balanceRest.refetch(), siloBalanceRest.refetch()]); + }, [balanceRest, siloBalanceRest]); + + /** + * Returns the LPBalanceSummary for a given well + */ + const getPositionWithWell = useCallback( + (well: Well | undefined) => { + if (!well?.lpToken?.symbol) return undefined; + return positions?.[well.lpToken.symbol]; + }, + [positions] + ); + + const hasPositions = useMemo(() => { + if (!positions) return false; + + return Object.entries(positions).some(([_, { total }]) => { + return total.gt(TokenValue.ZERO); + }); + }, [positions]); + + return { + data: positions, + isLoading: siloBalanceRest.isLoading || balanceRest.isLoading, + error: siloBalanceRest.error || balanceRest.error, + refetch: refetch, + isFetching: siloBalanceRest.isFetching || balanceRest.isFetching, + getPositionWithWell, + hasPositions + }; +}; diff --git a/projects/dex-ui/src/tokens/useSiloBalance.tsx b/projects/dex-ui/src/tokens/useSiloBalance.tsx index 3ea87ff878..26f243d95b 100644 --- a/projects/dex-ui/src/tokens/useSiloBalance.tsx +++ b/projects/dex-ui/src/tokens/useSiloBalance.tsx @@ -18,7 +18,7 @@ export const useSiloBalance = (token: Token) => { balance = TokenValue.ZERO; } else { const sdkLPToken = sdk.tokens.findByAddress(token.address); - const result = await sdk.silo.getBalance(sdkLPToken!, address, {source: DataSource.LEDGER}); + const result = await sdk.silo.getBalance(sdkLPToken!, address, { source: DataSource.LEDGER }); balance = result.amount; } return balance; @@ -38,3 +38,51 @@ export const useSiloBalance = (token: Token) => { return { data, isLoading, error, refetch, isFetching }; }; + +export const useSiloBalanceMany = (tokens: Token[]) => { + const { address } = useAccount(); + const sdk = useSdk(); + + const queryClient = useQueryClient(); + + const { data, isLoading, error, refetch, isFetching } = useQuery<Record<string, TokenValue>, Error>( + ["silo", "balance", sdk, ...tokens.map((token) => token.symbol)], + async () => { + const resultMap: Record<string, TokenValue> = {}; + if (!address) return resultMap; + + /** + * For some reason the symbol sdk.tokens.findByAddress returns a + * token with symbol of BEANETH & the token symbol stored in the well is BEANWETHCP2w + * + * We find the silo balance using the token with symbol BEANETH & + * then use BEANWETHCP2w as the key in the resultMap + */ + const _tokens = tokens + .map((token) => { + return { + token, + sdkToken: sdk.tokens.findByAddress(token.address) + }; + }) + .filter((tk) => tk.sdkToken !== undefined); + + const result = await Promise.all( + _tokens.map((item) => + sdk.silo + .getBalance(item.sdkToken!, address, { source: DataSource.LEDGER }) + .then((result) => ({ token: item.token, amount: result.amount })) + ) + ); + + result.forEach((val) => { + resultMap[val.token.symbol] = val.amount; + queryClient.setQueryData(["silo", "balance", sdk, val.token.symbol], val.amount); + }); + + return resultMap; + } + ); + + return { data, isLoading, error, refetch, isFetching }; +}; diff --git a/projects/dex-ui/src/tokens/useTokenBalanceInternal.tsx b/projects/dex-ui/src/tokens/useTokenBalanceInternal.tsx new file mode 100644 index 0000000000..9608e910a4 --- /dev/null +++ b/projects/dex-ui/src/tokens/useTokenBalanceInternal.tsx @@ -0,0 +1,43 @@ +import { Token, TokenValue } from "@beanstalk/sdk"; +import { useQuery } from "@tanstack/react-query"; +import useSdk from "src/utils/sdk/useSdk"; +import { useAccount } from "wagmi"; + +const emptyAddress = "0x0"; + +/** + * tokenBalanceInternal refers to farm balance + */ +export default function useTokenBalanceInternal(token: Token | undefined) { + const { address } = useAccount(); + const sdk = useSdk(); + + const beanstalk = sdk.contracts.beanstalk; + + const { data, isLoading, error, refetch, isFetching } = useQuery<Record<string, TokenValue>, Error>( + ["token", "internalBalance", sdk, token?.address || emptyAddress], + async () => { + const resultMap: Record<string, TokenValue> = {}; + + if (address && token) { + const result = await beanstalk.getInternalBalance(address, token.address); + resultMap[token.symbol] = token.fromBlockchain(result); + } + + return resultMap; + }, + { + /** + * Token balances are cached for 30 seconds, refetch value every 30 seconds, + * when the window is hidden/not visible, stop background refresh, + * when the window gains focus, force a refresh even if cache is not stale * + */ + staleTime: 1000 * 30, + refetchInterval: 1000 * 30, + refetchIntervalInBackground: false, + refetchOnWindowFocus: "always" + } + ); + + return { data, isLoading, error, refetch, isFetching }; +} diff --git a/projects/dex-ui/src/tokens/useTokenSupply.tsx b/projects/dex-ui/src/tokens/useTokenSupply.tsx index d765cb723a..e69c847fb7 100644 --- a/projects/dex-ui/src/tokens/useTokenSupply.tsx +++ b/projects/dex-ui/src/tokens/useTokenSupply.tsx @@ -20,3 +20,23 @@ export const useTokenSupply = (address: ERC20Token) => { return { totalSupply: data, loading: isLoading, error, refetch, isFetching }; }; + +/// useTokenSupply but for multiple tokens +export const useTokenSupplyMany = (tokens: ERC20Token[]) => { + const sdk = useSdk(); + + const { data, isLoading, error, refetch, isFetching } = useQuery<TokenValue[], Error>( + ["well", sdk, tokens, "totalSupply"], + async () => { + console.log("[useTokensSupply/FETCH]"); + let tokenTotalSupplies = await Promise.all(tokens.map((token) => token.getTotalSupply())); + return tokenTotalSupplies; + }, + { + staleTime: 1000 * 60, + refetchOnWindowFocus: false + } + ); + + return { totalSupply: data, loading: isLoading, error, refetch, isFetching }; +}; diff --git a/projects/dex-ui/src/utils/format.ts b/projects/dex-ui/src/utils/format.ts new file mode 100644 index 0000000000..ac12fea358 --- /dev/null +++ b/projects/dex-ui/src/utils/format.ts @@ -0,0 +1,46 @@ +import { Token, TokenValue } from "@beanstalk/sdk"; + +type NumberPrimitive = string | number | TokenValue | undefined; + +/** + * We can for the most part use TokenValue.toHuman("short"), + * but we can use this in cases where we don't want the shorthand K/M/B/T suffixes. + * We use Number.toLocaleString() instead of Number.toFixed() as it includes thousands separators + */ +export const formatNum = ( + val: NumberPrimitive, + options?: { + defaultValue?: string; + minDecimals?: number; + maxDecimals?: number; + } +) => { + if (val === undefined) return options?.defaultValue || "-.--"; + + const normalised = val instanceof TokenValue ? val.toHuman() : val.toString(); + + return Number(normalised).toLocaleString("en-US", { + minimumFractionDigits: 0 || options?.minDecimals, + maximumFractionDigits: 2 || options?.maxDecimals + }); +}; + +export const formatUSD = ( + val: NumberPrimitive, + options?: { + defaultValue: string; + } +) => { + return `$${formatNum(val || TokenValue.ZERO, { minDecimals: 2, maxDecimals: 2, ...options })}`; +}; + +const TokenSymbolMap = { + BEANWETHCP2w: "BEANETH LP" +}; +export const displayTokenSymbol = (token: Token) => { + if (token.symbol in TokenSymbolMap) { + return TokenSymbolMap[token.symbol as keyof typeof TokenSymbolMap]; + } + + return token.symbol; +}; diff --git a/projects/dex-ui/src/utils/ui/useIsMobile.ts b/projects/dex-ui/src/utils/ui/useIsMobile.ts new file mode 100644 index 0000000000..f6b7b08daa --- /dev/null +++ b/projects/dex-ui/src/utils/ui/useIsMobile.ts @@ -0,0 +1,18 @@ +import { useEffect, useState } from "react"; +import { size } from "src/breakpoints"; + +const useIsMobile = () => { + const [isMobile, setIsMobile] = useState(window.matchMedia(`(max-width: ${size.mobile})`).matches); + // 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)); + }; + }, []); + + return isMobile; +}; + +export default useIsMobile; diff --git a/projects/dex-ui/src/wells/useBeanstalkSiloWhitelist.ts b/projects/dex-ui/src/wells/useBeanstalkSiloWhitelist.ts new file mode 100644 index 0000000000..a125c52579 --- /dev/null +++ b/projects/dex-ui/src/wells/useBeanstalkSiloWhitelist.ts @@ -0,0 +1,24 @@ +import { useMemo } from "react"; +import { Well } from "@beanstalk/sdk/Wells"; + +const WHITELIST_MAP = { + /// BEANWETHCP2w (BEANETH LP) + "0xBEA0e11282e2bB5893bEcE110cF199501e872bAd": { + address: "0xBEA0e11282e2bB5893bEcE110cF199501e872bAd", + lpTokenAddress: "0xbea0e11282e2bb5893bece110cf199501e872bad" + } +}; + +/// set of wells that are whitelisted for the Beanstalk silo +export const useBeanstalkSiloWhitelist = () => { + const whitelistedAddresses = useMemo(() => Object.keys(WHITELIST_MAP), []); + + const getIsWhitelisted = (well: Well | undefined) => { + if (!well) return false; + const wellAddress = well.address; + + return wellAddress in WHITELIST_MAP; + }; + + return { whitelist: whitelistedAddresses, getIsWhitelisted } as const; +}; diff --git a/projects/dex-ui/src/wells/useWellLPTokenPrice.tsx b/projects/dex-ui/src/wells/useWellLPTokenPrice.tsx new file mode 100644 index 0000000000..64bc3e73fd --- /dev/null +++ b/projects/dex-ui/src/wells/useWellLPTokenPrice.tsx @@ -0,0 +1,81 @@ +import { useCallback, useEffect, useMemo, useState } from "react"; +import { ERC20Token, TokenValue } from "@beanstalk/sdk"; +import { Well } from "@beanstalk/sdk/Wells"; +import useSdk from "src/utils/sdk/useSdk"; +import { getPrice } from "src/utils/price/usePrice"; +import { useTokenSupplyMany } from "src/tokens/useTokenSupply"; + +type TokenMap<T> = Record<string, T>; + +/** + * LP Token Price is calculated as: TVL / total supply + * where: + * - TVL = (reserve1 amount * token1 price ) + (reserve2 amount + token2 price) + */ + +export const useWellLPTokenPrice = (params: Well | (Well | undefined)[] | undefined) => { + const [lpTokenPriceMap, setLPTokenPriceMap] = useState<TokenMap<TokenValue>>({}); + const sdk = useSdk(); + + const wells = useMemo(() => { + // Make into array for easier processing + if (!params) return []; + return Array.isArray(params) ? params : [params]; + }, [params]); + + const lpTokens = useMemo(() => { + if (!wells || !wells.length) return []; + const _tokens: ERC20Token[] = []; + wells.forEach((well) => well?.lpToken && _tokens.push(well.lpToken)); + return _tokens; + }, [wells]); + + const { totalSupply: tokenSupplies } = useTokenSupplyMany(lpTokens); + + const fetchData = useCallback(async () => { + if (!wells || !tokenSupplies?.length) return; + + const fetchTokenPrices = async () => { + const _tokenMap = wells.reduce<TokenMap<ERC20Token>>((memo, well) => { + if (!well || !well?.tokens) return memo; + well.tokens.forEach((token) => (memo[token.address] = token)); + return memo; + }, {}); + + const tokenLyst = Object.entries(_tokenMap); + + const prices = await Promise.all(tokenLyst.map(([, token]) => getPrice(token, sdk))); + const data = tokenLyst.reduce<TokenMap<TokenValue>>((memo, [tokenAddress], index) => { + memo[tokenAddress] = prices[index] || TokenValue.ZERO; + return memo; + }, {}); + return data; + }; + + const tokenPriceMap = await fetchTokenPrices(); + + const lpTokenPrices: TokenMap<TokenValue> = {}; + + for (const wellIdx in wells) { + const well = wells[wellIdx]; + + const tokens = well?.tokens; + const reserves = well?.reserves && well.reserves.length === 2 ? well.reserves : [TokenValue.ZERO, TokenValue.ZERO]; + const lpToken = well?.lpToken; + const lpTokenSupply = tokenSupplies[wellIdx] || TokenValue.ONE; + + if (well && tokens && lpToken) { + const wellReserveValues = reserves.map((reserve, rIdx) => reserve.mul(tokenPriceMap[tokens[rIdx].address] || TokenValue.ZERO)); + const wellTVL = wellReserveValues?.reduce((acc, val) => acc.add(val)); + lpTokenPrices[lpToken.address] = wellTVL && lpTokenSupply.gt(0) ? wellTVL.div(lpTokenSupply) : TokenValue.ZERO; + } + } + setLPTokenPriceMap(lpTokenPrices); + }, [sdk, tokenSupplies, wells]); + + useEffect(() => { + fetchData(); + }, [fetchData]); + + return { data: lpTokenPriceMap, fetch: fetchData } as const; +}; diff --git a/projects/sdk-wells/src/constants/abi/UnwrapAndSendEthJunction.json b/projects/sdk-wells/src/constants/abi/UnwrapAndSendEthJunction.json index 2ace55c24a..7c47d96b01 100644 --- a/projects/sdk-wells/src/constants/abi/UnwrapAndSendEthJunction.json +++ b/projects/sdk-wells/src/constants/abi/UnwrapAndSendEthJunction.json @@ -1 +1,10 @@ -[{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"unwrapAndSendETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file +[ + { + "inputs": [{ "internalType": "address", "name": "to", "type": "address" }], + "name": "unwrapAndSendETH", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/projects/sdk-wells/src/constants/addresses.ts b/projects/sdk-wells/src/constants/addresses.ts index c81dc73ac7..0a1e99b758 100644 --- a/projects/sdk-wells/src/constants/addresses.ts +++ b/projects/sdk-wells/src/constants/addresses.ts @@ -12,5 +12,5 @@ export const addresses = { DEPOT: Address.make("0xDEb0f00071497a5cc9b4A6B96068277e57A82Ae2"), PIPELINE: Address.make("0xb1bE0000C6B3C62749b5F0c92480146452D15423"), WETH9: Address.make("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"), - UNWRAP_AND_SEND_JUNCTION: Address.make("0x737cad465b75cdc4c11b3e312eb3fe5bef793d96"), + UNWRAP_AND_SEND_JUNCTION: Address.make("0x737cad465b75cdc4c11b3e312eb3fe5bef793d96") }; diff --git a/projects/sdk-wells/src/lib/swap/Quote.ts b/projects/sdk-wells/src/lib/swap/Quote.ts index d064c95d10..54f90b22a9 100644 --- a/projects/sdk-wells/src/lib/swap/Quote.ts +++ b/projects/sdk-wells/src/lib/swap/Quote.ts @@ -1,7 +1,14 @@ import { Token, TokenValue } from "@beanstalk/sdk-core"; import { Route } from "src/lib/routing"; import { Direction, SwapStep } from "src/lib/swap/SwapStep"; -import { Depot, Depot__factory, WETH9, WETH9__factory, UnwrapAndSendEthJunction__factory, UnwrapAndSendEthJunction } from "src/constants/generated"; +import { + Depot, + Depot__factory, + WETH9, + WETH9__factory, + UnwrapAndSendEthJunction__factory, + UnwrapAndSendEthJunction +} from "src/constants/generated"; import { addresses } from "src/constants/addresses"; import { WellsSDK } from "src/lib/WellsSDK"; import { TxOverrides } from "src/lib/Well"; @@ -57,7 +64,10 @@ export class Quote { this.account = account; this.weth9 = WETH9__factory.connect(addresses.WETH9.get(this.sdk.chainId), this.sdk.providerOrSigner); - this.unwrapAndSendEthJunction = UnwrapAndSendEthJunction__factory.connect(addresses.UNWRAP_AND_SEND_JUNCTION.get(this.sdk.chainId), this.sdk.providerOrSigner); + this.unwrapAndSendEthJunction = UnwrapAndSendEthJunction__factory.connect( + addresses.UNWRAP_AND_SEND_JUNCTION.get(this.sdk.chainId), + this.sdk.providerOrSigner + ); for (const { from, to, well } of this.route) { if (from.symbol === "ETH" && to.symbol === "WETH") { diff --git a/projects/sdk/src/classes/Workflow.ts b/projects/sdk/src/classes/Workflow.ts index 5a5421b216..337a15c13b 100644 --- a/projects/sdk/src/classes/Workflow.ts +++ b/projects/sdk/src/classes/Workflow.ts @@ -307,7 +307,7 @@ export abstract class Workflow< default: Workflow.sdk.debug(`[Workflow][${this.name}][add] Not a bundle of Pipeline operations`); } - + this._generators.push(input); this._options.push(pipelineOptions || options || null); // null = no options set } @@ -568,7 +568,12 @@ export abstract class Workflow< */ protected async estimateAndEncodeSteps(amountIn: ethers.BigNumber | TokenValue, runMode: RunMode, data: RunData) { Workflow.sdk.debug(`[Workflow][${this.name}][estimateAndEncodeSteps] building...`, { amountIn, runMode, data }); - await this.buildSteps(amountIn instanceof TokenValue ? amountIn.toBigNumber() : amountIn, { runMode, data, steps: this._steps, tagMap: this._tagMap }); + await this.buildSteps(amountIn instanceof TokenValue ? amountIn.toBigNumber() : amountIn, { + runMode, + data, + steps: this._steps, + tagMap: this._tagMap + }); Workflow.sdk.debug(`[Workflow][${this.name}][estimateAndEncodeSteps] encoding...`, { count: this._steps.length }); return this.encodeSteps(); } diff --git a/projects/sdk/src/lib/farm/LibraryPresets.ts b/projects/sdk/src/lib/farm/LibraryPresets.ts index ae501f24e4..07ae96a164 100644 --- a/projects/sdk/src/lib/farm/LibraryPresets.ts +++ b/projects/sdk/src/lib/farm/LibraryPresets.ts @@ -258,18 +258,24 @@ export class LibraryPresets { // This transfers the output token back to Beanstalk, from PIPELINE. Used when transferBack == true const transferClipboard = { - tag: "swap", - copySlot: 0, + tag: "swap", + copySlot: 0, pasteSlot: 2 - } - const transferToBeanstalk = new sdk.farm.actions.TransferToken(toToken.address, account, FarmFromMode.EXTERNAL, FarmToMode.INTERNAL, transferClipboard); + }; + const transferToBeanstalk = new sdk.farm.actions.TransferToken( + toToken.address, + account, + FarmFromMode.EXTERNAL, + FarmToMode.INTERNAL, + transferClipboard + ); // This approves the transferToBeanstalk operation. Used when transferBack == true const approveClipboard = { - tag: "swap", - copySlot: 0, + tag: "swap", + copySlot: 0, pasteSlot: 1 - } + }; const approveBack = new sdk.farm.actions.ApproveERC20(toToken, sdk.contracts.beanstalk.address, approveClipboard); // When transferBack is true, we tell Wells to send the swap result to PIPELINE, otherwise @@ -314,19 +320,25 @@ export class LibraryPresets { // This approves the transferToBeanstalk operation. const approveClipboard = { - tag: "amountToDeposit", - copySlot: 0, + tag: "amountToDeposit", + copySlot: 0, pasteSlot: 1 - } + }; const approveBack = new sdk.farm.actions.ApproveERC20(well.lpToken, sdk.contracts.beanstalk.address, approveClipboard); // Transfers the output token back to Beanstalk, from PIPELINE. const transferClipboard = { - tag: "amountToDeposit", - copySlot: 0, + tag: "amountToDeposit", + copySlot: 0, pasteSlot: 2 - } - const transferToBeanstalk = new sdk.farm.actions.TransferToken(well.address, account, FarmFromMode.EXTERNAL, FarmToMode.INTERNAL, transferClipboard); + }; + const transferToBeanstalk = new sdk.farm.actions.TransferToken( + well.address, + account, + FarmFromMode.EXTERNAL, + FarmToMode.INTERNAL, + transferClipboard + ); result.push(transfer); advancedPipe.add(addLiquidity, { tag: "amountToDeposit" }); diff --git a/projects/sdk/src/lib/farm/actions/ApproveERC20.ts b/projects/sdk/src/lib/farm/actions/ApproveERC20.ts index b4afd27973..639d026f6f 100644 --- a/projects/sdk/src/lib/farm/actions/ApproveERC20.ts +++ b/projects/sdk/src/lib/farm/actions/ApproveERC20.ts @@ -36,7 +36,9 @@ export class ApproveERC20 extends StepClass<AdvancedPipePreparedResult> { return { target: this.token.address, callData: this.token.getContract().interface.encodeFunctionData("approve", [this.spender, _amountInStep]), - clipboard: this.clipboard ? Clipboard.encodeSlot(context.step.findTag(this.clipboard.tag), this.clipboard.copySlot, this.clipboard.pasteSlot) : undefined + clipboard: this.clipboard + ? Clipboard.encodeSlot(context.step.findTag(this.clipboard.tag), this.clipboard.copySlot, this.clipboard.pasteSlot) + : undefined }; }, decode: (data: string) => this.token.getContract().interface.decodeFunctionData("approve", data), diff --git a/projects/sdk/src/lib/farm/actions/Deposit.ts b/projects/sdk/src/lib/farm/actions/Deposit.ts index d6b1d41073..438b3c605b 100644 --- a/projects/sdk/src/lib/farm/actions/Deposit.ts +++ b/projects/sdk/src/lib/farm/actions/Deposit.ts @@ -10,8 +10,8 @@ export class Deposit extends StepClass<BasicPreparedResult> { public clipboard?: ClipboardSettings; constructor( - public readonly token: Token, - public readonly fromMode: FarmFromMode = FarmFromMode.INTERNAL_EXTERNAL, + public readonly token: Token, + public readonly fromMode: FarmFromMode = FarmFromMode.INTERNAL_EXTERNAL, clipboard?: ClipboardSettings ) { super(); @@ -22,11 +22,11 @@ export class Deposit extends StepClass<BasicPreparedResult> { // Checking if the user isn't directly depositing BEANETH const indirectBeanEth = this.token.symbol === "BEANETH" && context.step.index > 0; const beanEthClipboard = { - tag: `deposit${context.step.index}Amount`, - copySlot: 6, + tag: `deposit${context.step.index}Amount`, + copySlot: 6, pasteSlot: 1 }; - + if (indirectBeanEth && !this.clipboard) this.clipboard = beanEthClipboard; return { @@ -48,9 +48,11 @@ export class Deposit extends StepClass<BasicPreparedResult> { _amountInStep, this.fromMode ]), - clipboard: this.clipboard ? Clipboard.encodeSlot(context.step.findTag(this.clipboard.tag), this.clipboard.copySlot, this.clipboard.pasteSlot) : undefined + clipboard: this.clipboard + ? Clipboard.encodeSlot(context.step.findTag(this.clipboard.tag), this.clipboard.copySlot, this.clipboard.pasteSlot) + : undefined }; - }, + }, decode: (data: string) => Deposit.sdk.contracts.beanstalk.interface.decodeFunctionData("deposit", data), decodeResult: (result: string) => Deposit.sdk.contracts.beanstalk.interface.decodeFunctionResult("deposit", result) }; diff --git a/projects/sdk/src/lib/farm/actions/TransferToken.ts b/projects/sdk/src/lib/farm/actions/TransferToken.ts index c376dd1a22..035c446232 100644 --- a/projects/sdk/src/lib/farm/actions/TransferToken.ts +++ b/projects/sdk/src/lib/farm/actions/TransferToken.ts @@ -41,7 +41,9 @@ export class TransferToken extends StepClass<BasicPreparedResult> { this._fromMode, // this._toMode // ]), - clipboard: this.clipboard ? Clipboard.encodeSlot(context.step.findTag(this.clipboard.tag), this.clipboard.copySlot, this.clipboard.pasteSlot) : undefined + clipboard: this.clipboard + ? Clipboard.encodeSlot(context.step.findTag(this.clipboard.tag), this.clipboard.copySlot, this.clipboard.pasteSlot) + : undefined }; }, decode: (data: string) => TransferToken.sdk.contracts.beanstalk.interface.decodeFunctionData("transferToken", data), diff --git a/projects/sdk/src/lib/farm/actions/UnwrapEth.ts b/projects/sdk/src/lib/farm/actions/UnwrapEth.ts index 5a740191f1..e23c7c594d 100644 --- a/projects/sdk/src/lib/farm/actions/UnwrapEth.ts +++ b/projects/sdk/src/lib/farm/actions/UnwrapEth.ts @@ -13,26 +13,26 @@ export class UnwrapEth extends StepClass<BasicPreparedResult> { async run(_amountInStep: ethers.BigNumber, context: RunContext) { if (!this.clipboard) { - const pipelineBeanWethSwapIndex = context.steps.findIndex(step => step.name === "pipelineBeanWethSwap"); + const pipelineBeanWethSwapIndex = context.steps.findIndex((step) => step.name === "pipelineBeanWethSwap"); // If the action before (happens when reverse estimating) or after this one is a BEAN -> WETH swap through Pipeline... if (pipelineBeanWethSwapIndex >= 0 && Math.abs(pipelineBeanWethSwapIndex - context.step.index) === 1) { // We use clipboard... this.clipboard = { // Then find the correct tag in the tag map - tag: Object.keys(context.tagMap).find(tag => context.tagMap[tag] === pipelineBeanWethSwapIndex)!, - copySlot: 9, + tag: Object.keys(context.tagMap).find((tag) => context.tagMap[tag] === pipelineBeanWethSwapIndex)!, + copySlot: 9, pasteSlot: 0 }; - }; - }; + } + } return { name: this.name, amountOut: _amountInStep, // amountInStep should be an amount of ETH. prepare: () => { - UnwrapEth.sdk.debug(`[${this.name}.encode()]`, { - fromMode: this.fromMode, - _amountInStep, - context, + UnwrapEth.sdk.debug(`[${this.name}.encode()]`, { + fromMode: this.fromMode, + _amountInStep, + context, clipboard: this.clipboard }); return { @@ -41,11 +41,13 @@ export class UnwrapEth extends StepClass<BasicPreparedResult> { _amountInStep, // ignore minAmountOut since there is no slippage this.fromMode ]), - clipboard: this.clipboard ? Clipboard.encodeSlot(context.step.findTag(this.clipboard.tag), this.clipboard.copySlot, this.clipboard.pasteSlot) : undefined + clipboard: this.clipboard + ? Clipboard.encodeSlot(context.step.findTag(this.clipboard.tag), this.clipboard.copySlot, this.clipboard.pasteSlot) + : undefined }; }, decode: (data: string) => UnwrapEth.sdk.contracts.beanstalk.interface.decodeFunctionData("unwrapEth", data), - decodeResult: (result: string) => UnwrapEth.sdk.contracts.beanstalk.interface.decodeFunctionResult("unwrapEth", result), - }; + decodeResult: (result: string) => UnwrapEth.sdk.contracts.beanstalk.interface.decodeFunctionResult("unwrapEth", result) + }; } } diff --git a/projects/sdk/src/lib/swap/Swap.test.ts b/projects/sdk/src/lib/swap/Swap.test.ts index b859fe8c07..ca9e0b9bf8 100644 --- a/projects/sdk/src/lib/swap/Swap.test.ts +++ b/projects/sdk/src/lib/swap/Swap.test.ts @@ -153,8 +153,7 @@ async function swapTest(tokenIn: Token, tokenOut: Token, from: FarmFromMode, to: const afterBalance = pipelineBalancesAfter.get(token); expect(beforeBalance.external.eq(afterBalance!.external)); expect(beforeBalance.internal.eq(afterBalance!.internal)); - }; - + } } async function getBalance(token: Token, mode: string, user?: string) { @@ -178,4 +177,4 @@ async function getPipelineBalances() { const allBalances = erc20Balances.set(sdk.tokens.ETH, ethBalance); return allBalances; -}; +} diff --git a/projects/sdk/src/lib/swap/Swap.ts b/projects/sdk/src/lib/swap/Swap.ts index 1fc51620ae..218e0bd9c0 100644 --- a/projects/sdk/src/lib/swap/Swap.ts +++ b/projects/sdk/src/lib/swap/Swap.ts @@ -36,13 +36,17 @@ export class Swap { for (let i = 0; i < route.length; i++) { if (route.getStep(i - 1)) { - if (route.getStep(i - 1).from === "BEAN" && route.getStep(i - 1).to === "WETH" && - route.getStep(i).from === "WETH" && route.getStep(i).to === "ETH") { - useAdvancedFarm = true; - break; - }; - }; - }; + if ( + route.getStep(i - 1).from === "BEAN" && + route.getStep(i - 1).to === "WETH" && + route.getStep(i).from === "WETH" && + route.getStep(i).to === "ETH" + ) { + useAdvancedFarm = true; + break; + } + } + } if (useAdvancedFarm) { workflow = Swap.sdk.farm.createAdvancedFarm(`Swap ${tokenIn.symbol}->${tokenOut.symbol}`); diff --git a/projects/sdk/src/lib/swap/SwapOperation.ts b/projects/sdk/src/lib/swap/SwapOperation.ts index 54ac3128c7..ef504978ec 100644 --- a/projects/sdk/src/lib/swap/SwapOperation.ts +++ b/projects/sdk/src/lib/swap/SwapOperation.ts @@ -18,7 +18,7 @@ export class SwapOperation { sdk: BeanstalkSDK, readonly tokenIn: Token, readonly tokenOut: Token, - private readonly workflow: AdvancedFarmWorkflow|Workflow, + private readonly workflow: AdvancedFarmWorkflow | Workflow, private readonly route: Route ) { SwapOperation.sdk = sdk; diff --git a/projects/sdk/src/types/index.ts b/projects/sdk/src/types/index.ts index d25fbbccd8..a8752b9d8e 100644 --- a/projects/sdk/src/types/index.ts +++ b/projects/sdk/src/types/index.ts @@ -1,7 +1,7 @@ export type StringMap<T> = { [address: string]: T }; export type ClipboardSettings = { - tag: string, - copySlot: number, - pasteSlot: number, -}; \ No newline at end of file + tag: string; + copySlot: number; + pasteSlot: number; +}; diff --git a/projects/subgraph-beanft/abis/basin.json b/projects/subgraph-beanft/abis/basin.json index 0c66e741c7..90ff5705eb 100644 --- a/projects/subgraph-beanft/abis/basin.json +++ b/projects/subgraph-beanft/abis/basin.json @@ -1,569 +1,542 @@ [ - { - "inputs": [], - "name": "ApprovalCallerNotOwnerNorApproved", - "type": "error" - }, - { "inputs": [], "name": "ApprovalQueryForNonexistentToken", "type": "error" }, - { "inputs": [], "name": "ApproveToCaller", "type": "error" }, - { "inputs": [], "name": "BalanceQueryForZeroAddress", "type": "error" }, - { "inputs": [], "name": "MintERC2309QuantityExceedsLimit", "type": "error" }, - { "inputs": [], "name": "MintToZeroAddress", "type": "error" }, - { "inputs": [], "name": "MintZeroQuantity", "type": "error" }, - { "inputs": [], "name": "OwnerQueryForNonexistentToken", "type": "error" }, - { - "inputs": [], - "name": "OwnershipNotInitializedForExtraData", - "type": "error" - }, - { - "inputs": [], - "name": "TransferCallerNotOwnerNorApproved", - "type": "error" - }, - { "inputs": [], "name": "TransferFromIncorrectOwner", "type": "error" }, - { - "inputs": [], - "name": "TransferToNonERC721ReceiverImplementer", - "type": "error" - }, - { "inputs": [], "name": "TransferToZeroAddress", "type": "error" }, - { "inputs": [], "name": "URIQueryForNonexistentToken", "type": "error" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "previousAdmin", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "newAdmin", - "type": "address" - } - ], - "name": "AdminChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "approved", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "operator", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "approved", - "type": "bool" - } - ], - "name": "ApprovalForAll", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "beacon", - "type": "address" - } - ], - "name": "BeaconUpgraded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "fromTokenId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "toTokenId", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - } - ], - "name": "ConsecutiveTransfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "Upgraded", - "type": "event" - }, - { - "inputs": [ - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "approve", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" } - ], - "name": "balanceOf", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "baseURI", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "burn", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "bool", "name": "approvalCheck", "type": "bool" } - ], - "name": "burn", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "exists", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "getApproved", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" } - ], - "name": "getAux", - "outputs": [{ "internalType": "uint64", "name": "", "type": "uint64" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "index", "type": "uint256" } - ], - "name": "getOwnershipAt", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "addr", "type": "address" }, - { - "internalType": "uint64", - "name": "startTimestamp", - "type": "uint64" - }, - { "internalType": "bool", "name": "burned", "type": "bool" }, - { "internalType": "uint24", "name": "extraData", "type": "uint24" } - ], - "internalType": "struct IERC721AUpgradeable.TokenOwnership", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "index", "type": "uint256" } - ], - "name": "getOwnershipOf", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "addr", "type": "address" }, - { - "internalType": "uint64", - "name": "startTimestamp", - "type": "uint64" - }, - { "internalType": "bool", "name": "burned", "type": "bool" }, - { "internalType": "uint24", "name": "extraData", "type": "uint24" } - ], - "internalType": "struct IERC721AUpgradeable.TokenOwnership", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string", "name": "name_", "type": "string" }, - { "internalType": "string", "name": "symbol_", "type": "string" }, - { - "internalType": "address[]", - "name": "addresses_", - "type": "address[]" - }, - { "internalType": "uint256[]", "name": "amt_", "type": "uint256[]" } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "index", "type": "uint256" } - ], - "name": "initializeOwnershipAt", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" }, - { "internalType": "address", "name": "operator", "type": "address" } - ], - "name": "isApprovedForAll", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "nextTokenId", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" } - ], - "name": "numberMinted", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "ownerOf", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxiableUUID", - "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "safeTransferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "bytes", "name": "_data", "type": "bytes" } - ], - "name": "safeTransferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "operator", "type": "address" }, - { "internalType": "bool", "name": "approved", "type": "bool" } - ], - "name": "setApprovalForAll", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" }, - { "internalType": "uint64", "name": "aux", "type": "uint64" } - ], - "name": "setAux", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" } - ], - "name": "supportsInterface", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint256", "name": "x", "type": "uint256" }], - "name": "toString", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "tokenURI", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalBurned", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalMinted", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "transferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "newOwner", "type": "address" } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newImplementation", - "type": "address" - } - ], - "name": "upgradeTo", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newImplementation", - "type": "address" - }, - { "internalType": "bytes", "name": "data", "type": "bytes" } - ], - "name": "upgradeToAndCall", - "outputs": [], - "stateMutability": "payable", - "type": "function" - } - ] - \ No newline at end of file + { + "inputs": [], + "name": "ApprovalCallerNotOwnerNorApproved", + "type": "error" + }, + { "inputs": [], "name": "ApprovalQueryForNonexistentToken", "type": "error" }, + { "inputs": [], "name": "ApproveToCaller", "type": "error" }, + { "inputs": [], "name": "BalanceQueryForZeroAddress", "type": "error" }, + { "inputs": [], "name": "MintERC2309QuantityExceedsLimit", "type": "error" }, + { "inputs": [], "name": "MintToZeroAddress", "type": "error" }, + { "inputs": [], "name": "MintZeroQuantity", "type": "error" }, + { "inputs": [], "name": "OwnerQueryForNonexistentToken", "type": "error" }, + { + "inputs": [], + "name": "OwnershipNotInitializedForExtraData", + "type": "error" + }, + { + "inputs": [], + "name": "TransferCallerNotOwnerNorApproved", + "type": "error" + }, + { "inputs": [], "name": "TransferFromIncorrectOwner", "type": "error" }, + { + "inputs": [], + "name": "TransferToNonERC721ReceiverImplementer", + "type": "error" + }, + { "inputs": [], "name": "TransferToZeroAddress", "type": "error" }, + { "inputs": [], "name": "URIQueryForNonexistentToken", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "fromTokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "toTokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "ConsecutiveTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "baseURI", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "bool", "name": "approvalCheck", "type": "bool" } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "exists", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "getApproved", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }], + "name": "getAux", + "outputs": [{ "internalType": "uint64", "name": "", "type": "uint64" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "index", "type": "uint256" }], + "name": "getOwnershipAt", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "addr", "type": "address" }, + { + "internalType": "uint64", + "name": "startTimestamp", + "type": "uint64" + }, + { "internalType": "bool", "name": "burned", "type": "bool" }, + { "internalType": "uint24", "name": "extraData", "type": "uint24" } + ], + "internalType": "struct IERC721AUpgradeable.TokenOwnership", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "index", "type": "uint256" }], + "name": "getOwnershipOf", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "addr", "type": "address" }, + { + "internalType": "uint64", + "name": "startTimestamp", + "type": "uint64" + }, + { "internalType": "bool", "name": "burned", "type": "bool" }, + { "internalType": "uint24", "name": "extraData", "type": "uint24" } + ], + "internalType": "struct IERC721AUpgradeable.TokenOwnership", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "name_", "type": "string" }, + { "internalType": "string", "name": "symbol_", "type": "string" }, + { + "internalType": "address[]", + "name": "addresses_", + "type": "address[]" + }, + { "internalType": "uint256[]", "name": "amt_", "type": "uint256[]" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "index", "type": "uint256" }], + "name": "initializeOwnershipAt", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "operator", "type": "address" } + ], + "name": "isApprovedForAll", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nextTokenId", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }], + "name": "numberMinted", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "ownerOf", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "bytes", "name": "_data", "type": "bytes" } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "operator", "type": "address" }, + { "internalType": "bool", "name": "approved", "type": "bool" } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "uint64", "name": "aux", "type": "uint64" } + ], + "name": "setAux", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "x", "type": "uint256" }], + "name": "toString", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "tokenURI", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalBurned", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalMinted", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { "internalType": "bytes", "name": "data", "type": "bytes" } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/projects/subgraph-beanft/networks.json b/projects/subgraph-beanft/networks.json index 260c2eb93d..a464522b95 100644 --- a/projects/subgraph-beanft/networks.json +++ b/projects/subgraph-beanft/networks.json @@ -17,4 +17,4 @@ "startBlock": 18590231 } } -} \ No newline at end of file +} diff --git a/projects/subgraph-beanft/schema.graphql b/projects/subgraph-beanft/schema.graphql index 0a25b889fe..7bd60d8ba3 100644 --- a/projects/subgraph-beanft/schema.graphql +++ b/projects/subgraph-beanft/schema.graphql @@ -4,8 +4,8 @@ type BeaNFTUser @entity { genesis: [Int!] winter: [Int!] basin: [Int!] -}, +} type CollectionData @entity { id: ID! minted: [Int!] -} \ No newline at end of file +} diff --git a/projects/subgraph-beanft/src/mappings.ts b/projects/subgraph-beanft/src/mappings.ts index 22cf7e7bbc..61d610c14e 100644 --- a/projects/subgraph-beanft/src/mappings.ts +++ b/projects/subgraph-beanft/src/mappings.ts @@ -1,225 +1,226 @@ -import { log } from "@graphprotocol/graph-ts" -import { - ConsecutiveTransfer as ConsecutiveTransferEventBasin, - Transfer as TransferEventBasin -} from "../generated/basin/basin" +import { log } from "@graphprotocol/graph-ts"; +import { ConsecutiveTransfer as ConsecutiveTransferEventBasin, Transfer as TransferEventBasin } from "../generated/basin/basin"; import { ConsecutiveTransfer as ConsecutiveTransferEventBarnRaise, Transfer as TransferEventBarnRaise -} from "../generated/barnraise/barnraise" -import { - Transfer as TransferEventGenesis -} from "../generated/genesis/genesis" -import { - Transfer as TransferEventWinter -} from "../generated/winter/winter" -import { - BeaNFTUser, CollectionData -} from "../generated/schema" +} from "../generated/barnraise/barnraise"; +import { Transfer as TransferEventGenesis } from "../generated/genesis/genesis"; +import { Transfer as TransferEventWinter } from "../generated/winter/winter"; +import { BeaNFTUser, CollectionData } from "../generated/schema"; -const zeroAddress = '0x0000000000000000000000000000000000000000' +const zeroAddress = "0x0000000000000000000000000000000000000000"; export function handleTransferGenesis(event: TransferEventGenesis): void { - log.info("GENESIS TRANSFER! BEANFT: {}, RECEIVER: {}", [event.params.tokenId.toI32().toString(), event.params.to.toHexString()]) - let from = event.params.from.toHexString() - let to = event.params.to.toHexString() - let tokenId = event.params.tokenId.toI32() - transferHandler(from, to, tokenId, 'genesis') + log.info("GENESIS TRANSFER! BEANFT: {}, RECEIVER: {}", [event.params.tokenId.toI32().toString(), event.params.to.toHexString()]); + let from = event.params.from.toHexString(); + let to = event.params.to.toHexString(); + let tokenId = event.params.tokenId.toI32(); + transferHandler(from, to, tokenId, "genesis"); } export function handleTransferWinter(event: TransferEventWinter): void { - log.info("WINTER TRANSFER! BEANFT: {}, RECEIVER: {}", [event.params.tokenId.toI32().toString(), event.params.to.toHexString()]) - let from = event.params.from.toHexString() - let to = event.params.to.toHexString() - let tokenId = event.params.tokenId.toI32() - transferHandler(from, to, tokenId, 'winter') + log.info("WINTER TRANSFER! BEANFT: {}, RECEIVER: {}", [event.params.tokenId.toI32().toString(), event.params.to.toHexString()]); + let from = event.params.from.toHexString(); + let to = event.params.to.toHexString(); + let tokenId = event.params.tokenId.toI32(); + transferHandler(from, to, tokenId, "winter"); } export function handleTransferBarnRaise(event: TransferEventBarnRaise): void { - log.info("BARN RAISE TRANSFER! BEANFT: {}, RECEIVER: {}", [event.params.tokenId.toI32().toString(), event.params.to.toHexString()]) - let from = event.params.from.toHexString() - let to = event.params.to.toHexString() - let tokenId = event.params.tokenId.toI32() - transferHandler(from, to, tokenId, 'barnraise') + log.info("BARN RAISE TRANSFER! BEANFT: {}, RECEIVER: {}", [event.params.tokenId.toI32().toString(), event.params.to.toHexString()]); + let from = event.params.from.toHexString(); + let to = event.params.to.toHexString(); + let tokenId = event.params.tokenId.toI32(); + transferHandler(from, to, tokenId, "barnraise"); } export function handleTransferBasin(event: TransferEventBasin): void { - log.info("BASIN TRANSFER! BEANFT: {}, RECEIVER: {}", [event.params.tokenId.toI32().toString(), event.params.to.toHexString()]) - let from = event.params.from.toHexString() - let to = event.params.to.toHexString() - let tokenId = event.params.tokenId.toI32() - transferHandler(from, to, tokenId, 'basin') + log.info("BASIN TRANSFER! BEANFT: {}, RECEIVER: {}", [event.params.tokenId.toI32().toString(), event.params.to.toHexString()]); + let from = event.params.from.toHexString(); + let to = event.params.to.toHexString(); + let tokenId = event.params.tokenId.toI32(); + transferHandler(from, to, tokenId, "basin"); } export function handleConsecutiveTransferBarnRaise(event: ConsecutiveTransferEventBarnRaise): void { - log.info("BARN RAISE CONSECUTIVE TRANSFER! FROM BEANFT {} TO {}, RECEIVER: {}", [event.params.fromTokenId.toString(), event.params.toTokenId.toString(), event.params.to.toHexString()]) - let fromTokenId = event.params.fromTokenId.toI32() - let toTokenId = event.params.toTokenId.toI32() - let from = event.params.from.toHexString() - let to = event.params.to.toHexString() - consecutiveTransferHandler(fromTokenId, toTokenId, from, to, 'barnraise') + log.info("BARN RAISE CONSECUTIVE TRANSFER! FROM BEANFT {} TO {}, RECEIVER: {}", [ + event.params.fromTokenId.toString(), + event.params.toTokenId.toString(), + event.params.to.toHexString() + ]); + let fromTokenId = event.params.fromTokenId.toI32(); + let toTokenId = event.params.toTokenId.toI32(); + let from = event.params.from.toHexString(); + let to = event.params.to.toHexString(); + consecutiveTransferHandler(fromTokenId, toTokenId, from, to, "barnraise"); } export function handleConsecutiveTransferBasin(event: ConsecutiveTransferEventBasin): void { - log.info("BASIN CONSECUTIVE TRANSFER! FROM BEANFT {} TO {}, RECEIVER: {}", [event.params.fromTokenId.toString(), event.params.toTokenId.toString(), event.params.to.toHexString()]) - let fromTokenId = event.params.fromTokenId.toI32() - let toTokenId = event.params.toTokenId.toI32() - let from = event.params.from.toHexString() - let to = event.params.to.toHexString() - consecutiveTransferHandler(fromTokenId, toTokenId, from, to, 'basin') + log.info("BASIN CONSECUTIVE TRANSFER! FROM BEANFT {} TO {}, RECEIVER: {}", [ + event.params.fromTokenId.toString(), + event.params.toTokenId.toString(), + event.params.to.toHexString() + ]); + let fromTokenId = event.params.fromTokenId.toI32(); + let toTokenId = event.params.toTokenId.toI32(); + let from = event.params.from.toHexString(); + let to = event.params.to.toHexString(); + consecutiveTransferHandler(fromTokenId, toTokenId, from, to, "basin"); } -function transferHandler(from:string, to:string, tokenId:i32, mode:string): void { - let source = BeaNFTUser.load(from) - let destination = BeaNFTUser.load(to) - if (source) { // If source is true this means it is a user wallet, as we make sure to not add the zero address as an user - if (mode === 'genesis') { - let nftIndex = source.genesis!.indexOf(tokenId) - let genesisNew = source.genesis - genesisNew!.splice(nftIndex, 1) - source.genesis = genesisNew - } else if (mode === 'winter') { - let nftIndex = source.winter!.indexOf(tokenId) - let winterNew = source.winter - winterNew!.splice(nftIndex, 1) - source.winter = winterNew - } else if (mode === 'barnraise') { - let nftIndex = source.barnRaise!.indexOf(tokenId) - let barnRaiseNew = source.barnRaise - barnRaiseNew!.splice(nftIndex, 1) - source.barnRaise = barnRaiseNew - } else if (mode === 'basin') { - let nftIndex = source.basin!.indexOf(tokenId) - let basinNew = source.basin - basinNew!.splice(nftIndex, 1) - source.basin = basinNew +function transferHandler(from: string, to: string, tokenId: i32, mode: string): void { + let source = BeaNFTUser.load(from); + let destination = BeaNFTUser.load(to); + if (source) { + // If source is true this means it is a user wallet, as we make sure to not add the zero address as an user + if (mode === "genesis") { + let nftIndex = source.genesis!.indexOf(tokenId); + let genesisNew = source.genesis; + genesisNew!.splice(nftIndex, 1); + source.genesis = genesisNew; + } else if (mode === "winter") { + let nftIndex = source.winter!.indexOf(tokenId); + let winterNew = source.winter; + winterNew!.splice(nftIndex, 1); + source.winter = winterNew; + } else if (mode === "barnraise") { + let nftIndex = source.barnRaise!.indexOf(tokenId); + let barnRaiseNew = source.barnRaise; + barnRaiseNew!.splice(nftIndex, 1); + source.barnRaise = barnRaiseNew; + } else if (mode === "basin") { + let nftIndex = source.basin!.indexOf(tokenId); + let basinNew = source.basin; + basinNew!.splice(nftIndex, 1); + source.basin = basinNew; } else { - log.critical("TRANSFER HANDLER - MODE MISSING", []) + log.critical("TRANSFER HANDLER - MODE MISSING", []); } - source.save() + source.save(); } else if (from == zeroAddress) { - log.info("NEW {} COLLECTION MINT! ID: {}", [mode.toUpperCase(), tokenId.toString()]) - let collectionData = CollectionData.load(mode) + log.info("NEW {} COLLECTION MINT! ID: {}", [mode.toUpperCase(), tokenId.toString()]); + let collectionData = CollectionData.load(mode); if (!collectionData) { - collectionData = new CollectionData(mode) - collectionData.minted = new Array<i32>() + collectionData = new CollectionData(mode); + collectionData.minted = new Array<i32>(); } - let mintedData = collectionData.minted - mintedData!.push(tokenId) - collectionData.minted = mintedData - collectionData.save() + let mintedData = collectionData.minted; + mintedData!.push(tokenId); + collectionData.minted = mintedData; + collectionData.save(); } - if (destination) { // If true we have indexed the receiver as an user already, just update the arrays - if (mode === 'genesis') { - let genesisNew = destination.genesis - genesisNew!.push(tokenId) - destination.genesis = genesisNew - } else if (mode === 'winter') { - let winterNew = destination.winter - winterNew!.push(tokenId) - destination.winter = winterNew - } else if (mode === 'barnraise') { - let barnRaiseNew = destination.barnRaise - barnRaiseNew!.push(tokenId) - destination.barnRaise = barnRaiseNew - } else if (mode === 'basin') { - let basinNew = destination.basin - basinNew!.push(tokenId) - destination.basin = basinNew + if (destination) { + // If true we have indexed the receiver as an user already, just update the arrays + if (mode === "genesis") { + let genesisNew = destination.genesis; + genesisNew!.push(tokenId); + destination.genesis = genesisNew; + } else if (mode === "winter") { + let winterNew = destination.winter; + winterNew!.push(tokenId); + destination.winter = winterNew; + } else if (mode === "barnraise") { + let barnRaiseNew = destination.barnRaise; + barnRaiseNew!.push(tokenId); + destination.barnRaise = barnRaiseNew; + } else if (mode === "basin") { + let basinNew = destination.basin; + basinNew!.push(tokenId); + destination.basin = basinNew; } else { - log.critical("TRANSFER HANDLER - MODE MISSING", []) + log.critical("TRANSFER HANDLER - MODE MISSING", []); } - destination.save() - } else if (to !== zeroAddress) { // This is a new user, so initialize the arrays. This check also makes sure we don't index the zero address - destination = new BeaNFTUser(to) - destination.id = to - destination.genesis = new Array<i32>() - destination.winter = new Array<i32>() - destination.barnRaise = new Array<i32>() - destination.basin = new Array<i32>() - if (mode === 'genesis') { - let genesisNew = destination.genesis - genesisNew!.push(tokenId) - destination.genesis = genesisNew - } else if (mode === 'winter') { - let winterNew = destination.winter - winterNew!.push(tokenId) - destination.winter = winterNew - } else if (mode === 'barnraise') { - let barnRaiseNew = destination.barnRaise - barnRaiseNew!.push(tokenId) - destination.barnRaise = barnRaiseNew - } else if (mode === 'basin') { - let basinNew = destination.basin - basinNew!.push(tokenId) - destination.basin = basinNew + destination.save(); + } else if (to !== zeroAddress) { + // This is a new user, so initialize the arrays. This check also makes sure we don't index the zero address + destination = new BeaNFTUser(to); + destination.id = to; + destination.genesis = new Array<i32>(); + destination.winter = new Array<i32>(); + destination.barnRaise = new Array<i32>(); + destination.basin = new Array<i32>(); + if (mode === "genesis") { + let genesisNew = destination.genesis; + genesisNew!.push(tokenId); + destination.genesis = genesisNew; + } else if (mode === "winter") { + let winterNew = destination.winter; + winterNew!.push(tokenId); + destination.winter = winterNew; + } else if (mode === "barnraise") { + let barnRaiseNew = destination.barnRaise; + barnRaiseNew!.push(tokenId); + destination.barnRaise = barnRaiseNew; + } else if (mode === "basin") { + let basinNew = destination.basin; + basinNew!.push(tokenId); + destination.basin = basinNew; } else { - log.critical("TRANSFER HANDLER - MODE MISSING", []) + log.critical("TRANSFER HANDLER - MODE MISSING", []); } - destination.save() + destination.save(); } } -function consecutiveTransferHandler(fromTokenId:i32, toTokenId:i32, from:string, to:string, mode:string): void { - let totalNFTsSent = (toTokenId - fromTokenId) + 1 +function consecutiveTransferHandler(fromTokenId: i32, toTokenId: i32, from: string, to: string, mode: string): void { + let totalNFTsSent = toTokenId - fromTokenId + 1; for (let i = 0; i < totalNFTsSent; i++) { - let sender = BeaNFTUser.load(from) - let receiver = BeaNFTUser.load(to) - let tokenId = fromTokenId + i + let sender = BeaNFTUser.load(from); + let receiver = BeaNFTUser.load(to); + let tokenId = fromTokenId + i; if (sender) { - if (mode === 'barnraise') { - let nftIndex = sender.barnRaise!.indexOf(tokenId) - let barnRaiseNew = sender.barnRaise - barnRaiseNew!.splice(nftIndex, 1) - sender.barnRaise = barnRaiseNew - } else if (mode === 'basin') { - let nftIndex = sender.basin!.indexOf(tokenId) - let basinNew = sender.basin - basinNew!.splice(nftIndex, 1) - sender.basin = basinNew + if (mode === "barnraise") { + let nftIndex = sender.barnRaise!.indexOf(tokenId); + let barnRaiseNew = sender.barnRaise; + barnRaiseNew!.splice(nftIndex, 1); + sender.barnRaise = barnRaiseNew; + } else if (mode === "basin") { + let nftIndex = sender.basin!.indexOf(tokenId); + let basinNew = sender.basin; + basinNew!.splice(nftIndex, 1); + sender.basin = basinNew; } - sender.save() + sender.save(); } else if (from == zeroAddress) { - log.info("NEW {} COLLECTION MINT! ID: {}", [mode.toUpperCase(), tokenId.toString()]) - let collectionData = CollectionData.load(mode) + log.info("NEW {} COLLECTION MINT! ID: {}", [mode.toUpperCase(), tokenId.toString()]); + let collectionData = CollectionData.load(mode); if (!collectionData) { - collectionData = new CollectionData(mode) - collectionData.minted = new Array<i32>() + collectionData = new CollectionData(mode); + collectionData.minted = new Array<i32>(); } - let mintedData = collectionData.minted - mintedData!.push(tokenId) - collectionData.minted = mintedData - collectionData.save() + let mintedData = collectionData.minted; + mintedData!.push(tokenId); + collectionData.minted = mintedData; + collectionData.save(); } if (receiver) { - if (mode === 'barnraise') { - let barnRaiseNew = receiver.barnRaise - barnRaiseNew!.push(tokenId) - receiver.barnRaise = barnRaiseNew - } else if (mode === 'basin') { - let basinNew = receiver.basin - basinNew!.push(tokenId) - receiver.basin = basinNew + if (mode === "barnraise") { + let barnRaiseNew = receiver.barnRaise; + barnRaiseNew!.push(tokenId); + receiver.barnRaise = barnRaiseNew; + } else if (mode === "basin") { + let basinNew = receiver.basin; + basinNew!.push(tokenId); + receiver.basin = basinNew; } - receiver.save() - } - else if (to !== zeroAddress) { - receiver = new BeaNFTUser(to) - receiver.id = to - receiver.genesis = new Array<i32>() - receiver.winter = new Array<i32>() - receiver.barnRaise = new Array<i32>() - receiver.basin = new Array<i32>() - if (mode === 'barnraise') { - let barnRaiseNew = receiver.barnRaise - barnRaiseNew!.push(tokenId) - receiver.barnRaise = barnRaiseNew - } else if (mode === 'basin') { - let basinNew = receiver.basin - basinNew!.push(tokenId) - receiver.basin = basinNew + receiver.save(); + } else if (to !== zeroAddress) { + receiver = new BeaNFTUser(to); + receiver.id = to; + receiver.genesis = new Array<i32>(); + receiver.winter = new Array<i32>(); + receiver.barnRaise = new Array<i32>(); + receiver.basin = new Array<i32>(); + if (mode === "barnraise") { + let barnRaiseNew = receiver.barnRaise; + barnRaiseNew!.push(tokenId); + receiver.barnRaise = barnRaiseNew; + } else if (mode === "basin") { + let basinNew = receiver.basin; + basinNew!.push(tokenId); + receiver.basin = basinNew; } - receiver.save() + receiver.save(); } } } diff --git a/projects/subgraph-beanft/subgraph.yaml b/projects/subgraph-beanft/subgraph.yaml index 9bded0195b..a1486e600c 100644 --- a/projects/subgraph-beanft/subgraph.yaml +++ b/projects/subgraph-beanft/subgraph.yaml @@ -90,4 +90,3 @@ dataSources: - event: Transfer(indexed address,indexed address,indexed uint256) handler: handleTransferBasin file: ./src/mappings.ts - diff --git a/projects/ui/src/components/Analytics/Bean/Crosses.tsx b/projects/ui/src/components/Analytics/Bean/Crosses.tsx index 43b04c3036..49cd7513b9 100644 --- a/projects/ui/src/components/Analytics/Bean/Crosses.tsx +++ b/projects/ui/src/components/Analytics/Bean/Crosses.tsx @@ -17,7 +17,8 @@ const getValue = (season: SnapshotData<SeasonalCrossesQuery>) => season.crosses; const formatValue = (value: number) => `${value}`; const statProps = { title: 'Peg Crosses', - titleTooltip: 'The total number of times Bean has crossed its peg at the beginning of every Season.', + titleTooltip: + 'The total number of times Bean has crossed its peg at the beginning of every Season.', gap: 0.25, sx: { ml: 0 }, }; diff --git a/projects/ui/src/components/Analytics/Bean/DeltaB.tsx b/projects/ui/src/components/Analytics/Bean/DeltaB.tsx index 8105ce0fdf..afb94db6ca 100644 --- a/projects/ui/src/components/Analytics/Bean/DeltaB.tsx +++ b/projects/ui/src/components/Analytics/Bean/DeltaB.tsx @@ -20,7 +20,8 @@ const formatValue = (value: number) => `${value.toLocaleString('en-us', { maximumFractionDigits: 2 })}`; const statProps = { title: 'deltaB', - titleTooltip: 'The liquidity and time weighted average shortage of Beans in liquidity pools on the Oracle Whitelist at the beginning of every Season.', + titleTooltip: + 'The liquidity and time weighted average shortage of Beans in liquidity pools on the Oracle Whitelist at the beginning of every Season.', gap: 0.25, }; diff --git a/projects/ui/src/components/Analytics/Bean/Liquidity.tsx b/projects/ui/src/components/Analytics/Bean/Liquidity.tsx index ba40a81d3c..f348bdf559 100644 --- a/projects/ui/src/components/Analytics/Bean/Liquidity.tsx +++ b/projects/ui/src/components/Analytics/Bean/Liquidity.tsx @@ -18,7 +18,8 @@ const formatValue = (value: number) => `$${value.toLocaleString('en-US', { maximumFractionDigits: 0 })}`; const statProps = { title: 'Liquidity', - titleTooltip: 'The total USD value of tokens in liquidity pools on the Oracle Whitelist at the beginning of every Season. Pre-exploit values include liquidity in pools on the Deposit Whitelist.', + titleTooltip: + 'The total USD value of tokens in liquidity pools on the Oracle Whitelist at the beginning of every Season. Pre-exploit values include liquidity in pools on the Deposit Whitelist.', gap: 0.25, }; const queryConfig = { diff --git a/projects/ui/src/components/Analytics/Bean/LiquiditySupplyRatio.tsx b/projects/ui/src/components/Analytics/Bean/LiquiditySupplyRatio.tsx index 48c141e5f9..ac0f139756 100644 --- a/projects/ui/src/components/Analytics/Bean/LiquiditySupplyRatio.tsx +++ b/projects/ui/src/components/Analytics/Bean/LiquiditySupplyRatio.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { tickFormatPercentage } from '~/components/Analytics/formatters'; import { LineChartProps } from '~/components/Common/Charts/LineChart'; import SeasonPlot, { @@ -11,13 +12,11 @@ import useSeason from '~/hooks/beanstalk/useSeason'; import { FC } from '~/types'; const getValue = (season: LiquiditySupplyRatioQuery['seasons'][number]) => - (season.supplyInPegLP * 100); -const formatValue = (value: number) => - `${value.toFixed(4)}%`; + season.supplyInPegLP * 100; +const formatValue = (value: number) => `${value.toFixed(4)}%`; const statProps = { title: 'Liquidity to Supply Ratio', - titleTooltip: - `The ratio of Beans in liquidity pools on the Oracle Whitelist per Bean, displayed as a percentage, at the beginning of every Season. The Liquidity to Supply Ratio is a useful indicator of Beanstalk's health. Pre-exploit values include liquidity in pools on the Deposit Whitelist.`, + titleTooltip: `The ratio of Beans in liquidity pools on the Oracle Whitelist per Bean, displayed as a percentage, at the beginning of every Season. The Liquidity to Supply Ratio is a useful indicator of Beanstalk's health. Pre-exploit values include liquidity in pools on the Deposit Whitelist.`, gap: 0.25, }; const queryConfig = { diff --git a/projects/ui/src/components/Analytics/Bean/Volume.tsx b/projects/ui/src/components/Analytics/Bean/Volume.tsx index f45d54f508..3f1dcbc727 100644 --- a/projects/ui/src/components/Analytics/Bean/Volume.tsx +++ b/projects/ui/src/components/Analytics/Bean/Volume.tsx @@ -18,7 +18,8 @@ const formatValue = (value: number) => `$${value.toLocaleString('en-US', { maximumFractionDigits: 0 })}`; const statProps = { title: 'Volume', - titleTooltip: 'The total USD volume in liquidity pools on the Oracle Whitelist every Season.', + titleTooltip: + 'The total USD volume in liquidity pools on the Oracle Whitelist every Season.', gap: 0.25, }; const queryConfig = { context: { subgraph: 'bean' } }; diff --git a/projects/ui/src/components/Analytics/Bean/VolumeChart.tsx b/projects/ui/src/components/Analytics/Bean/VolumeChart.tsx index c337e74016..635a441569 100644 --- a/projects/ui/src/components/Analytics/Bean/VolumeChart.tsx +++ b/projects/ui/src/components/Analytics/Bean/VolumeChart.tsx @@ -93,7 +93,9 @@ const VolumeChart: FC<{ width?: number; height: number }> = ({ }` : 0; - const currentDate = currentHoverBar ? currentHoverBar.date.toLocaleDateString() : (new Date()).toLocaleDateString(); + const currentDate = currentHoverBar + ? currentHoverBar.date.toLocaleDateString() + : new Date().toLocaleDateString(); const chartControlsHeight = 75; const chartHeight = height - chartControlsHeight; @@ -146,7 +148,12 @@ const VolumeChart: FC<{ width?: number; height: number }> = ({ seriesData={transformData(queryData?.data[0])} getX={(datum) => datum.date} getY={(datum) => Number(datum.count)} - xTickFormat={(date: Date) => date.toLocaleDateString(undefined, { month: '2-digit', day: '2-digit' })} + xTickFormat={(date: Date) => + date.toLocaleDateString(undefined, { + month: '2-digit', + day: '2-digit', + }) + } yTickFormat={tickFormatUSD} width={width || parent.width} height={chartHeight || parent.height} diff --git a/projects/ui/src/components/Analytics/Bean/index.tsx b/projects/ui/src/components/Analytics/Bean/index.tsx index bd6e509fab..da1f69e5a6 100644 --- a/projects/ui/src/components/Analytics/Bean/index.tsx +++ b/projects/ui/src/components/Analytics/Bean/index.tsx @@ -1,12 +1,12 @@ import { Card, Tab, Tabs } from '@mui/material'; +import React from 'react'; import Crosses from '~/components/Analytics/Bean/Crosses'; import DeltaB from '~/components/Analytics/Bean/DeltaB'; import { FC } from '~/types'; import Liquidity from '~/components/Analytics/Bean/Liquidity'; import MarketCap from '~/components/Analytics/Bean/MarketCap'; import Price from './Price'; -import React from 'react'; import Supply from '~/components/Analytics/Bean/Supply'; import VolumeChart from '~/components/Analytics/Bean/VolumeChart'; import useTabs from '~/hooks/display/useTabs'; @@ -20,7 +20,7 @@ const SLUGS = [ 'supply', 'crosses', 'delta_b', - 'liquiditysupplyratio' + 'liquiditysupplyratio', ]; const BeanAnalytics: FC<{}> = () => { diff --git a/projects/ui/src/components/Analytics/Field/HarvestedPods.tsx b/projects/ui/src/components/Analytics/Field/HarvestedPods.tsx index b839a43a13..4019a9568e 100644 --- a/projects/ui/src/components/Analytics/Field/HarvestedPods.tsx +++ b/projects/ui/src/components/Analytics/Field/HarvestedPods.tsx @@ -20,7 +20,8 @@ const formatValue = (value: number) => `${value.toLocaleString('en-US', { maximumFractionDigits: 0 })}`; const StatProps = { title: 'Pods Harvested', - titleTooltip: 'The total number of Pods Harvested at the beginning of every Season.', + titleTooltip: + 'The total number of Pods Harvested at the beginning of every Season.', gap: 0.5, }; const lineChartProps: Partial<LineChartProps> = { diff --git a/projects/ui/src/components/Analytics/Field/Pods.tsx b/projects/ui/src/components/Analytics/Field/Pods.tsx index b80a19676a..5c4df2e8fa 100644 --- a/projects/ui/src/components/Analytics/Field/Pods.tsx +++ b/projects/ui/src/components/Analytics/Field/Pods.tsx @@ -17,7 +17,8 @@ const formatValue = (value: number) => `${value.toLocaleString('en-US', { maximumFractionDigits: 0 })}`; const statProps = { title: 'Pods', - titleTooltip: 'The total number of Unharvestable Pods at the beginning of every Season.', + titleTooltip: + 'The total number of Unharvestable Pods at the beginning of every Season.', gap: 0.5, }; const lineChartProps: Partial<LineChartProps> = { diff --git a/projects/ui/src/components/Analytics/Field/Sown.tsx b/projects/ui/src/components/Analytics/Field/Sown.tsx index ee8eedc26c..0b6244339e 100644 --- a/projects/ui/src/components/Analytics/Field/Sown.tsx +++ b/projects/ui/src/components/Analytics/Field/Sown.tsx @@ -18,7 +18,8 @@ const formatValue = (value: number) => `${value.toLocaleString('en-US', { maximumFractionDigits: 0 })}`; const statProps = { title: 'Beans Sown', - titleTooltip: 'The total number of Beans Sown at the beginning of every Season.', + titleTooltip: + 'The total number of Beans Sown at the beginning of every Season.', gap: 0.25, sx: { ml: 0 }, }; diff --git a/projects/ui/src/components/Analytics/Field/TotalSowers.tsx b/projects/ui/src/components/Analytics/Field/TotalSowers.tsx index 5102845919..a42b7e3ae1 100644 --- a/projects/ui/src/components/Analytics/Field/TotalSowers.tsx +++ b/projects/ui/src/components/Analytics/Field/TotalSowers.tsx @@ -18,7 +18,8 @@ const getValue = (season: SnapshotData<SeasonalTotalSowersQuery>) => const formatValue = (value: number) => `${value}`; const statProps = { title: 'Total Sowers', - titleTooltip: 'The total number of unique Sowers at the beginning of every Season.', + titleTooltip: + 'The total number of unique Sowers at the beginning of every Season.', gap: 0.25, sx: { ml: 0 }, }; diff --git a/projects/ui/src/components/Balances/SiloBalancesHistory.tsx b/projects/ui/src/components/Balances/SiloBalancesHistory.tsx index 3924cb0c28..d33f60d590 100644 --- a/projects/ui/src/components/Balances/SiloBalancesHistory.tsx +++ b/projects/ui/src/components/Balances/SiloBalancesHistory.tsx @@ -60,14 +60,16 @@ const SiloBalancesHistory: React.FC<{}> = () => { height={300} StatProps={{ title: 'Value Deposited', - titleTooltip: + titleTooltip: ( <> - The historical USD value of your Silo Deposits at the beginning of every Season. <br /> + The historical USD value of your Silo Deposits at the beginning + of every Season. <br /> <Typography variant="bodySmall"> - Note: Unripe assets are valued based on the current Chop Rate. Earned Beans are shown upon Plant. + Note: Unripe assets are valued based on the current Chop Rate. + Earned Beans are shown upon Plant. </Typography> </> - , + ), gap: 0.25, }} timeTabParams={timeTabParams} diff --git a/projects/ui/src/components/Barn/Actions/Buy.tsx b/projects/ui/src/components/Barn/Actions/Buy.tsx index d2f6a0f69b..d9f2ea8c2c 100644 --- a/projects/ui/src/components/Barn/Actions/Buy.tsx +++ b/projects/ui/src/components/Barn/Actions/Buy.tsx @@ -43,7 +43,12 @@ import useFarmerBalances from '~/hooks/farmer/useFarmerBalances'; import usePreferredToken, { PreferredToken, } from '~/hooks/farmer/usePreferredToken'; -import { displayTokenAmount, getTokenIndex, normaliseTV, tokenValueToBN } from '~/util'; +import { + displayTokenAmount, + getTokenIndex, + normaliseTV, + tokenValueToBN, +} from '~/util'; import { useFetchFarmerAllowances } from '~/state/farmer/allowances/updater'; import { FarmerBalances } from '~/state/farmer/balances'; import FertilizerItem from '../FertilizerItem'; @@ -206,7 +211,7 @@ const BuyForm: FC< balanceFrom={values.balanceFrom} params={quoteProviderParams} /> - <ClaimBeanDrawerToggle actionText='Buy Fert with'/> + <ClaimBeanDrawerToggle actionText="Buy Fert with" /> {/* Outputs */} {fert?.gt(0) ? ( <> @@ -239,21 +244,23 @@ const BuyForm: FC< </> )}{' '} {values.claimableBeans.amount?.gt(0) && ( - <> - {values.tokens[0].amount?.gt(0) && (<>+ </>)} + <> + {values.tokens[0].amount?.gt(0) && <>+ </>} {displayTokenAmount( - values.claimableBeans.amount, - sdk.tokens.BEAN, + values.claimableBeans.amount, + sdk.tokens.BEAN, { showName: false, showSymbol: true } )} </> )}{' '} {values.tokens[0].token.symbol !== 'WETH' && ( - <> - →{' '} + <> + →{' '} {displayTokenAmount( - values.tokens[0].amountOut?.plus(values.claimableBeans.amountOut || BigNumber(0)) || BigNumber(0), - sdk.tokens.WETH, + values.tokens[0].amountOut?.plus( + values.claimableBeans.amountOut || BigNumber(0) + ) || BigNumber(0), + sdk.tokens.WETH, { showName: false, showSymbol: true } )} </> diff --git a/projects/ui/src/components/Common/Balances/TokenRow.tsx b/projects/ui/src/components/Common/Balances/TokenRow.tsx index f096e098af..0214cb0e0c 100644 --- a/projects/ui/src/components/Common/Balances/TokenRow.tsx +++ b/projects/ui/src/components/Common/Balances/TokenRow.tsx @@ -93,7 +93,10 @@ const TokenRow: FC<{ </Tooltip> )} </Row> - <Tooltip title={amountTooltip || ''} placement={amountTooltipLocation || "top-end"}> + <Tooltip + title={amountTooltip || ''} + placement={amountTooltipLocation || 'top-end'} + > <div> <Row gap={0.5}> {token && <TokenIcon token={token} />} diff --git a/projects/ui/src/components/Common/Form/FormTxn/AddPlantTxnToggle.tsx b/projects/ui/src/components/Common/Form/FormTxn/AddPlantTxnToggle.tsx index dc85371c4a..d48b24b18d 100644 --- a/projects/ui/src/components/Common/Form/FormTxn/AddPlantTxnToggle.tsx +++ b/projects/ui/src/components/Common/Form/FormTxn/AddPlantTxnToggle.tsx @@ -127,7 +127,9 @@ const AddPlantTxnToggle: React.FC<{ {`${actionText || 'Use'} Earned Beans`} </Typography> <Typography variant="body1" color="text.tertiary"> - {`Toggle to ${actionText || 'Claim'} Earned Beans in this transaction.`} + {`Toggle to ${ + actionText || 'Claim' + } Earned Beans in this transaction.`} </Typography> </Stack> </Row> diff --git a/projects/ui/src/components/Common/Form/FormTxn/ClaimBeanDrawerToggle.tsx b/projects/ui/src/components/Common/Form/FormTxn/ClaimBeanDrawerToggle.tsx index 4e4d8eb75e..f4ad421a1c 100644 --- a/projects/ui/src/components/Common/Form/FormTxn/ClaimBeanDrawerToggle.tsx +++ b/projects/ui/src/components/Common/Form/FormTxn/ClaimBeanDrawerToggle.tsx @@ -35,7 +35,9 @@ const actionsToIconMap = { }, }; -const ClaimBeanDrawerToggle: React.FC<{ actionText?: string }> = ({ actionText }) => { +const ClaimBeanDrawerToggle: React.FC<{ actionText?: string }> = ({ + actionText, +}) => { /// Formik const { values } = useFormikContext<FormTxnsFormState>(); diff --git a/projects/ui/src/components/Common/Form/TokenOutput.tsx b/projects/ui/src/components/Common/Form/TokenOutput.tsx index 234431a7e5..9c91a5405a 100644 --- a/projects/ui/src/components/Common/Form/TokenOutput.tsx +++ b/projects/ui/src/components/Common/Form/TokenOutput.tsx @@ -62,7 +62,7 @@ const formatBN = (value?: BigNumber, _decimals?: number, suffix?: string) => { const prefix = value ? (value.gt(0) ? '+' : value.lt(0) ? '-' : '') : ''; if (value.abs().lt(0.01) && value.abs().gt(0)) { return `${prefix} <0.01${suffix || ''}`; - }; + } return `${prefix} ${displayFullBN(value.abs(), decimals, decimals)}${ suffix || '' }`; diff --git a/projects/ui/src/components/Common/Form/TxnPreview.tsx b/projects/ui/src/components/Common/Form/TxnPreview.tsx index 05789bd309..6bbef47364 100644 --- a/projects/ui/src/components/Common/Form/TxnPreview.tsx +++ b/projects/ui/src/components/Common/Form/TxnPreview.tsx @@ -517,7 +517,7 @@ const TxnPreview: FC<{ return null; })} {customOrder - ? actions.map((action, index) => + ? actions.map((action, index) => action ? ( <TxnStep key={index} diff --git a/projects/ui/src/components/Field/Actions/Sow.tsx b/projects/ui/src/components/Field/Actions/Sow.tsx index a1cf600c58..d2d1f379f1 100644 --- a/projects/ui/src/components/Field/Actions/Sow.tsx +++ b/projects/ui/src/components/Field/Actions/Sow.tsx @@ -268,7 +268,7 @@ const SowForm: FC< balanceFrom={values.balanceFrom} disableTokenSelect={!hasSoil || !maxAmountIn} /> - {hasSoil && <ClaimBeanDrawerToggle actionText='Sow'/>} + {hasSoil && <ClaimBeanDrawerToggle actionText="Sow" />} {!hasSoil ? ( <Box> <WarningAlert sx={{ color: 'black' }}> diff --git a/projects/ui/src/components/Forecast/LiquidityByState.tsx b/projects/ui/src/components/Forecast/LiquidityByState.tsx index f5e2c3326a..94ae42e4d8 100644 --- a/projects/ui/src/components/Forecast/LiquidityByState.tsx +++ b/projects/ui/src/components/Forecast/LiquidityByState.tsx @@ -37,7 +37,7 @@ const LiquidityByState: FC<CardProps> = ({ sx }) => { token: STALK, amount: beanstalkSilo.stalk.total, }, - /* { + /* { title: 'Seeds', tooltip: 'This is the total Seed supply. Each Seed yields 1/10000 Grown Stalk each Season.', diff --git a/projects/ui/src/components/Forecast/LiquidityOverTime.tsx b/projects/ui/src/components/Forecast/LiquidityOverTime.tsx index 4bca8f8415..d79216ce9e 100644 --- a/projects/ui/src/components/Forecast/LiquidityOverTime.tsx +++ b/projects/ui/src/components/Forecast/LiquidityOverTime.tsx @@ -22,7 +22,8 @@ const formatValue = (value: number) => ( ); const StatProps = { title: 'Liquidity', - titleTooltip: 'The total USD value of tokens in liquidity pools on the Oracle Whitelist at the beginning of every Season. Pre-exploit values include liquidity in pools on the Deposit Whitelist.', + titleTooltip: + 'The total USD value of tokens in liquidity pools on the Oracle Whitelist at the beginning of every Season. Pre-exploit values include liquidity in pools on the Deposit Whitelist.', gap: 0.25, color: 'primary', sx: { ml: 0 }, diff --git a/projects/ui/src/components/NFT/NFTGrid.tsx b/projects/ui/src/components/NFT/NFTGrid.tsx index 6fb70eac40..92e3caaf3d 100644 --- a/projects/ui/src/components/NFT/NFTGrid.tsx +++ b/projects/ui/src/components/NFT/NFTGrid.tsx @@ -1,5 +1,13 @@ import React from 'react'; -import { Box, Card, CircularProgress, Grid, Stack, Typography, Link } from '@mui/material'; +import { + Box, + Card, + CircularProgress, + Grid, + Stack, + Typography, + Link, +} from '@mui/material'; import { Nft } from '~/util/BeaNFTs'; import NFTDetails from './NFTDetails'; import { BeanstalkPalette } from '../App/muiTheme'; @@ -11,7 +19,11 @@ export interface NFTGridProps { handleDialogOpen: any; } -const NFTGrid: FC<NFTGridProps> = ({ nfts, collectionAddress, handleDialogOpen }) => { +const NFTGrid: FC<NFTGridProps> = ({ + nfts, + collectionAddress, + handleDialogOpen, +}) => { if (nfts === null) { return ( <Stack @@ -25,8 +37,6 @@ const NFTGrid: FC<NFTGridProps> = ({ nfts, collectionAddress, handleDialogOpen } ); } - - return ( <> {nfts.length === 0 ? ( @@ -40,7 +50,11 @@ const NFTGrid: FC<NFTGridProps> = ({ nfts, collectionAddress, handleDialogOpen } <Typography variant="body1" color="gray"> You don't have any NFTs from this collection! </Typography> - <Link href={`https://opensea.io/assets/ethereum/${collectionAddress}`} variant="body1" color="primary"> + <Link + href={`https://opensea.io/assets/ethereum/${collectionAddress}`} + variant="body1" + color="primary" + > Explore this collection on Opensea → </Link> </Box> diff --git a/projects/ui/src/components/Nav/Buttons/AboutButton.tsx b/projects/ui/src/components/Nav/Buttons/AboutButton.tsx index 49599d2075..cc13cd70b0 100644 --- a/projects/ui/src/components/Nav/Buttons/AboutButton.tsx +++ b/projects/ui/src/components/Nav/Buttons/AboutButton.tsx @@ -85,9 +85,7 @@ const AboutButton: FC<ButtonProps> = ({ sx }) => { > <Row spacing={1}> <ListItemText> - <Typography variant="h4"> - Contracts - </Typography> + <Typography variant="h4">Contracts</Typography> </ListItemText> <Typography variant="body2" color="white"> <ArrowForwardIcon diff --git a/projects/ui/src/components/Silo/Actions/Convert.tsx b/projects/ui/src/components/Silo/Actions/Convert.tsx index c532a0bc3f..ba5f45ec1e 100644 --- a/projects/ui/src/components/Silo/Actions/Convert.tsx +++ b/projects/ui/src/components/Silo/Actions/Convert.tsx @@ -326,7 +326,10 @@ const ConvertForm: FC< params={quoteHandlerParams} /> {!canConvert && tokenOut && maxAmountIn ? null : ( - <AddPlantTxnToggle plantAndDoX={plantAndDoX.plantAction} actionText='Convert' /> + <AddPlantTxnToggle + plantAndDoX={plantAndDoX.plantAction} + actionText="Convert" + /> )} {/* User Input: destination token */} diff --git a/projects/ui/src/components/Silo/Actions/Deposit.tsx b/projects/ui/src/components/Silo/Actions/Deposit.tsx index 9f64e8b3d0..fc76047e13 100644 --- a/projects/ui/src/components/Silo/Actions/Deposit.tsx +++ b/projects/ui/src/components/Silo/Actions/Deposit.tsx @@ -231,7 +231,9 @@ const DepositForm: FC< /> ); })} - {migrationNeeded === true ? null : <ClaimBeanDrawerToggle actionText="Deposit" />} + {migrationNeeded === true ? null : ( + <ClaimBeanDrawerToggle actionText="Deposit" /> + )} {isReady ? ( <> <TxnSeparator /> @@ -543,7 +545,7 @@ const DepositPropProvider: FC<{ amountIn, values.settings.slippage, target.equals(BEAN_ETH_WELL_LP) ? 1.2 : undefined, - tokenIn.symbol !== "BEANETH" && target.equals(BEAN_ETH_WELL_LP) + tokenIn.symbol !== 'BEANETH' && target.equals(BEAN_ETH_WELL_LP) ); const txn = await execute(); diff --git a/projects/ui/src/components/Silo/Actions/Deposits.tsx b/projects/ui/src/components/Silo/Actions/Deposits.tsx index bb10f956b0..57cc79c1b4 100644 --- a/projects/ui/src/components/Silo/Actions/Deposits.tsx +++ b/projects/ui/src/components/Silo/Actions/Deposits.tsx @@ -43,7 +43,9 @@ const Deposits: FC< () => siloBalance?.deposited.crates.map((deposit) => ({ id: deposit.stem?.toString(), - mowableStalk: deposit.bdv?.multipliedBy(deltaStem).shiftedBy(decimalShift), + mowableStalk: deposit.bdv + ?.multipliedBy(deltaStem) + .shiftedBy(decimalShift), ...deposit, })) || [], [siloBalance?.deposited.crates, deltaStem, decimalShift] @@ -73,7 +75,10 @@ const Deposits: FC< {displayFullBN(params.row.bdv, token.displayDecimals)} </StatHorizontal> <StatHorizontal label="Current BDV"> - {displayFullBN(params.row.amount.multipliedBy(getBDV(token)), token.displayDecimals)} + {displayFullBN( + params.row.amount.multipliedBy(getBDV(token)), + token.displayDecimals + )} </StatHorizontal> <StatHorizontal label="Current Value"> <Fiat amount={params.row.amount} token={token} /> @@ -142,28 +147,32 @@ const Deposits: FC< headerAlign: 'right', valueFormatter: (params) => displayBN(params.value), renderCell: (params) => ( - <Tooltip - placement="bottom" - title={ - <Stack gap={0.5}> - <StatHorizontal label="Mown Grown Stalk"> - {displayFullBN(params.row.stalk.grown.minus(params.row.mowableStalk), 2, 2)} - </StatHorizontal> - <StatHorizontal label="Mowable Grown Stalk"> - {displayFullBN(params.row.mowableStalk, 2, 2)} - </StatHorizontal> - </Stack> - } - > - <span> - <Typography display={{ xs: 'none', md: 'block' }}> - {displayFullBN(params.row.stalk.grown, 2, 2)} - </Typography> - <Typography display={{ xs: 'block', md: 'none' }}> - {displayFullBN(params.row.stalk.grown, 2, 2)} - </Typography> - </span> - </Tooltip> + <Tooltip + placement="bottom" + title={ + <Stack gap={0.5}> + <StatHorizontal label="Mown Grown Stalk"> + {displayFullBN( + params.row.stalk.grown.minus(params.row.mowableStalk), + 2, + 2 + )} + </StatHorizontal> + <StatHorizontal label="Mowable Grown Stalk"> + {displayFullBN(params.row.mowableStalk, 2, 2)} + </StatHorizontal> + </Stack> + } + > + <span> + <Typography display={{ xs: 'none', md: 'block' }}> + {displayFullBN(params.row.stalk.grown, 2, 2)} + </Typography> + <Typography display={{ xs: 'block', md: 'none' }}> + {displayFullBN(params.row.stalk.grown, 2, 2)} + </Typography> + </span> + </Tooltip> ), sortable: false, }, diff --git a/projects/ui/src/components/Silo/Actions/Transfer.tsx b/projects/ui/src/components/Silo/Actions/Transfer.tsx index 75338bad4f..4c247cfc8a 100644 --- a/projects/ui/src/components/Silo/Actions/Transfer.tsx +++ b/projects/ui/src/components/Silo/Actions/Transfer.tsx @@ -152,11 +152,17 @@ const TransferForm: FC< <TokenOutput> <TokenOutput.Row token={whitelistedToken} - amount={(isUsingPlant ? withdrawResult.amount.add(earnedBeans) : withdrawResult.amount).mul(-1)} + amount={(isUsingPlant + ? withdrawResult.amount.add(earnedBeans) + : withdrawResult.amount + ).mul(-1)} /> <TokenOutput.Row token={STALK} - amount={(isUsingPlant ? withdrawResult.stalk.add(earnedStalk) : withdrawResult.stalk).mul(-1)} + amount={(isUsingPlant + ? withdrawResult.stalk.add(earnedStalk) + : withdrawResult.stalk + ).mul(-1)} amountTooltip={ <> <div> @@ -177,7 +183,13 @@ const TransferForm: FC< </> } /> - <TokenOutput.Row token={SEEDS} amount={(isUsingPlant ? withdrawResult.seeds.add(earnedSeeds) : withdrawResult.seeds).mul(-1)} /> + <TokenOutput.Row + token={SEEDS} + amount={(isUsingPlant + ? withdrawResult.seeds.add(earnedSeeds) + : withdrawResult.seeds + ).mul(-1)} + /> </TokenOutput> ); }; @@ -194,86 +206,108 @@ const TransferForm: FC< balanceLabel="Deposited Balance" InputProps={InputProps} /> - <AddPlantTxnToggle plantAndDoX={plantAndDoX} actionText='Transfer' /> + <AddPlantTxnToggle plantAndDoX={plantAndDoX} actionText="Transfer" /> {depositedBalance?.gt(0) && ( <> <FieldWrapper label="Transfer to"> <AddressInputField name="to" /> </FieldWrapper> - {values.to !== '' && withdrawResult?.amount.add(earnedBeans).abs().gt(0) && ( - <> - <TxnSeparator /> - <TokenOutputs /> - {withdrawResult?.amount.abs().gt(0) && - <WarningAlert> - More recent Deposits are Transferred first. - </WarningAlert> - } - <AdditionalTxnsAccordion filter={disabledActions} /> - <Box> - <TxnAccordion> - <TxnPreview - actions={[ - { - type: ActionType.TRANSFER, - amount: withdrawResult - ? toBN((isUsingPlant ? withdrawResult.amount.add(earnedBeans) : withdrawResult.amount).abs()) - : ZERO_BN, - token: getNewToOldToken(whitelistedToken), - stalk: withdrawResult - ? toBN((isUsingPlant ? withdrawResult.stalk.add(earnedStalk) : withdrawResult.stalk).abs()) - : ZERO_BN, - seeds: withdrawResult - ? toBN((isUsingPlant ? withdrawResult.seeds.add(earnedSeeds) : withdrawResult.seeds).abs()) - : ZERO_BN, - to: values.to, - }, - withdrawResult?.amount.abs().gt(0) - ? { - type: ActionType.BASE, - message: ( - <> - The following Deposits will be used: - <br /> - <ul - css={{ - paddingLeft: '25px', - marginTop: '10px', - marginBottom: 0, - fontSize: FontSize.sm, - }} - > - {isUsingPlant && - <li key="earnedBeanCrate"> - {`${displayTokenAmount(earnedBeans, sdk.tokens.BEAN, { showName: false })} Earned Beans`} - </li> - } - {withdrawResult.crates.map((crate, index) => ( - <li key={index}> - {displayTokenAmount( - crate.amount, - whitelistedToken - )}{' '} - from Deposits at Stem{' '} - {crate.stem.toString()} - </li> - ))} - </ul> - </> - ), - } - : undefined, - { - type: ActionType.END_TOKEN, - token: getNewToOldToken(whitelistedToken), - }, - ]} - {...txnActions} - /> - </TxnAccordion> - </Box> - </> - )} + {values.to !== '' && + withdrawResult?.amount.add(earnedBeans).abs().gt(0) && ( + <> + <TxnSeparator /> + <TokenOutputs /> + {withdrawResult?.amount.abs().gt(0) && ( + <WarningAlert> + More recent Deposits are Transferred first. + </WarningAlert> + )} + <AdditionalTxnsAccordion filter={disabledActions} /> + <Box> + <TxnAccordion> + <TxnPreview + actions={[ + { + type: ActionType.TRANSFER, + amount: withdrawResult + ? toBN( + (isUsingPlant + ? withdrawResult.amount.add(earnedBeans) + : withdrawResult.amount + ).abs() + ) + : ZERO_BN, + token: getNewToOldToken(whitelistedToken), + stalk: withdrawResult + ? toBN( + (isUsingPlant + ? withdrawResult.stalk.add(earnedStalk) + : withdrawResult.stalk + ).abs() + ) + : ZERO_BN, + seeds: withdrawResult + ? toBN( + (isUsingPlant + ? withdrawResult.seeds.add(earnedSeeds) + : withdrawResult.seeds + ).abs() + ) + : ZERO_BN, + to: values.to, + }, + withdrawResult?.amount.abs().gt(0) + ? { + type: ActionType.BASE, + message: ( + <> + The following Deposits will be used: + <br /> + <ul + css={{ + paddingLeft: '25px', + marginTop: '10px', + marginBottom: 0, + fontSize: FontSize.sm, + }} + > + {isUsingPlant && ( + <li key="earnedBeanCrate"> + {`${displayTokenAmount( + earnedBeans, + sdk.tokens.BEAN, + { showName: false } + )} Earned Beans`} + </li> + )} + {withdrawResult.crates.map( + (crate, index) => ( + <li key={index}> + {displayTokenAmount( + crate.amount, + whitelistedToken + )}{' '} + from Deposits at Stem{' '} + {crate.stem.toString()} + </li> + ) + )} + </ul> + </> + ), + } + : undefined, + { + type: ActionType.END_TOKEN, + token: getNewToOldToken(whitelistedToken), + }, + ]} + {...txnActions} + /> + </TxnAccordion> + </Box> + </> + )} </> )} <SmartSubmitButton diff --git a/projects/ui/src/components/Silo/Actions/Withdraw.tsx b/projects/ui/src/components/Silo/Actions/Withdraw.tsx index b431269d47..05714a62df 100644 --- a/projects/ui/src/components/Silo/Actions/Withdraw.tsx +++ b/projects/ui/src/components/Silo/Actions/Withdraw.tsx @@ -104,7 +104,10 @@ const WithdrawForm: FC< // FIXME: Temporarily disabled Withdraws of Bean:ETH LP in Bean/WETH, needs routing code () => [ whitelistedToken, - ...(((whitelistedToken.isLP && whitelistedToken !== sdk.tokens.BEAN_ETH_WELL_LP) && pool?.tokens) || []), + ...((whitelistedToken.isLP && + whitelistedToken !== sdk.tokens.BEAN_ETH_WELL_LP && + pool?.tokens) || + []), ], [pool, sdk.tokens, whitelistedToken] ); @@ -159,7 +162,7 @@ const WithdrawForm: FC< const { setDestination } = useFormTxnContext(); useEffect(() => { setDestination(values.destination); - }, [values.destination, setDestination]) + }, [values.destination, setDestination]); const [isTokenSelectVisible, showTokenSelect, hideTokenSelect] = useToggle(); @@ -279,7 +282,7 @@ const WithdrawForm: FC< /> </> </Stack> - <AddPlantTxnToggle plantAndDoX={plantAndDoX} actionText='Withdraw'/> + <AddPlantTxnToggle plantAndDoX={plantAndDoX} actionText="Withdraw" /> {isReady ? ( <Stack direction="column" gap={1}> <TxnSeparator /> @@ -348,7 +351,9 @@ const WithdrawForm: FC< { type: ActionType.IN_TRANSIT, amount: toBN(withdrawResult.amount), - token: getNewToOldToken(values.tokenOut || whitelistedToken), + token: getNewToOldToken( + values.tokenOut || whitelistedToken + ), destination: values.destination || FarmToMode.EXTERNAL, withdrawSeasons, }, diff --git a/projects/ui/src/components/Silo/Overview.tsx b/projects/ui/src/components/Silo/Overview.tsx index 40baccf506..7d3cd20f8b 100644 --- a/projects/ui/src/components/Silo/Overview.tsx +++ b/projects/ui/src/components/Silo/Overview.tsx @@ -7,11 +7,7 @@ import { AppState } from '~/state'; import useTabs from '~/hooks/display/useTabs'; import TokenIcon from '~/components/Common/TokenIcon'; import { SEEDS, STALK } from '~/constants/tokens'; -import { - displayPercentage, - displayStalk, - displayUSD, -} from '~/util'; +import { displayPercentage, displayStalk, displayUSD } from '~/util'; import { ChipLabel, StyledTab } from '~/components/Common/Tabs'; import { ZERO_BN } from '~/constants'; import Row from '~/components/Common/Row'; @@ -78,7 +74,10 @@ const Overview: FC<{ const migrationNeeded = useMigrationNeeded(); const siloBalance = useFarmerSiloBalances(); // - const [tab, handleChange] = useTabs(migrationNeeded ? SLUGS : altSLUGS, 'view'); + const [tab, handleChange] = useTabs( + migrationNeeded ? SLUGS : altSLUGS, + 'view' + ); // const ownership = @@ -86,13 +85,15 @@ const Overview: FC<{ ? farmerSilo.stalk.active.div(beanstalkSilo.stalk.total) : ZERO_BN; - const deposits = Object.values(siloBalance).map(token => token.deposited.crates).flat(Infinity) + const deposits = Object.values(siloBalance) + .map((token) => token.deposited.crates) + .flat(Infinity); let totalStalkGrown = farmerSilo.stalk.grown; deposits.forEach((deposit: any) => { - totalStalkGrown = totalStalkGrown.plus(deposit.stalk.grown) - }) + totalStalkGrown = totalStalkGrown.plus(deposit.stalk.grown); + }); const stalkStats = useCallback( (s: BigNumber, v: BigNumber[], d: string) => ( @@ -118,9 +119,7 @@ const Overview: FC<{ <Stat title="Total Stalk Grown" titleTooltip="The total number of Mown and Mowable Grown Stalk your Deposits have accrued." - amount={displayStalk( - totalStalkGrown - )} + amount={displayStalk(totalStalkGrown)} color="text.primary" gap={0.25} sx={{ minWidth: 120, ml: 0 }} @@ -177,7 +176,9 @@ const Overview: FC<{ <MigrateTab /> </Box> )} - <Box sx={{ display: tab === (migrationNeeded ? 1 : 0) ? 'block' : 'none' }}> + <Box + sx={{ display: tab === (migrationNeeded ? 1 : 0) ? 'block' : 'none' }} + > <OverviewPlot label="Silo Deposits" account={account} @@ -199,7 +200,9 @@ const Overview: FC<{ empty={breakdown.states.deposited.value.eq(0)} /> </Box> - <Box sx={{ display: tab === (migrationNeeded ? 2 : 1) ? 'block' : 'none' }}> + <Box + sx={{ display: tab === (migrationNeeded ? 2 : 1) ? 'block' : 'none' }} + > <OverviewPlot label="Stalk Ownership" account={account} @@ -229,7 +232,9 @@ const Overview: FC<{ empty={farmerSilo.stalk.total.lte(0)} /> </Box> - <Box sx={{ display: tab === (migrationNeeded ? 3 : 2) ? 'block' : 'none' }}> + <Box + sx={{ display: tab === (migrationNeeded ? 3 : 2) ? 'block' : 'none' }} + > <OverviewPlot label="Seeds Ownership" account={account} diff --git a/projects/ui/src/components/Silo/SiloCarousel.tsx b/projects/ui/src/components/Silo/SiloCarousel.tsx index 823fe7445b..48d039eca8 100644 --- a/projects/ui/src/components/Silo/SiloCarousel.tsx +++ b/projects/ui/src/components/Silo/SiloCarousel.tsx @@ -61,7 +61,9 @@ const useCardContentWithToken = (token: ERC20Token) => [ imageSrc: depositCardContentByToken[token.address]?.img || depositBeanImg, }, { - title: `Receive Stalk ${!token.isUnripe ? 'and Seeds' : ''} for your Deposit`, + title: `Receive Stalk ${ + !token.isUnripe ? 'and Seeds' : '' + } for your Deposit`, texts: [ 'Stalk entitles holders to participate in Beanstalk governance and earn a portion of Bean mints.', ], diff --git a/projects/ui/src/components/Silo/Whitelist.tsx b/projects/ui/src/components/Silo/Whitelist.tsx index e63de55420..b335d0e8ed 100644 --- a/projects/ui/src/components/Silo/Whitelist.tsx +++ b/projects/ui/src/components/Silo/Whitelist.tsx @@ -500,10 +500,11 @@ const Whitelist: FC<{ {/* If this is the entry for Bean deposits, * display Earned Beans and Deposited Beans separately. * Internally they are both considered "Deposited". */} - <Tooltip - placement="right" - title={ - token.equals(Bean) && farmerSilo.beans.earned.gt(0) ? ( + <Tooltip + placement="right" + title={ + token.equals(Bean) && + farmerSilo.beans.earned.gt(0) ? ( <> {displayFullBN( deposited?.amount || ZERO_BN, @@ -538,34 +539,47 @@ const Whitelist: FC<{ BEAN <br /> </> - ) : !token.equals(Bean) && deposited?.amount.gt(0) && + ) : ( + !token.equals(Bean) && + deposited?.amount.gt(0) && ( <Stack gap={0.5}> <StatHorizontal label="Current BDV:"> - {displayFullBN(deposited?.amount.multipliedBy(getBDV(token)) || ZERO_BN, token.displayDecimals)} + {displayFullBN( + deposited?.amount.multipliedBy( + getBDV(token) + ) || ZERO_BN, + token.displayDecimals + )} </StatHorizontal> <StatHorizontal label="Recorded BDV:"> - {displayFullBN(deposited?.bdv || ZERO_BN,token.displayDecimals)} + {displayFullBN( + deposited?.bdv || ZERO_BN, + token.displayDecimals + )} </StatHorizontal> </Stack> - } - > - <span> - {displayFullBN( - deposited?.amount || ZERO_BN, - token.displayDecimals - )} - {token.equals(Bean) && farmerSilo.beans.earned.gt(0) ? ( - <Typography component="span" color="primary.main"> - {' + '} - {displayFullBN( - farmerSilo.beans.earned, - token.displayDecimals - )} - </Typography> - ) : null} -  {token.symbol} - </span> - </Tooltip> + ) + ) + } + > + <span> + {displayFullBN( + deposited?.amount || ZERO_BN, + token.displayDecimals + )} + {token.equals(Bean) && + farmerSilo.beans.earned.gt(0) ? ( + <Typography component="span" color="primary.main"> + {' + '} + {displayFullBN( + farmerSilo.beans.earned, + token.displayDecimals + )} + </Typography> + ) : null} +  {token.symbol} + </span> + </Tooltip> </Typography> </Grid> diff --git a/projects/ui/src/graph/graphql.schema.json b/projects/ui/src/graph/graphql.schema.json index d6ec9eb16e..24322c92ce 100644 --- a/projects/ui/src/graph/graphql.schema.json +++ b/projects/ui/src/graph/graphql.schema.json @@ -157278,9 +157278,7 @@ "name": "derivedFrom", "description": "creates a virtual field on the entity that may be queried but cannot be set manually through the mappings API.", "isRepeatable": false, - "locations": [ - "FIELD_DEFINITION" - ], + "locations": ["FIELD_DEFINITION"], "args": [ { "name": "field", @@ -157304,20 +157302,14 @@ "name": "entity", "description": "Marks the GraphQL type as indexable entity. Each type that should be an entity is required to be annotated with this directive.", "isRepeatable": false, - "locations": [ - "OBJECT" - ], + "locations": ["OBJECT"], "args": [] }, { "name": "include", "description": "Directs the executor to include this field or fragment only when the `if` argument is true.", "isRepeatable": false, - "locations": [ - "FIELD", - "FRAGMENT_SPREAD", - "INLINE_FRAGMENT" - ], + "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"], "args": [ { "name": "if", @@ -157341,11 +157333,7 @@ "name": "skip", "description": "Directs the executor to skip this field or fragment when the `if` argument is true.", "isRepeatable": false, - "locations": [ - "FIELD", - "FRAGMENT_SPREAD", - "INLINE_FRAGMENT" - ], + "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"], "args": [ { "name": "if", @@ -157369,9 +157357,7 @@ "name": "specifiedBy", "description": "Exposes a URL that specifies the behavior of this scalar.", "isRepeatable": false, - "locations": [ - "SCALAR" - ], + "locations": ["SCALAR"], "args": [ { "name": "url", @@ -157395,9 +157381,7 @@ "name": "subgraphId", "description": "Defined a Subgraph ID for an object type", "isRepeatable": false, - "locations": [ - "OBJECT" - ], + "locations": ["OBJECT"], "args": [ { "name": "id", @@ -157419,4 +157403,4 @@ } ] } -} \ No newline at end of file +} diff --git a/projects/ui/src/hooks/beanstalk/useBeanstalkBalancesBreakdown.tsx b/projects/ui/src/hooks/beanstalk/useBeanstalkBalancesBreakdown.tsx index 25613f981a..d1c434e139 100644 --- a/projects/ui/src/hooks/beanstalk/useBeanstalkBalancesBreakdown.tsx +++ b/projects/ui/src/hooks/beanstalk/useBeanstalkBalancesBreakdown.tsx @@ -33,7 +33,10 @@ export const STATE_CONFIG = { withdrawn: [ 'Claimable', colors.chart.yellowLight, - (name: string) => `Legacy Claimable ${name === 'Beans' ? 'Bean' : name} Withdrawals from before Silo V3.`, + (name: string) => + `Legacy Claimable ${ + name === 'Beans' ? 'Bean' : name + } Withdrawals from before Silo V3.`, ], farmable: [ 'Farm & Circulating', @@ -206,11 +209,11 @@ export default function useBeanstalkSiloBreakdown() { // Ripe Pooled = BEAN:ETH_RESERVES * (Ripe BEAN:ETH / BEAN:ETH Token Supply) ripePooled = new BigNumber(totalPooled).multipliedBy( - new BigNumber( - unripeTokenState[ripeToUnripe[BeanETH.address].address] - ?.underlying || 0 - ).div(new BigNumber(poolState[BeanETH.address]?.supply || 0)) - ); + new BigNumber( + unripeTokenState[ripeToUnripe[BeanETH.address].address] + ?.underlying || 0 + ).div(new BigNumber(poolState[BeanETH.address]?.supply || 0)) + ); // pooled = new BigNumber(totalPooled).minus(ripePooled); farmable = beanSupply diff --git a/projects/ui/src/hooks/beanstalk/useProposalBlockData.ts b/projects/ui/src/hooks/beanstalk/useProposalBlockData.ts index 21495e3a8a..50b294c594 100644 --- a/projects/ui/src/hooks/beanstalk/useProposalBlockData.ts +++ b/projects/ui/src/hooks/beanstalk/useProposalBlockData.ts @@ -89,9 +89,11 @@ export default function useProposalBlockData( const score = proposal.space.id === GovSpace.BeanSprout ? new BigNumber(proposal.scores_total || ZERO_BN) - : proposal.title.includes("BFCP-B-") && proposal.choices && proposal.choices[1].includes("Remove") - ? new BigNumber(proposal.scores[1]) - : new BigNumber(proposal.scores[0] || ZERO_BN); + : proposal.title.includes('BFCP-B-') && + proposal.choices && + proposal.choices[1].includes('Remove') + ? new BigNumber(proposal.scores[1]) + : new BigNumber(proposal.scores[0] || ZERO_BN); /// Voting power const { data: vpData } = useProposalVotingPowerQuery({ diff --git a/projects/ui/src/hooks/farmer/form-txn/useFarmerFormTxnActions.ts b/projects/ui/src/hooks/farmer/form-txn/useFarmerFormTxnActions.ts index f7118f8c42..e2f5f2b2f4 100644 --- a/projects/ui/src/hooks/farmer/form-txn/useFarmerFormTxnActions.ts +++ b/projects/ui/src/hooks/farmer/form-txn/useFarmerFormTxnActions.ts @@ -86,12 +86,18 @@ export default function useFarmerFormTxnsActions(options?: { }; return transfer; - } - - if ((transferTo === FarmToMode.INTERNAL || transferTo === undefined) && transferAmount?.gt(0)) { + } + + if ( + (transferTo === FarmToMode.INTERNAL || transferTo === undefined) && + transferAmount?.gt(0) + ) { const transfer = { type: ActionType.BASE, - message: `Return ${displayTokenAmount(transferAmount, sdk.tokens.BEAN)} to your Farm Balance.`, + message: `Return ${displayTokenAmount( + transferAmount, + sdk.tokens.BEAN + )} to your Farm Balance.`, }; return transfer; diff --git a/projects/ui/src/hooks/farmer/form-txn/useFarmerFormTxnsSummary.ts b/projects/ui/src/hooks/farmer/form-txn/useFarmerFormTxnsSummary.ts index 46f824504d..1361f5bbb7 100644 --- a/projects/ui/src/hooks/farmer/form-txn/useFarmerFormTxnsSummary.ts +++ b/projects/ui/src/hooks/farmer/form-txn/useFarmerFormTxnsSummary.ts @@ -259,7 +259,10 @@ export default function useFarmerFormTxnsSummary(mode?: 'plantToggle') { { type: ActionType.RINSE, amount: rinsableSprouts, - destination: mode === 'plantToggle' ? (values.destination || FarmToMode.INTERNAL) : values.destination, + destination: + mode === 'plantToggle' + ? values.destination || FarmToMode.INTERNAL + : values.destination, }, ], }, @@ -300,7 +303,7 @@ export default function useFarmerFormTxnsSummary(mode?: 'plantToggle') { revitalizedStalk, sdk.tokens, values.destination, - mode + mode, ]); /** diff --git a/projects/ui/src/pages/barn.tsx b/projects/ui/src/pages/barn.tsx index 88da08d61d..79f20a8d96 100644 --- a/projects/ui/src/pages/barn.tsx +++ b/projects/ui/src/pages/barn.tsx @@ -10,7 +10,7 @@ import { HOW_TO_RINSE_SPROUTS, HOW_TO_TRANSFER_FERTILIZER, HOW_TO_TRADE_FERTILIZER, - UNDERSTAND_FERT_VAPY + UNDERSTAND_FERT_VAPY, } from '~/util/Guides'; import { FC } from '~/types'; diff --git a/projects/ui/src/pages/silo/index.tsx b/projects/ui/src/pages/silo/index.tsx index e03db00cf2..23b076be66 100644 --- a/projects/ui/src/pages/silo/index.tsx +++ b/projects/ui/src/pages/silo/index.tsx @@ -38,7 +38,11 @@ import { AppState } from '~/state'; import { UNRIPE_BEAN, UNRIPE_BEAN_WETH } from '~/constants/tokens'; import useGetChainToken from '~/hooks/chain/useGetChainToken'; import GuideButton from '~/components/Common/Guide/GuideButton'; -import { CLAIM_SILO_REWARDS, HOW_TO_DEPOSIT_IN_THE_SILO, UNDERSTAND_SILO_VAPY } from '~/util/Guides'; +import { + CLAIM_SILO_REWARDS, + HOW_TO_DEPOSIT_IN_THE_SILO, + UNDERSTAND_SILO_VAPY, +} from '~/util/Guides'; import { FC } from '~/types'; import useSdk from '~/hooks/sdk'; @@ -345,8 +349,14 @@ const RewardsBar: FC<{ await farm.estimate(ethers.BigNumber.from(0)); - const adjustedGas = gas ? Math.floor(gas.toNumber() * gasMultiplier) : undefined; - const txn = await farm.execute(ethers.BigNumber.from(0), { slippage: 0 }, adjustedGas ? { gasLimit: adjustedGas } : undefined); + const adjustedGas = gas + ? Math.floor(gas.toNumber() * gasMultiplier) + : undefined; + const txn = await farm.execute( + ethers.BigNumber.from(0), + { slippage: 0 }, + adjustedGas ? { gasLimit: adjustedGas } : undefined + ); txToast.confirming(txn); const receipt = await txn.wait(); @@ -596,7 +606,9 @@ const RewardsBar: FC<{ label={ <GasTag px={0} - gasLimit={BigNumberJS(Math.floor((gas?.toNumber() || 0) * gasMultiplier))} + gasLimit={BigNumberJS( + Math.floor((gas?.toNumber() || 0) * gasMultiplier) + )} /> } /> @@ -643,7 +655,11 @@ const SiloPage: FC<{}> = () => { control={ <GuideButton title="The Farmers' Almanac: Silo Guides" - guides={[UNDERSTAND_SILO_VAPY, HOW_TO_DEPOSIT_IN_THE_SILO, CLAIM_SILO_REWARDS]} + guides={[ + UNDERSTAND_SILO_VAPY, + HOW_TO_DEPOSIT_IN_THE_SILO, + CLAIM_SILO_REWARDS, + ]} /> } /> diff --git a/projects/ui/src/util/Actions.ts b/projects/ui/src/util/Actions.ts index 41be4cb7eb..7d7e1df719 100644 --- a/projects/ui/src/util/Actions.ts +++ b/projects/ui/src/util/Actions.ts @@ -281,13 +281,21 @@ export const parseActionMessage = (a: Action) => { case ActionType.END_TOKEN: return null; case ActionType.SWAP: - if (a.tokenOut.isLP && a.tokenOut.symbol !== CRV3[1].symbol && !a.tokenOut.isUnripe) { + if ( + a.tokenOut.isLP && + a.tokenOut.symbol !== CRV3[1].symbol && + !a.tokenOut.isUnripe + ) { return `Add ${displayTokenAmount( a.amountIn, a.tokenIn )} of liquidity for ${displayTokenAmount(a.amountOut, a.tokenOut)}.`; } - if (a.tokenIn.isLP && a.tokenIn.symbol !== CRV3[1].symbol && !a.tokenIn.isUnripe) { + if ( + a.tokenIn.isLP && + a.tokenIn.symbol !== CRV3[1].symbol && + !a.tokenIn.isUnripe + ) { return `Burn ${displayTokenAmount( a.amountIn, a.tokenIn @@ -332,7 +340,9 @@ export const parseActionMessage = (a: Action) => { a.token )} from the Silo.`; case ActionType.IN_TRANSIT: - return `Receive ${displayTokenAmount(a.amount.abs(), a.token)} in your ${copy.MODES[a.destination]}.`; + return `Receive ${displayTokenAmount(a.amount.abs(), a.token)} in your ${ + copy.MODES[a.destination] + }.`; case ActionType.UPDATE_SILO_REWARDS: // FIXME: don't like "update" here return `${a.stalk.lt(0) ? 'Burn' : 'Receive'} ${displayFullBN( a.stalk.abs(), @@ -355,7 +365,10 @@ export const parseActionMessage = (a: Action) => { case ActionType.PLANT: return `Plant ${displayFullBN(a.bean, 2)} Bean${ a.bean.gt(1) ? 's' : '' - }, ${displayFullBN(a.stalk, 2)} Stalk, and ${displayFullBN(a.seeds, 2)} Seeds.`; + }, ${displayFullBN(a.stalk, 2)} Stalk, and ${displayFullBN( + a.seeds, + 2 + )} Seeds.`; case ActionType.ENROOT: return `Enroot revitalized ${displayFullBN( a.stalk, @@ -402,9 +415,14 @@ export const parseActionMessage = (a: Action) => { return `Rinse ${displayFullBN( a.amount, SPROUTS.displayDecimals - )} Sprouts${a.destination ? ` and send to your ${copy.MODES[a.destination]}.` : `.`}`; + )} Sprouts${ + a.destination ? ` and send to your ${copy.MODES[a.destination]}.` : `.` + }`; case ActionType.BUY_FERTILIZER: - return `Buy ${displayFullBN(a.amountOut, 2)} Fertilizer at ${displayFullBN( + return `Buy ${displayFullBN( + a.amountOut, + 2 + )} Fertilizer at ${displayFullBN( a.humidity.multipliedBy(100), 1 )}% Humidity with ${displayFullBN(a.amountIn, 2)} Wrapped Ether.`; diff --git a/projects/ui/src/util/BeaNFTs.ts b/projects/ui/src/util/BeaNFTs.ts index 9a97a1aaf2..bb4a08cacc 100644 --- a/projects/ui/src/util/BeaNFTs.ts +++ b/projects/ui/src/util/BeaNFTs.ts @@ -126,7 +126,7 @@ export async function loadNFTs(account: string) { } } } catch (e) { - console.error(e) + console.error(e); } return { diff --git a/projects/ui/src/util/Tokens.ts b/projects/ui/src/util/Tokens.ts index b195ec2a9c..35ae662e00 100644 --- a/projects/ui/src/util/Tokens.ts +++ b/projects/ui/src/util/Tokens.ts @@ -117,10 +117,10 @@ export function displayTokenAmount( ? _amount : tokenValueToBN(_amount); - const outputValue = config.allowNegative - ? displayFullBN(amount, token.displayDecimals) + const outputValue = config.allowNegative + ? displayFullBN(amount, token.displayDecimals) : displayFullBN(amount.abs(), token.displayDecimals); - + const modifier = config.modifier || ''; const name = config.showName ? token.name : ''; diff --git a/yarn.lock b/yarn.lock index 2a760cfc42..579de88ea4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3826,6 +3826,42 @@ __metadata: languageName: node linkType: hard +"@graphprotocol/graph-cli@npm:0.46.1": + version: 0.46.1 + resolution: "@graphprotocol/graph-cli@npm:0.46.1" + dependencies: + "@float-capital/float-subgraph-uncrashable": ^0.0.0-alpha.4 + "@oclif/core": 2.8.0 + "@whatwg-node/fetch": ^0.8.4 + assemblyscript: 0.19.23 + binary-install-raw: 0.0.13 + chalk: 3.0.0 + chokidar: 3.5.3 + debug: 4.3.4 + docker-compose: 0.23.19 + dockerode: 2.5.8 + fs-extra: 9.1.0 + glob: 9.3.4 + gluegun: "git+https://github.com/edgeandnode/gluegun.git#v4.3.1-pin-colors-dep" + graphql: 15.5.0 + immutable: 4.2.1 + ipfs-http-client: 55.0.0 + jayson: 4.0.0 + js-yaml: 3.14.1 + prettier: 1.19.1 + request: 2.88.2 + semver: 7.3.8 + sync-request: 6.1.0 + tmp-promise: 3.0.3 + web3-eth-abi: 1.7.0 + which: 2.0.2 + yaml: 1.10.2 + bin: + graph: dist/bin.js + checksum: ea82cbe4529212301a339a104a35a7d35c830bca5900662445b28870b076a870c967934102abef95dcb161c6d696189b10d3d54f9c78183da83b8fce74edcfd6 + languageName: node + linkType: hard + "@graphprotocol/graph-cli@npm:0.56.0": version: 0.56.0 resolution: "@graphprotocol/graph-cli@npm:0.56.0" @@ -3880,6 +3916,15 @@ __metadata: languageName: node linkType: hard +"@graphprotocol/graph-ts@npm:^0.29.1": + version: 0.29.3 + resolution: "@graphprotocol/graph-ts@npm:0.29.3" + dependencies: + assemblyscript: 0.19.10 + checksum: 3f79e638e9c21f4dbfac6dfcf22b5a2272f19648ac161a33b11a107f95c05f4d2e67de5684382c6c7a526b2aaf0eb72b3f734b50a5db0da780aac60943b85228 + languageName: node + linkType: hard + "@graphql-codegen/add@npm:^4.0.1": version: 4.0.1 resolution: "@graphql-codegen/add@npm:4.0.1" @@ -7958,6 +8003,43 @@ __metadata: languageName: node linkType: hard +"@oclif/core@npm:2.8.0": + version: 2.8.0 + resolution: "@oclif/core@npm:2.8.0" + dependencies: + "@types/cli-progress": ^3.11.0 + ansi-escapes: ^4.3.2 + ansi-styles: ^4.3.0 + cardinal: ^2.1.1 + chalk: ^4.1.2 + clean-stack: ^3.0.1 + cli-progress: ^3.12.0 + debug: ^4.3.4 + ejs: ^3.1.8 + fs-extra: ^9.1.0 + get-package-type: ^0.1.0 + globby: ^11.1.0 + hyperlinker: ^1.0.0 + indent-string: ^4.0.0 + is-wsl: ^2.2.0 + js-yaml: ^3.14.1 + natural-orderby: ^2.0.3 + object-treeify: ^1.1.33 + password-prompt: ^1.1.2 + semver: ^7.3.7 + string-width: ^4.2.3 + strip-ansi: ^6.0.1 + supports-color: ^8.1.1 + supports-hyperlinks: ^2.2.0 + ts-node: ^10.9.1 + tslib: ^2.5.0 + widest-line: ^3.1.0 + wordwrap: ^1.0.0 + wrap-ansi: ^7.0.0 + checksum: 6d47b0052898a04dfb5157fc2f7b14065d9851d1d88a65083260c72d6bea25318d7c18325b1ce692e06651258b6e90c851a5a1bce5c3d292afefbe1937bfd331 + languageName: node + linkType: hard + "@oclif/core@npm:2.8.6": version: 2.8.6 resolution: "@oclif/core@npm:2.8.6" @@ -14650,7 +14732,7 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:^3.0.0": +"ansi-colors@npm:^3.0.0, ansi-colors@npm:^3.2.1": version: 3.2.4 resolution: "ansi-colors@npm:3.2.4" checksum: 026c51880e9f8eb59b112669a87dbea4469939ff94b131606303bbd697438a6691b16b9db3027aa9bf132a244214e83ab1508b998496a34d2aea5b437ac9e62d @@ -14873,6 +14955,16 @@ __metadata: languageName: node linkType: hard +"apisauce@npm:^1.0.1": + version: 1.1.5 + resolution: "apisauce@npm:1.1.5" + dependencies: + axios: ^0.21.2 + ramda: ^0.25.0 + checksum: 4bac8117b484616c82e102d238580f835ffabee15da4527e845d091c12618f3b85293fb96c2357a119efede4ac1695d1ec78cc2ae5e3774f1522e1e1b1852365 + languageName: node + linkType: hard + "apisauce@npm:^2.1.5": version: 2.1.6 resolution: "apisauce@npm:2.1.6" @@ -15480,7 +15572,7 @@ __metadata: languageName: node linkType: hard -"axios@npm:^0.21.0, axios@npm:^0.21.1, axios@npm:^0.21.4": +"axios@npm:^0.21.0, axios@npm:^0.21.1, axios@npm:^0.21.2, axios@npm:^0.21.4": version: 0.21.4 resolution: "axios@npm:0.21.4" dependencies: @@ -17431,7 +17523,7 @@ __metadata: languageName: node linkType: hard -"cli-table3@npm:^0.5.0": +"cli-table3@npm:^0.5.0, cli-table3@npm:~0.5.0": version: 0.5.1 resolution: "cli-table3@npm:0.5.1" dependencies: @@ -17706,6 +17798,13 @@ __metadata: languageName: node linkType: hard +"colors@npm:1.3.3": + version: 1.3.3 + resolution: "colors@npm:1.3.3" + checksum: c57f0aa2b71a836435fb0cd8ac4b9f4025ff5411cb027ffcbaa2274347fd00ed52b9d66904f46be73086c27ac31bad9500da675250c95182568454b392f87ee5 + languageName: node + linkType: hard + "colors@npm:1.4.0, colors@npm:^1.1.2": version: 1.4.0 resolution: "colors@npm:1.4.0" @@ -18235,6 +18334,19 @@ __metadata: languageName: node linkType: hard +"cosmiconfig@npm:6.0.0, cosmiconfig@npm:^6.0.0": + version: 6.0.0 + resolution: "cosmiconfig@npm:6.0.0" + dependencies: + "@types/parse-json": ^4.0.0 + import-fresh: ^3.1.0 + parse-json: ^5.0.0 + path-type: ^4.0.0 + yaml: ^1.7.2 + checksum: 8eed7c854b91643ecb820767d0deb038b50780ecc3d53b0b19e03ed8aabed4ae77271198d1ae3d49c3b110867edf679f5faad924820a8d1774144a87cb6f98fc + languageName: node + linkType: hard + "cosmiconfig@npm:7.0.1, cosmiconfig@npm:^7.0.0": version: 7.0.1 resolution: "cosmiconfig@npm:7.0.1" @@ -18260,19 +18372,6 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^6.0.0": - version: 6.0.0 - resolution: "cosmiconfig@npm:6.0.0" - dependencies: - "@types/parse-json": ^4.0.0 - import-fresh: ^3.1.0 - parse-json: ^5.0.0 - path-type: ^4.0.0 - yaml: ^1.7.2 - checksum: 8eed7c854b91643ecb820767d0deb038b50780ecc3d53b0b19e03ed8aabed4ae77271198d1ae3d49c3b110867edf679f5faad924820a8d1774144a87cb6f98fc - languageName: node - linkType: hard - "cosmiconfig@npm:^8.1.0, cosmiconfig@npm:^8.1.3": version: 8.2.0 resolution: "cosmiconfig@npm:8.2.0" @@ -19898,6 +19997,13 @@ __metadata: languageName: node linkType: hard +"ejs@npm:^2.6.1": + version: 2.7.4 + resolution: "ejs@npm:2.7.4" + checksum: a1d2bfc7d1f0b39e99ae19b20c9469a25aeddba1ffc225db098110b18d566f73772fcdcc740b108cfda7452276f67d7b64eb359f90285414c942f4ae70713371 + languageName: node + linkType: hard + "ejs@npm:^3.1.6": version: 3.1.8 resolution: "ejs@npm:3.1.8" @@ -20083,6 +20189,15 @@ __metadata: languageName: node linkType: hard +"enquirer@npm:2.3.4": + version: 2.3.4 + resolution: "enquirer@npm:2.3.4" + dependencies: + ansi-colors: ^3.2.1 + checksum: e1dc49cfd9ca0c5d952dd5729e3129d5170016a89e490fbd3fee92aeaf7511b4f01be5cef1053faecbb5874f58a63acac1c494050e63c7020e509ddc6590d310 + languageName: node + linkType: hard + "enquirer@npm:2.3.6, enquirer@npm:^2.3.0, enquirer@npm:^2.3.5, enquirer@npm:^2.3.6": version: 2.3.6 resolution: "enquirer@npm:2.3.6" @@ -21541,6 +21656,24 @@ __metadata: languageName: node linkType: hard +"execa@npm:^3.0.0": + version: 3.4.0 + resolution: "execa@npm:3.4.0" + dependencies: + cross-spawn: ^7.0.0 + get-stream: ^5.0.0 + human-signals: ^1.1.1 + is-stream: ^2.0.0 + merge-stream: ^2.0.0 + npm-run-path: ^4.0.0 + onetime: ^5.1.0 + p-finally: ^2.0.0 + signal-exit: ^3.0.2 + strip-final-newline: ^2.0.0 + checksum: 72832ff72f79f9082dc3567775cbb52f4682452f7d8015714d924e476a37c36a98183fd669317327ed2e7800ffe7ec2a7be4bfe704a2173ef22ae00109fe9123 + languageName: node + linkType: hard + "execa@npm:^6.0.0, execa@npm:^6.1.0": version: 6.1.0 resolution: "execa@npm:6.1.0" @@ -22838,6 +22971,16 @@ __metadata: languageName: node linkType: hard +"fs-jetpack@npm:^2.2.2": + version: 2.4.0 + resolution: "fs-jetpack@npm:2.4.0" + dependencies: + minimatch: ^3.0.2 + rimraf: ^2.6.3 + checksum: 486a2974f5bbd3181b787416ff9c5fe128e2fa4a902e7314c659f0e141431ff075da1c674b98ba96e4f5b667a5f492231c51703ac3f073920f6388221394e92b + languageName: node + linkType: hard + "fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" @@ -23358,6 +23501,18 @@ __metadata: languageName: node linkType: hard +"glob@npm:9.3.4": + version: 9.3.4 + resolution: "glob@npm:9.3.4" + dependencies: + fs.realpath: ^1.0.0 + minimatch: ^8.0.2 + minipass: ^4.2.4 + path-scurry: ^1.6.1 + checksum: bcf49eaf475dc4ce8d4e98f896408a9f6507a2cb7d24a207c012cb318b969e04a02bcde2ff2920eadd5055ccae444a007b769e418147a56268fab2cda8694cde + languageName: node + linkType: hard + "glob@npm:9.3.5": version: 9.3.5 resolution: "glob@npm:9.3.5" @@ -23579,6 +23734,47 @@ __metadata: languageName: node linkType: hard +"gluegun@git+https://github.com/edgeandnode/gluegun.git#v4.3.1-pin-colors-dep": + version: 4.3.1 + resolution: "gluegun@https://github.com/edgeandnode/gluegun.git#commit=b34b9003d7bf556836da41b57ef36eb21570620a" + dependencies: + apisauce: ^1.0.1 + app-module-path: ^2.2.0 + cli-table3: ~0.5.0 + colors: 1.3.3 + cosmiconfig: 6.0.0 + cross-spawn: ^7.0.0 + ejs: ^2.6.1 + enquirer: 2.3.4 + execa: ^3.0.0 + fs-jetpack: ^2.2.2 + lodash.camelcase: ^4.3.0 + lodash.kebabcase: ^4.1.1 + lodash.lowercase: ^4.3.0 + lodash.lowerfirst: ^4.3.1 + lodash.pad: ^4.5.1 + lodash.padend: ^4.6.1 + lodash.padstart: ^4.6.1 + lodash.repeat: ^4.1.0 + lodash.snakecase: ^4.1.1 + lodash.startcase: ^4.4.0 + lodash.trim: ^4.5.1 + lodash.trimend: ^4.5.1 + lodash.trimstart: ^4.5.1 + lodash.uppercase: ^4.3.0 + lodash.upperfirst: ^4.3.1 + ora: ^4.0.0 + pluralize: ^8.0.0 + ramdasauce: ^2.1.0 + semver: ^7.0.0 + which: ^2.0.0 + yargs-parser: ^16.1.0 + bin: + gluegun: bin/gluegun + checksum: 71abe7f31555f169a47510675596f79193c8f55e4beeb4e6efa06c22d41988fa9c747d5e398af7f8401cca22c08ffb7a6d57b03d764c14858513c9eba23b53b8 + languageName: node + linkType: hard + "gluegun@npm:5.1.2": version: 5.1.2 resolution: "gluegun@npm:5.1.2" @@ -31769,6 +31965,22 @@ __metadata: languageName: node linkType: hard +"ora@npm:^4.0.0": + version: 4.1.1 + resolution: "ora@npm:4.1.1" + dependencies: + chalk: ^3.0.0 + cli-cursor: ^3.1.0 + cli-spinners: ^2.2.0 + is-interactive: ^1.0.0 + log-symbols: ^3.0.0 + mute-stream: 0.0.8 + strip-ansi: ^6.0.0 + wcwidth: ^1.0.1 + checksum: 5dcee3a2e143c7b578531ceda051e8c4b64655a019030fe3de4aef67ac28d08fca996aef71522d40b2316a272aa158d65028d7f43c126d318b70a49d9fa4f991 + languageName: node + linkType: hard + "ora@npm:^5.4.1": version: 5.4.1 resolution: "ora@npm:5.4.1" @@ -31927,6 +32139,13 @@ __metadata: languageName: node linkType: hard +"p-finally@npm:^2.0.0": + version: 2.0.1 + resolution: "p-finally@npm:2.0.1" + checksum: 6306a2851c3b28f8b603624f395ae84dce76970498fed8aa6aae2d930595053746edf1e4ee0c4b78a97410d84aa4504d63179f5310d555511ecd226f53ed1e8e + languageName: node + linkType: hard + "p-is-promise@npm:^2.0.0": version: 2.1.0 resolution: "p-is-promise@npm:2.1.0" @@ -33571,6 +33790,20 @@ __metadata: languageName: node linkType: hard +"ramda@npm:^0.24.1": + version: 0.24.1 + resolution: "ramda@npm:0.24.1" + checksum: c2dc048f5a0f61872eec7925f76cdf8e7b7cf7a4f457e274d915d8bf86bd108938795d92061d56eae315a4818ea65276a87c9db336356191aaf879647afd8c82 + languageName: node + linkType: hard + +"ramda@npm:^0.25.0": + version: 0.25.0 + resolution: "ramda@npm:0.25.0" + checksum: 008abbcc69aefd89a2a4a0c9f4cf9f8da2ec490a0e1e261b4c88de8540ef0c383d469bfdf71b758b559377c71bfa8efea164fdb1779169359a86b46f7cb23cb1 + languageName: node + linkType: hard + "ramda@npm:^0.28.0": version: 0.28.0 resolution: "ramda@npm:0.28.0" @@ -33578,6 +33811,15 @@ __metadata: languageName: node linkType: hard +"ramdasauce@npm:^2.1.0": + version: 2.1.3 + resolution: "ramdasauce@npm:2.1.3" + dependencies: + ramda: ^0.24.1 + checksum: e4b7be3b7dd9f0b986a99ec5946a980e26be550644957980c05d518e158512175bf0027b12d300d87dfb600ad8c548888c551e1836dd0a2c42735dda7dccca1a + languageName: node + linkType: hard + "random-bytes@npm:~1.0.0": version: 1.0.0 resolution: "random-bytes@npm:1.0.0" @@ -35466,6 +35708,17 @@ __metadata: languageName: node linkType: hard +"semver@npm:7.3.8, semver@npm:7.x, semver@npm:^7.0.0, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8": + version: 7.3.8 + resolution: "semver@npm:7.3.8" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 + languageName: node + linkType: hard + "semver@npm:7.4.0": version: 7.4.0 resolution: "semver@npm:7.4.0" @@ -35488,17 +35741,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.x, semver@npm:^7.0.0, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8": - version: 7.3.8 - resolution: "semver@npm:7.3.8" - dependencies: - lru-cache: ^6.0.0 - bin: - semver: bin/semver.js - checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 - languageName: node - linkType: hard - "semver@npm:^6.0.0, semver@npm:^6.1.1, semver@npm:^6.1.2, semver@npm:^6.3.0": version: 6.3.0 resolution: "semver@npm:6.3.0" @@ -39885,7 +40127,7 @@ __metadata: languageName: node linkType: hard -"which@npm:2.0.2, which@npm:^2.0.1, which@npm:^2.0.2": +"which@npm:2.0.2, which@npm:^2.0.0, which@npm:^2.0.1, which@npm:^2.0.2": version: 2.0.2 resolution: "which@npm:2.0.2" dependencies: @@ -40344,6 +40586,16 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^16.1.0": + version: 16.1.0 + resolution: "yargs-parser@npm:16.1.0" + dependencies: + camelcase: ^5.0.0 + decamelize: ^1.2.0 + checksum: 29d1e380e24616c67b8897c9fc2159b24418b42b6d8f91535cd504f02ba14e49d75dcd45258936f0fda58c449f441362c5bcc22f0f19cbf3a512cc4f346309fe + languageName: node + linkType: hard + "yargs-parser@npm:^18.1.2": version: 18.1.3 resolution: "yargs-parser@npm:18.1.3"