From 64c5d68df03a96c2ad67e21479cd2a452b97347b Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xmasayoshi@protonmail.com> Date: Mon, 21 Aug 2023 18:03:54 +0800 Subject: [PATCH 01/31] feat: token security table view --- ...ross-chain-swap-token-not-found-dialog.tsx | 332 ++++++++--------- .../simple-swap-token-not-found-dialog.tsx | 334 ++++++++---------- .../src/hooks/tokens/useTokenSecurity.ts | 155 ++++++-- .../future/components/TokenSecurityView.tsx | 143 ++++++++ .../TokenSelector/TokenSelector.tsx | 5 +- .../TokenSelector/TokenSelectorImportRow.tsx | 230 ++++++------ packages/wagmi/src/future/components/index.ts | 1 + 7 files changed, 654 insertions(+), 546 deletions(-) create mode 100644 packages/wagmi/src/future/components/TokenSecurityView.tsx diff --git a/apps/evm/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx b/apps/evm/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx index ba10748a64..bb1579f049 100644 --- a/apps/evm/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx +++ b/apps/evm/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx @@ -4,15 +4,24 @@ import { Chain } from '@sushiswap/chain' import { defaultQuoteCurrency, Native, Token } from '@sushiswap/currency' import { shortenAddress } from '@sushiswap/format' import { useCustomTokens } from '@sushiswap/hooks' -import { useTokenSecurity } from '@sushiswap/react-query' -import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogNew, DialogTitle } from '@sushiswap/ui' +import { isTokenSecurityChainId, useTokenSecurity } from '@sushiswap/react-query' +import { + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogNew, + DialogTitle, + NetworkIcon, +} from '@sushiswap/ui' import { Button } from '@sushiswap/ui/components/button' -import { GoPlusLabsIcon } from '@sushiswap/ui/components/icons' import { List } from '@sushiswap/ui/components/list/List' import { useTokenWithCache } from '@sushiswap/wagmi/future/hooks' import React, { useCallback, useMemo } from 'react' import { useDerivedStateCrossChainSwap } from './derivedstate-cross-chain-swap-provider' +import { TokenSecurityView } from '@sushiswap/wagmi/future' +import { Icon } from '@sushiswap/ui/components/currency/Icon' export const CrossChainSwapTokenNotFoundDialog = () => { const { @@ -54,7 +63,7 @@ export const CrossChainSwapTokenNotFoundDialog = () => { setTokens(Native.onChain(chainId0), defaultQuoteCurrency[chainId1 as keyof typeof defaultQuoteCurrency]) }, [chainId0, chainId1, setTokens]) - const { data: tokenSecurity } = useTokenSecurity({ + const { data: tokenSecurityResponse, isInitialLoading: tokenSecurityLoading } = useTokenSecurity({ currencies: useMemo( () => [ ...(token0NotInList && tokenFrom?.token ? [tokenFrom.token] : []), @@ -65,204 +74,141 @@ export const CrossChainSwapTokenNotFoundDialog = () => { enabled: Boolean(!tokenFromLoading && !tokenToLoading && (token0NotInList || token1NotInList)), }) - const isNotHoneyPot = Boolean(!tokenFromLoading && !tokenToLoading && (token0NotInList || token1NotInList)) + const honeypot = Boolean( + (tokenFrom?.token && tokenSecurityResponse?.[tokenFrom?.token?.address]?.is_honeypot) || + (tokenTo?.token && tokenSecurityResponse?.[tokenTo?.token?.address]?.is_honeypot) + ) - if (!tokenSecurity) return null + if ((isTokenSecurityChainId(chainId0) || isTokenSecurityChainId(chainId1)) && tokenSecurityLoading) return null return ( - - + !open && reset()} + > + - {isNotHoneyPot ? ( - <> - Unknown token{tokenFrom?.token && tokenTo?.token ? 's' : ''} - - Anyone can create a token, including creating fake versions of existing tokens that claim to represent - projects. If you purchase this token, you may not be able to sell it back. - - - ) : ( - <> - - Honeypot token{tokenSecurity.honeypots.length > 1 ? 's' : ''} detected - - - {tokenSecurity.honeypots.length > 1 - ? 'These tokens have been identified as potential honeypot scams and are not supported. Do not interact with these tokens to safeguard your assets.' - : 'This token has been identified as a potential honeypot scam and is not supported. Do not interact with this token to safeguard your assets.'}{' '} - Click{' '} - - here - {' '} - to learn more about honepot scams. - - - )} - -
- {isNotHoneyPot ? ( - <> - {token0 && token0NotInList && !tokenFrom?.token && ( - - Token {tokenFrom?.token && tokenTo?.token ? '1' : ''} - -

- Could not retrieve token info for{' '} - - {shortenAddress(token0.wrapped.address)} - {' '} - are you sure this token is on {Chain.from(chainId0).name}? -

-
-
- )} - {token1 && token1NotInList && !tokenTo?.token && ( - - Token {tokenFrom?.token && tokenTo?.token ? '2' : ''} - -

- Could not retrieve token info for{' '} - - {shortenAddress(token1.wrapped.address)} - {' '} - are you sure this token is on {Chain.from(chainId1).name}? -

-
-
- )} - {token0NotInList && tokenFrom?.token && ( - - Token {tokenFrom.token && tokenTo?.token ? '1' : ''} - - {tokenFrom.token.name} - {tokenFrom.token.symbol} - - - {shortenAddress(tokenFrom.token.address)} - - - - - )} - {token1NotInList && tokenTo?.token && ( - - Token {tokenFrom?.token && tokenTo?.token ? '2' : ''} - - {tokenTo.token.name} - {tokenTo.token.symbol} - - - {shortenAddress(tokenTo.token.address)} - - - - - )} - - {tokenSecurity?.isSupported && ( -
- - Honeypot detection powered by GoPlus - - + +
+ {token0 && token0NotInList ? ( +
+ +
- )} - - ) : ( - <> - - {tokenFrom?.token && tokenSecurity.honeypots.includes(tokenFrom.token.address) && ( - - - Token {tokenTo?.token && tokenSecurity.honeypots.includes(tokenTo.token.address) ? '1' : ''} - - - {tokenFrom.token.name} - {tokenFrom.token.symbol} - - - {shortenAddress(tokenFrom.token.address)} - - - - - )} - {tokenTo?.token && tokenSecurity.honeypots.includes(tokenTo.token.address) && ( - - - Token {tokenFrom?.token && tokenSecurity.honeypots.includes(tokenFrom.token.address) ? '2' : ''} - - - - {tokenTo.token.name ? `${tokenTo.token.name} (SCAM)` : null} - - - {tokenTo.token.symbol ? `${tokenTo.token.symbol} (SCAM)` : null} - - - - {shortenAddress(tokenTo.token.address)} - - - - - )} - {tokenSecurity.isSupported && ( -
- - Honeypot detection powered by GoPlus - - + ) : null} + {token1 && token1NotInList ? ( +
+ +
- )} - + ) : null} +
+ + Unknown token{(token0NotInList || !tokenFrom?.token) && (token1NotInList || !tokenTo?.token) ? 's' : ''} + + + + Anyone can create a token, including creating fake versions of existing tokens that claim to represent + projects. If you purchase this token, you may not be able to sell it back. + + +
+ {token0 && token0NotInList && !tokenFrom?.token && ( + + {token1NotInList || !tokenTo?.token ? Token 1 : null} + +

+ Could not retrieve token info for{' '} + + {shortenAddress(token0.wrapped.address)} + {' '} + are you sure this token is on {Chain.from(chainId0).name}? +

+
+
+ )} + {token0NotInList && tokenFrom?.token && ( + + {token1NotInList || !tokenTo?.token ? Token 1 : null} + + Name}> + {tokenFrom.token.name} + + Symbol}> + {tokenFrom.token.symbol} + + Address}> + + {shortenAddress(tokenFrom.token.address)} + + + + )} + {token0NotInList && tokenFrom?.token && isTokenSecurityChainId(tokenFrom.token.chainId) ? ( + + ) : null} + {token1 && token1NotInList && !tokenTo?.token && ( + + {token0NotInList || !tokenFrom?.token ? Token 2 : null} + +

+ Could not retrieve token info for{' '} + + {shortenAddress(token1.wrapped.address)} + {' '} + are you sure this token is on {Chain.from(chainId1).name}? +

+
+
+ )} + {token1NotInList && tokenTo?.token && ( + + {token0NotInList || !tokenFrom?.token ? Token 2 : null} + + Name}> + {tokenTo.token.name} + + Symbol}> + {tokenTo.token.symbol} + + Address}> + + {shortenAddress(tokenTo.token.address)} + + + + + )} + {token1NotInList && tokenTo?.token && isTokenSecurityChainId(tokenTo.token.chainId) ? ( + + ) : null}
- {isNotHoneyPot ? ( - (token0NotInList && tokenFrom?.token) || (token1NotInList && tokenTo?.token) ? ( - - ) : ( - - ) + {!honeypot && ((token0NotInList && tokenFrom?.token) || (token1NotInList && tokenTo?.token)) ? ( + ) : ( - {tokenFrom?.token && tokenSecurity.honeypots.includes(tokenFrom.token.address) && ( - - - Token {tokenTo?.token && tokenSecurity.honeypots.includes(tokenTo.token.address) ? '1' : ''} - - - {tokenFrom.token.name} - {tokenFrom.token.symbol} - - - {shortenAddress(tokenFrom.token.address)} - - - - - )} - {tokenTo?.token && tokenSecurity.honeypots.includes(tokenTo.token.address) && ( - - - Token {tokenFrom?.token && tokenSecurity.honeypots.includes(tokenFrom.token.address) ? '2' : ''} - - - - {tokenTo.token.name ? `${tokenTo.token.name} (SCAM)` : null} - - - {tokenTo.token.symbol ? `${tokenTo.token.symbol} (SCAM)` : null} - - - - {shortenAddress(tokenTo.token.address)} - - - - - )} - {tokenSecurity.isSupported && ( -
- - Honeypot detection powered by GoPlus - - + ) : null} + {token1 && token1NotInList ? ( +
+ +
- )} - + ) : null} +
+ + Unknown token{(token0NotInList || !tokenFrom?.token) && (token1NotInList || !tokenTo?.token) ? 's' : ''} + + + + Anyone can create a token, including creating fake versions of existing tokens that claim to represent + projects. If you purchase this token, you may not be able to sell it back. + + +
+ {token0 && token0NotInList && !tokenFrom?.token && ( + + {token1NotInList || !tokenTo?.token ? Token 1 : null} + +

+ Could not retrieve token info for{' '} + + {shortenAddress(token0.wrapped.address)} + {' '} + are you sure this token is on {Chain.from(chainId).name}? +

+
+
+ )} + {token0NotInList && tokenFrom?.token && ( + + {token1NotInList || !tokenTo?.token ? Token 1 : null} + + Name}> + {tokenFrom.token.name} + + Symbol}> + {tokenFrom.token.symbol} + + Address}> + + {shortenAddress(tokenFrom.token.address)} + + + + )} + {token0NotInList && tokenFrom?.token && isTokenSecurityChainId(tokenFrom.token.chainId) ? ( + + ) : null} + {token1 && token1NotInList && !tokenTo?.token && ( + + {token0NotInList || !tokenFrom?.token ? Token 2 : null} + +

+ Could not retrieve token info for{' '} + + {shortenAddress(token1.wrapped.address)} + {' '} + are you sure this token is on {Chain.from(chainId).name}? +

+
+
+ )} + {token1NotInList && tokenTo?.token && ( + + {token0NotInList || !tokenFrom?.token ? Token 2 : null} + + Name}> + {tokenTo.token.name} + + Symbol}> + {tokenTo.token.symbol} + + Address}> + + {shortenAddress(tokenTo.token.address)} + + + + + )} + {token1NotInList && tokenTo?.token && isTokenSecurityChainId(tokenTo.token.chainId) ? ( + + ) : null}
- {isNotHoneyPot ? ( - (token0NotInList && tokenFrom?.token) || (token1NotInList && tokenTo?.token) ? ( - - ) : ( - - ) + {!honeypot && ((token0NotInList && tokenFrom?.token) || (token1NotInList && tokenTo?.token)) ? ( + ) : (
+ } + value={ +
0 ? 'bg-yellow/20 text-yellow' : 'bg-green/20 text-green' + )} + > + {Number(issues?.length) > 0 ? ( + + ) : ( + + )} + {`${Number(issues?.length)} issue${ + Number(issues?.length) !== 1 ? 's' : '' + } found`} +
+ } + /> + {issues.map((key) => ( + + {TokenSecurityLabel[key]} + {TokenSecurityMessage[key]} +
+ } + > +
+
{tokenSecurity?.[key] === undefined ? 'Unknown' : tokenSecurity[key] ? 'Yes' : 'No'}
+ +
+ + ))} + {showMore + ? nonIssues.map((key) => ( + + {TokenSecurityLabel[key]} + {TokenSecurityMessage[key]} +
+ } + > +
+ {tokenSecurity?.[key] ? 'Yes' : 'No'} + +
+ + )) + : null} +
+ +
+ + + ) +} diff --git a/packages/wagmi/src/future/components/TokenSelector/TokenSelector.tsx b/packages/wagmi/src/future/components/TokenSelector/TokenSelector.tsx index e78cad0613..ba5c6139b7 100644 --- a/packages/wagmi/src/future/components/TokenSelector/TokenSelector.tsx +++ b/packages/wagmi/src/future/components/TokenSelector/TokenSelector.tsx @@ -217,10 +217,7 @@ export const TokenSelector: FC = ({ {queryToken && !customTokenMap[`${queryToken.chainId}:${queryToken.wrapped.address}`] && !tokenMap?.[`${queryToken.wrapped.address}`] && ( - queryToken && handleImport(queryToken)} - /> + queryToken && handleImport(queryToken)} /> )} = ({ currencies, onImport }) => { +export const TokenSelectorImportRow: FC = ({ currency, onImport }) => { const [open, setOpen] = useState(false) - const { data: tokenSecurity } = useTokenSecurity({ currencies, enabled: open }) + + const { data: tokenSecurityResponse, isInitialLoading: tokenSecurityLoading } = useTokenSecurity({ + currencies: [currency], + enabled: open, + }) const onClick = useCallback(() => { - if (!tokenSecurity?.honeypots || tokenSecurity.honeypots.length === 0) { - onImport() + onImport() - setTimeout(() => { - setOpen(false) - }, 250) - } else { + setTimeout(() => { setOpen(false) - } + }, 250) }, [onImport]) + const honeypot = Boolean(currency && tokenSecurityResponse?.[currency.address]?.is_honeypot) + return ( - + !open && setOpen(false)}>
- {currencies[0] ? ( -
-
- -
-
- - {currencies[0].symbol} - - - {currencies[0].name} - -
+
+
+
- ) : null} - +
+ + {currency.symbol} + + + {currency.name} + +
+
- +
- - - {!tokenSecurity?.honeypots || tokenSecurity.honeypots.length === 0 ? ( - <> - Import token - - Trade at your own risk! {currencies.length > 1 ? "These tokens don't" : "This token doesn't"} appear on - the active token list(s). Anyone can create a token, including creating fake versions of existing tokens - that claim to represent projects. - - - ) : ( - <> - Not supported! - - {tokenSecurity.honeypots.length > 1 - ? 'These tokens have been identified as potential honeypot scams and are not supported. Do not interact with these tokens to safeguard your assets.' - : 'This token has been identified as a potential honeypot scam and is not supported. Do not interact with this token to safeguard your assets.'}{' '} - - - - )} - -
- {!tokenSecurity?.honeypots || tokenSecurity.honeypots.length === 0 ? ( - - - {currencies.reduce((acc, cur) => { - if (cur) { - acc.push( -
- - -
- ) - } - - return acc - }, [])} -
-
- ) : ( - + {!isTokenSecurityChainId(currency.chainId) || !tokenSecurityLoading ? ( + + + +
+
+ + +
+
+ Import token +
+ + Anyone can create a token, including creating fake versions of existing tokens that claim to represent + projects. If you purchase this token, you may not be able to sell it back. + +
+
+ - {tokenSecurity.honeypots.reduce((acc, cur) => { - const currency = currencies.find((currency) => currency?.address === cur) - if (currency) { - acc.push( -
- -
- - {currency.symbol} - - - {shortenAddress(currency.address)} - -
-
- ) - } - - return acc - }, [])} + Name}> + {currency.name} + + Symbol}> + {currency.symbol} + + Address}> + + {shortenAddress(currency.address)} + +
- )} -
- -
- - {tokenSecurity?.isSupported ? ( -
- Honeypot detection powered by GoPlus - -
+ {isTokenSecurityChainId(currency.chainId) ? ( + ) : null}
-
-
+ +
+ {!honeypot ? ( + + ) : ( + + + + )} +
+
+ + ) : null} ) } diff --git a/packages/wagmi/src/future/components/index.ts b/packages/wagmi/src/future/components/index.ts index b2e2711dbe..9ff7c72a07 100644 --- a/packages/wagmi/src/future/components/index.ts +++ b/packages/wagmi/src/future/components/index.ts @@ -2,5 +2,6 @@ export * from './ConnectButton' export * from './NetworkCheck' export * from './SanctionedAddressDialog' export * from './UserProfile' +export * from './TokenSecurityView' export * from './wagmi-store-version-check' export * from './WagmiHeaderComponents' From 26dcc541ac669c1fe782fb843b572a24450d7415 Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xmasayoshi@protonmail.com> Date: Mon, 28 Aug 2023 20:52:04 +0800 Subject: [PATCH 02/31] feat: TaxTokenDialog on pool page --- apps/evm/app/pool/add/page.tsx | 11 ++- apps/evm/ui/pool/TaxTokenDialog.tsx | 70 +++++++++++++++++++ apps/evm/ui/pool/UnknownTokenAlert.tsx | 39 ++++++++--- .../src/hooks/tokens/useTokenSecurity.ts | 7 +- 4 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 apps/evm/ui/pool/TaxTokenDialog.tsx diff --git a/apps/evm/app/pool/add/page.tsx b/apps/evm/app/pool/add/page.tsx index ea16e3457c..94dc07eddd 100644 --- a/apps/evm/app/pool/add/page.tsx +++ b/apps/evm/app/pool/add/page.tsx @@ -3,13 +3,14 @@ import { ArrowLeftIcon, SwitchHorizontalIcon } from '@heroicons/react-v1/solid' import { Chain } from '@sushiswap/chain' import { tryParseAmount } from '@sushiswap/currency' +import { isTokenSecurityChainId } from '@sushiswap/react-query' import { typographyVariants } from '@sushiswap/ui' import { Separator } from '@sushiswap/ui' import { Badge } from '@sushiswap/ui/components/Badge' import { Button } from '@sushiswap/ui/components/button' import { Currency } from '@sushiswap/ui/components/currency' import { IconButton } from '@sushiswap/ui/components/iconbutton' -import { NetworkIcon } from '@sushiswap/ui/components/icons' +import { GoPlusLabsIcon, NetworkIcon } from '@sushiswap/ui/components/icons' import { List } from '@sushiswap/ui/components/list/List' import { SkeletonText } from '@sushiswap/ui/components/skeleton' import { SplashController } from '@sushiswap/ui/components/SplashController' @@ -31,6 +32,7 @@ import { } from 'ui/pool/ConcentratedLiquidityURLStateProvider' import { ConcentratedLiquidityWidget } from 'ui/pool/ConcentratedLiquidityWidget' import { SelectFeeConcentratedWidget } from 'ui/pool/NewPositionSection/SelectFeeConcentratedWidget' +import { TaxTokenDialog } from 'ui/pool/TaxTokenDialog' export default function Page() { return ( @@ -111,6 +113,7 @@ const _Add: FC = () => { return ( <> + {token0 && isTokenSecurityChainId(token0.chainId) ? : null}
@@ -241,6 +244,12 @@ const _Add: FC = () => { successLink={`/pools/${chainId}:${poolAddress}?activeTab=myPositions`} /> + {token0?.chainId && isTokenSecurityChainId(token0.chainId) ? ( +
+ Token security powered by GoPlus + +
+ ) : null}
) diff --git a/apps/evm/ui/pool/TaxTokenDialog.tsx b/apps/evm/ui/pool/TaxTokenDialog.tsx new file mode 100644 index 0000000000..13997c3c95 --- /dev/null +++ b/apps/evm/ui/pool/TaxTokenDialog.tsx @@ -0,0 +1,70 @@ +import { Currency } from '@sushiswap/currency' +import { shortenAddress } from '@sushiswap/format' +import { useTokenSecurity } from '@sushiswap/react-query' +import { Button, DialogContent, DialogFooter, DialogHeader, DialogNew, DialogTitle } from '@sushiswap/ui' +import { useRouter } from 'next/navigation' +import { useMemo, useState } from 'react' + +const tokenName = (token: Currency) => + token.name ? `${token.name} (${token.symbol})` : shortenAddress(token.wrapped.address) + +export const TaxTokenDialog = ({ token0, token1 }: { token0: Currency | undefined; token1: Currency | undefined }) => { + const [open, setOpen] = useState(false) + + const router = useRouter() + + const { data: tokenSecurityResponse } = useTokenSecurity({ + currencies: useMemo( + () => [...(token0 ? [token0.wrapped] : []), ...(token1 ? [token1.wrapped] : [])], + [token0, token1] + ), + onSuccess(data) { + if ( + (token0 && (data?.[token0.wrapped.address]?.buy_tax || data?.[token0.wrapped.address]?.sell_tax)) || + (token1 && (data?.[token1.wrapped.address]?.buy_tax || data?.[token1.wrapped.address]?.sell_tax)) + ) + setOpen(true) + }, + }) + + const token0HasTax = + token0 && + (tokenSecurityResponse?.[token0.wrapped.address]?.buy_tax || + tokenSecurityResponse?.[token0?.wrapped.address]?.sell_tax) + + const token1HasTax = + token1 && + (tokenSecurityResponse?.[token1.wrapped.address]?.buy_tax || + tokenSecurityResponse?.[token1.wrapped.address]?.sell_tax) + + return ( + setOpen(open)}> + + + Tax token detected + +
+
+ {token0HasTax && token1HasTax + ? `${tokenName(token0)} and ${tokenName(token1)} are tax tokens.` + : token0HasTax || token1HasTax + ? `${token0HasTax ? tokenName(token0) : token1HasTax ? tokenName(token1) : null} is a tax token.` + : null} +
+
+ Tax tokens are not supported in V3. You might not be able to trade, transfer, or withdraw liquidity of this + token. +
+
+ +
+ + +
+
+
+
+ ) +} diff --git a/apps/evm/ui/pool/UnknownTokenAlert.tsx b/apps/evm/ui/pool/UnknownTokenAlert.tsx index 7bb1d4396e..e4de9a29ef 100644 --- a/apps/evm/ui/pool/UnknownTokenAlert.tsx +++ b/apps/evm/ui/pool/UnknownTokenAlert.tsx @@ -2,8 +2,11 @@ import { ChainId } from '@sushiswap/chain' import { Pool } from '@sushiswap/client' import { shortenAddress } from '@sushiswap/format' import { useCustomTokens } from '@sushiswap/hooks' +import { isTokenSecurityChainId } from '@sushiswap/react-query' +import { GoPlusLabsIcon } from '@sushiswap/ui' import { useTokenWithCache } from '@sushiswap/wagmi/future/hooks' import { FC, useMemo } from 'react' +import { TaxTokenDialog } from './TaxTokenDialog' interface UnknownTokenAlert { pool: Pool @@ -13,7 +16,7 @@ const tokenName = (token: Pool['token0']) => token.name ? `${token.name} (${token.symbol})` : shortenAddress(token.address) export const UnknownTokenAlert: FC = ({ pool }) => { - const { token0, token1 } = pool + const { token0, token1, protocol } = pool const { hasToken } = useCustomTokens() @@ -42,16 +45,30 @@ export const UnknownTokenAlert: FC = ({ pool }) => { if (!(token0NotInList || token1NotInList)) return <> return ( -
-
- {`${ - token0NotInList && token1NotInList - ? `${tokenName(token0)} & ${tokenName(token1)} are unknown.` - : `${tokenName(token0NotInList ? token0 : token1)} is unknown.` - } Please conduct your own research before interacting with ${ - token0NotInList && token1NotInList ? 'these tokens.' : 'this token.' - }`} + <> +
+
+ {`${ + token0NotInList && token1NotInList + ? `${tokenName(token0)} & ${tokenName(token1)} are unknown.` + : `${tokenName(token0NotInList ? token0 : token1)} is unknown.` + } Please conduct your own research before interacting with ${ + token0NotInList && token1NotInList ? 'these tokens.' : 'this token.' + }`} +
+ {protocol === 'SUSHISWAP_V3' && tokenFrom?.token?.chainId && isTokenSecurityChainId(tokenFrom.token.chainId) ? ( +
+ Token security powered by GoPlus + +
+ ) : null}
-
+ {protocol === 'SUSHISWAP_V3' && + tokenTo?.token && + tokenFrom?.token && + isTokenSecurityChainId(tokenFrom.token.chainId) ? ( + + ) : null} + ) } diff --git a/packages/react-query/src/hooks/tokens/useTokenSecurity.ts b/packages/react-query/src/hooks/tokens/useTokenSecurity.ts index f9e71d8d02..813a4beb19 100644 --- a/packages/react-query/src/hooks/tokens/useTokenSecurity.ts +++ b/packages/react-query/src/hooks/tokens/useTokenSecurity.ts @@ -1,6 +1,6 @@ import { ChainId } from '@sushiswap/chain' import { Token } from '@sushiswap/currency' -import { useQuery } from '@tanstack/react-query' +import { useQuery, UseQueryOptions } from '@tanstack/react-query' import { z } from 'zod' const SUPPORTED_CHAIN_IDS = [ @@ -160,9 +160,9 @@ const fetchTokenSecurityQueryFn = async (currencies: (Token | undefined)[]) => { export const useTokenSecurity = ({ currencies, enabled = true, -}: { + ...rest +}: UseQueryOptions>> & { currencies: (Token | undefined)[] - enabled?: boolean }) => { return useQuery({ queryKey: ['useTokenSecurity', currencies?.map((currency) => currency?.id)], @@ -171,5 +171,6 @@ export const useTokenSecurity = ({ keepPreviousData: true, staleTime: 900000, // 15 mins cacheTime: 86400000, // 24hs + ...rest, }) } From fab777b69ee08a1b675d4a444d1968901a343865 Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xmasayoshi@protonmail.com> Date: Mon, 4 Sep 2023 12:55:40 +0800 Subject: [PATCH 03/31] feat: honeypot token unsupported message --- .../cross-chain-swap-token-not-found-dialog.tsx | 14 ++++++++++---- .../simple-swap-token-not-found-dialog.tsx | 14 ++++++++++---- .../TokenSelector/TokenSelectorImportRow.tsx | 16 +++++++++++----- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/apps/evm/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx b/apps/evm/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx index d9fb332581..4ac1e06e6f 100644 --- a/apps/evm/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx +++ b/apps/evm/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx @@ -12,6 +12,7 @@ import { DialogFooter, DialogHeader, DialogTitle, + Message, NetworkIcon, } from '@sushiswap/ui' import { Button } from '@sushiswap/ui/components/button' @@ -86,7 +87,7 @@ export const CrossChainSwapTokenNotFoundDialog = () => { open={Boolean(!tokenFromLoading && !tokenToLoading && (token0NotInList || token1NotInList))} onOpenChange={(open) => !open && reset()} > - +
@@ -210,9 +211,14 @@ export const CrossChainSwapTokenNotFoundDialog = () => { I understand ) : ( - +
+ + + Sushi does not support honetpot tokens. This token contract cannot be imported! + +
)} diff --git a/apps/evm/ui/swap/simple/simple-swap-token-not-found-dialog.tsx b/apps/evm/ui/swap/simple/simple-swap-token-not-found-dialog.tsx index 0a06eabe35..d2a93ec6ac 100644 --- a/apps/evm/ui/swap/simple/simple-swap-token-not-found-dialog.tsx +++ b/apps/evm/ui/swap/simple/simple-swap-token-not-found-dialog.tsx @@ -12,6 +12,7 @@ import { DialogFooter, DialogHeader, DialogTitle, + Message, NetworkIcon, } from '@sushiswap/ui' import { Button } from '@sushiswap/ui/components/button' @@ -86,7 +87,7 @@ export const SimpleSwapTokenNotFoundDialog = () => { open={Boolean(!tokenFromLoading && !tokenToLoading && (token0NotInList || token1NotInList))} onOpenChange={(open) => !open && reset()} > - +
@@ -210,9 +211,14 @@ export const SimpleSwapTokenNotFoundDialog = () => { I understand ) : ( - +
+ + + Sushi does not support honetpot tokens. This token contract cannot be imported! + +
)} diff --git a/packages/wagmi/src/future/components/TokenSelector/TokenSelectorImportRow.tsx b/packages/wagmi/src/future/components/TokenSelector/TokenSelectorImportRow.tsx index 6f43c9bb69..50d882ca53 100644 --- a/packages/wagmi/src/future/components/TokenSelector/TokenSelectorImportRow.tsx +++ b/packages/wagmi/src/future/components/TokenSelector/TokenSelectorImportRow.tsx @@ -9,6 +9,7 @@ import { DialogHeader, DialogTitle, DialogTrigger, + Message, NetworkIcon, } from '@sushiswap/ui' import { Button } from '@sushiswap/ui/components/button' @@ -121,11 +122,16 @@ export const TokenSelectorImportRow: FC = ({ currency, o I understand ) : ( - - - +
+ + + + + Sushi does not support honetpot tokens. This token contract cannot be imported! + +
)}
From a940b69b0f5bcb0b386ebeefe8bfca2a46520697 Mon Sep 17 00:00:00 2001 From: Chilli Chelli Date: Thu, 7 Sep 2023 02:36:15 +0100 Subject: [PATCH 04/31] fix(apps/pool): some styling updates --- .../simple-swap-token-not-found-dialog.tsx | 24 ++---------- .../ui/src/components/list/ListControl.tsx | 4 +- packages/ui/src/components/list/ListItem.tsx | 2 +- .../ui/src/components/list/ListKeyValue.tsx | 12 ++---- .../ui/src/components/list/ListMenuItem.tsx | 2 +- .../future/components/TokenSecurityView.tsx | 37 ++++++++----------- .../TokenSelector/TokenSelectorImportRow.tsx | 21 ++--------- 7 files changed, 30 insertions(+), 72 deletions(-) diff --git a/apps/evm/ui/swap/simple/simple-swap-token-not-found-dialog.tsx b/apps/evm/ui/swap/simple/simple-swap-token-not-found-dialog.tsx index d2a93ec6ac..5d9db8fa51 100644 --- a/apps/evm/ui/swap/simple/simple-swap-token-not-found-dialog.tsx +++ b/apps/evm/ui/swap/simple/simple-swap-token-not-found-dialog.tsx @@ -13,16 +13,14 @@ import { DialogHeader, DialogTitle, Message, - NetworkIcon, } from '@sushiswap/ui' import { Button } from '@sushiswap/ui/components/button' import { List } from '@sushiswap/ui/components/list/List' +import { TokenSecurityView } from '@sushiswap/wagmi/future' import { useTokenWithCache } from '@sushiswap/wagmi/future/hooks' -import React, { useCallback, useMemo, useState } from 'react' +import React, { useCallback, useMemo } from 'react' import { useDerivedStateSimpleSwap } from './derivedstate-simple-swap-provider' -import { TokenSecurityView } from '@sushiswap/wagmi/future' -import { Icon } from '@sushiswap/ui/components/currency/Icon' export const SimpleSwapTokenNotFoundDialog = () => { const { @@ -90,23 +88,7 @@ export const SimpleSwapTokenNotFoundDialog = () => { -
- {token0 && token0NotInList ? ( -
- - -
- ) : null} - {token1 && token1NotInList ? ( -
- - -
- ) : null} -
- - Unknown token{(token0NotInList || !tokenFrom?.token) && (token1NotInList || !tokenTo?.token) ? 's' : ''} - + Unknown token{(token0NotInList || !tokenFrom?.token) && (token1NotInList || !tokenTo?.token) ? 's' : ''}
Anyone can create a token, including creating fake versions of existing tokens that claim to represent diff --git a/packages/ui/src/components/list/ListControl.tsx b/packages/ui/src/components/list/ListControl.tsx index 85eb003894..fdf945db49 100644 --- a/packages/ui/src/components/list/ListControl.tsx +++ b/packages/ui/src/components/list/ListControl.tsx @@ -10,9 +10,7 @@ export interface ListControlProps { export const ListControl: FC = ({ children, className }) => { return ( - + {children} ) diff --git a/packages/ui/src/components/list/ListItem.tsx b/packages/ui/src/components/list/ListItem.tsx index 7b1a2ae7eb..6bd91df47c 100644 --- a/packages/ui/src/components/list/ListItem.tsx +++ b/packages/ui/src/components/list/ListItem.tsx @@ -43,7 +43,7 @@ export const ListItem: ListItemComponent = ({ className={classNames( className, subtitle ? 'items-start' : 'items-center', - 'relative flex gap-4 px-4 py-3 w-full cursor-pointer' + 'relative flex gap-4 px-3 py-3 w-full cursor-pointer' )} > {loading ? ( diff --git a/packages/ui/src/components/list/ListKeyValue.tsx b/packages/ui/src/components/list/ListKeyValue.tsx index 45a5b6213a..1275878226 100644 --- a/packages/ui/src/components/list/ListKeyValue.tsx +++ b/packages/ui/src/components/list/ListKeyValue.tsx @@ -24,7 +24,7 @@ export const ListKeyValue: FC = ({ }) => { if (skeleton) { return ( -
+
{subtitle && } @@ -41,18 +41,14 @@ export const ListKeyValue: FC = ({ className={classNames( className, flex ? 'flex justify-between items-center' : 'grid grid-cols-2', - 'gap-2 py-3 px-4' + 'gap-2 py-3 px-3 rounded-lg' )} >
- {title} + {title} {subtitle && {subtitle}}
-
- - {children} - -
+
{children}
) } diff --git a/packages/ui/src/components/list/ListMenuItem.tsx b/packages/ui/src/components/list/ListMenuItem.tsx index 56bcc4bf1c..998e1dff85 100644 --- a/packages/ui/src/components/list/ListMenuItem.tsx +++ b/packages/ui/src/components/list/ListMenuItem.tsx @@ -54,7 +54,7 @@ export const ListMenuItem: ListMenuItemComponent = ({ className, disabled ? 'opacity-40 !pointer-events-none cursor-default' : '', subtitle ? 'items-start' : 'items-center', - 'hover:bg-muted relative flex gap-4 px-4 py-3 w-full cursor-pointer rounded-xl' + 'hover:bg-muted relative flex gap-4 px-3 py-3 w-full cursor-pointer rounded-xl' )} > {Icon && ( diff --git a/packages/wagmi/src/future/components/TokenSecurityView.tsx b/packages/wagmi/src/future/components/TokenSecurityView.tsx index f5bfb5c837..4c6fa0bf4e 100644 --- a/packages/wagmi/src/future/components/TokenSecurityView.tsx +++ b/packages/wagmi/src/future/components/TokenSecurityView.tsx @@ -1,8 +1,7 @@ -import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline' import { ExclamationTriangleIcon, HandThumbUpIcon } from '@heroicons/react/24/solid' import { Token } from '@sushiswap/currency' import { TokenSecurity, TokenSecurityLabel, TokenSecurityMessage, TokenSecurityResponse } from '@sushiswap/react-query' -import { Button, Explainer, GoPlusLabsIcon, List, classNames } from '@sushiswap/ui' +import { Button, classNames, Explainer, GoPlusLabsIcon, List, SelectIcon } from '@sushiswap/ui' import { useMemo, useState } from 'react' const isTokenSecurityIssue = { @@ -74,9 +73,9 @@ export const TokenSecurityView = ({ )} > {Number(issues?.length) > 0 ? ( - + ) : ( - + )} {`${Number(issues?.length)} issue${ Number(issues?.length) !== 1 ? 's' : '' @@ -86,10 +85,10 @@ export const TokenSecurityView = ({ /> {issues.map((key) => ( +
{TokenSecurityLabel[key]} {TokenSecurityMessage[key]}
@@ -120,21 +119,17 @@ export const TokenSecurityView = ({
)) : null} -
-
diff --git a/packages/wagmi/src/future/components/TokenSelector/TokenSelectorImportRow.tsx b/packages/wagmi/src/future/components/TokenSelector/TokenSelectorImportRow.tsx index 50d882ca53..19ff6a9666 100644 --- a/packages/wagmi/src/future/components/TokenSelector/TokenSelectorImportRow.tsx +++ b/packages/wagmi/src/future/components/TokenSelector/TokenSelectorImportRow.tsx @@ -1,3 +1,4 @@ +import { Chain } from '@sushiswap/chain' import { Token } from '@sushiswap/currency' import { shortenAddress } from '@sushiswap/format' import { isTokenSecurityChainId, useTokenSecurity } from '@sushiswap/react-query' @@ -10,14 +11,13 @@ import { DialogTitle, DialogTrigger, Message, - NetworkIcon, } from '@sushiswap/ui' import { Button } from '@sushiswap/ui/components/button' import { Icon } from '@sushiswap/ui/components/currency/Icon' import { List } from '@sushiswap/ui/components/list/List' import { FC, useCallback, useState } from 'react' + import { TokenSecurityView } from '../TokenSecurityView' -import { Chain } from '@sushiswap/chain' interface TokenSelectorImportRow { currency: Token @@ -71,21 +71,8 @@ export const TokenSelectorImportRow: FC = ({ currency, o {!isTokenSecurityChainId(currency.chainId) || !tokenSecurityLoading ? ( - -
-
- - -
-
- Import token -
- + Import token + Anyone can create a token, including creating fake versions of existing tokens that claim to represent projects. If you purchase this token, you may not be able to sell it back. From aedf79beca15f5cf86e39bd2505ba90f97ff58e2 Mon Sep 17 00:00:00 2001 From: Chilli Chelli Date: Thu, 7 Sep 2023 02:37:54 +0100 Subject: [PATCH 05/31] fix(apps/swap): improve dialog styles --- apps/evm/ui/pool/TaxTokenDialog.tsx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/apps/evm/ui/pool/TaxTokenDialog.tsx b/apps/evm/ui/pool/TaxTokenDialog.tsx index 448f005e1b..b62fd9c80e 100644 --- a/apps/evm/ui/pool/TaxTokenDialog.tsx +++ b/apps/evm/ui/pool/TaxTokenDialog.tsx @@ -1,7 +1,15 @@ import { Currency } from '@sushiswap/currency' import { shortenAddress } from '@sushiswap/format' import { useTokenSecurity } from '@sushiswap/react-query' -import { Button, DialogContent, DialogFooter, DialogHeader, Dialog, DialogTitle } from '@sushiswap/ui' +import { + Button, + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@sushiswap/ui' import { useRouter } from 'next/navigation' import { useMemo, useState } from 'react' @@ -42,20 +50,16 @@ export const TaxTokenDialog = ({ token0, token1 }: { token0: Currency | undefine Tax token detected - -
-
+ {token0HasTax && token1HasTax - ? `${tokenName(token0)} and ${tokenName(token1)} are tax tokens.` + ? `${tokenName(token0)} and ${tokenName(token1)} are tax tokens. ` : token0HasTax || token1HasTax - ? `${token0HasTax ? tokenName(token0) : token1HasTax ? tokenName(token1) : null} is a tax token.` + ? `${token0HasTax ? tokenName(token0) : token1HasTax ? tokenName(token1) : null} is a tax token. ` : null} -
-
Tax tokens are not supported in V3. You might not be able to trade, transfer, or withdraw liquidity of this token. -
-
+
+
= ({ `${queryToken.chainId}:${queryToken.wrapped.address}` ] && !tokenMap?.[`${queryToken.wrapped.address}`] && ( - queryToken && handleImport(queryToken)} /> + queryToken && handleImport(queryToken)} + /> )} Date: Thu, 25 Apr 2024 17:19:09 +0700 Subject: [PATCH 09/31] chore: update TokenSecurityChainIds --- packages/react-query/src/hooks/tokens/useTokenSecurity.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react-query/src/hooks/tokens/useTokenSecurity.ts b/packages/react-query/src/hooks/tokens/useTokenSecurity.ts index 12a0a51208..a45e903565 100644 --- a/packages/react-query/src/hooks/tokens/useTokenSecurity.ts +++ b/packages/react-query/src/hooks/tokens/useTokenSecurity.ts @@ -17,6 +17,8 @@ const SUPPORTED_CHAIN_IDS = [ ChainId.LINEA, ChainId.BASE, ChainId.POLYGON, + ChainId.SCROLL, + ChainId.BLAST, ] as const export const TokenSecurityChainIds = SUPPORTED_CHAIN_IDS From 7e9853f0cd81a2542d7f990884b10e2aedbee1f2 Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xMasayoshi@protonmail.com> Date: Thu, 25 Apr 2024 17:21:02 +0700 Subject: [PATCH 10/31] chore: remove TaxTokenDialog --- apps/evm/src/app/pool/add/page.tsx | 4 - apps/evm/src/ui/pool/TaxTokenDialog.tsx | 92 ---------------------- apps/evm/src/ui/pool/UnknownTokenAlert.tsx | 7 -- 3 files changed, 103 deletions(-) delete mode 100644 apps/evm/src/ui/pool/TaxTokenDialog.tsx diff --git a/apps/evm/src/app/pool/add/page.tsx b/apps/evm/src/app/pool/add/page.tsx index 7c8b6dad8c..9e5f271594 100644 --- a/apps/evm/src/app/pool/add/page.tsx +++ b/apps/evm/src/app/pool/add/page.tsx @@ -5,7 +5,6 @@ import { GoPlusLabsIcon } from '@sushiswap/ui' import { useAccount, useConcentratedPositionInfo } from '@sushiswap/wagmi' import { getV3FactoryContractConfig } from '@sushiswap/wagmi/hooks/contracts/useV3FactoryContract' import React, { FC, useMemo, useState } from 'react' -import { TaxTokenDialog } from 'src/ui/pool/TaxTokenDialog' import { computeSushiSwapV3PoolAddress } from 'sushi' import { tryParseAmount } from 'sushi/currency' import { SWRConfig } from 'swr' @@ -82,9 +81,6 @@ const _Add: FC = () => { return ( <> - {token0 && isTokenSecurityChainId(token0.chainId) ? ( - - ) : null} {/*
*/} {/*
*/} {/*
*/} diff --git a/apps/evm/src/ui/pool/TaxTokenDialog.tsx b/apps/evm/src/ui/pool/TaxTokenDialog.tsx deleted file mode 100644 index e5d8a792ae..0000000000 --- a/apps/evm/src/ui/pool/TaxTokenDialog.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useTokenSecurity } from '@sushiswap/react-query' -import { - Button, - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@sushiswap/ui' -import { useRouter } from 'next/navigation' -import { useMemo, useState } from 'react' -import { shortenAddress } from 'sushi' -import { Currency } from 'sushi/currency' - -const tokenName = (token: Currency) => - token.name - ? `${token.name} (${token.symbol})` - : shortenAddress(token.wrapped.address) - -export const TaxTokenDialog = ({ - token0, - token1, -}: { token0: Currency | undefined; token1: Currency | undefined }) => { - const [open, setOpen] = useState(false) - - const router = useRouter() - - const { data: tokenSecurityResponse } = useTokenSecurity({ - currencies: useMemo( - () => [ - ...(token0?.isToken ? [token0] : []), - ...(token1?.isToken ? [token1] : []), - ], - [token0, token1], - ), - onSuccess(data) { - if ( - (token0 && - (data?.[token0.wrapped.address]?.buy_tax || - data?.[token0.wrapped.address]?.sell_tax)) || - (token1 && - (data?.[token1.wrapped.address]?.buy_tax || - data?.[token1.wrapped.address]?.sell_tax)) - ) - setOpen(true) - }, - }) - - const token0HasTax = - token0 && - (tokenSecurityResponse?.[token0.wrapped.address]?.buy_tax || - tokenSecurityResponse?.[token0?.wrapped.address]?.sell_tax) - - const token1HasTax = - token1 && - (tokenSecurityResponse?.[token1.wrapped.address]?.buy_tax || - tokenSecurityResponse?.[token1.wrapped.address]?.sell_tax) - - return ( - setOpen(open)}> - - - Tax token detected - - {token0HasTax && token1HasTax - ? `${tokenName(token0)} and ${tokenName(token1)} are tax tokens. ` - : token0HasTax || token1HasTax - ? `${ - token0HasTax - ? tokenName(token0) - : token1HasTax - ? tokenName(token1) - : null - } is a tax token. ` - : null} - Tax tokens are not supported in V3. You might not be able to trade, - transfer, or withdraw liquidity of this token. - - - -
- - -
-
-
-
- ) -} diff --git a/apps/evm/src/ui/pool/UnknownTokenAlert.tsx b/apps/evm/src/ui/pool/UnknownTokenAlert.tsx index 62fb296382..fc31944d8a 100644 --- a/apps/evm/src/ui/pool/UnknownTokenAlert.tsx +++ b/apps/evm/src/ui/pool/UnknownTokenAlert.tsx @@ -8,7 +8,6 @@ import { useTokenWithCache } from '@sushiswap/wagmi' import { FC, useMemo } from 'react' import { shortenAddress } from 'sushi' import { ChainId } from 'sushi/chain' -import { TaxTokenDialog } from './TaxTokenDialog' interface UnknownTokenAlert { pool: Pool @@ -58,12 +57,6 @@ export const UnknownTokenAlert: FC = ({ pool }) => { return ( <> - {protocol === 'SUSHISWAP_V3' && - tokenTo?.token && - tokenFrom?.token && - isTokenSecurityChainId(tokenFrom.token.chainId) ? ( - - ) : null} {`${ token0NotInList && token1NotInList From 6a991731309ff09f99ba5024b08308f72f1b9b81 Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xMasayoshi@protonmail.com> Date: Thu, 25 Apr 2024 17:29:50 +0700 Subject: [PATCH 11/31] chore: rename TokenSecurity fields --- .../src/hooks/tokens/useTokenSecurity.ts | 86 ++++++++++--------- .../src/components/token-security-view.tsx | 32 +++---- 2 files changed, 63 insertions(+), 55 deletions(-) diff --git a/packages/react-query/src/hooks/tokens/useTokenSecurity.ts b/packages/react-query/src/hooks/tokens/useTokenSecurity.ts index a45e903565..69e899f5ee 100644 --- a/packages/react-query/src/hooks/tokens/useTokenSecurity.ts +++ b/packages/react-query/src/hooks/tokens/useTokenSecurity.ts @@ -32,41 +32,49 @@ export const isTokenSecurityChainId = ( const bit = z.optional(z.enum(['0', '1']).transform((val) => val !== '0')) -const tokenSecuritySchema = z.object({ - is_open_source: bit, - is_proxy: bit, - is_mintable: bit, - can_take_back_ownership: bit, - owner_change_balance: bit, - hidden_owner: bit, - selfdestruct: bit, - external_call: bit, - gas_abuse: bit, - buy_tax: z.optional( - z.preprocess( - (val) => (val === '' ? undefined : val !== '0'), - z.optional(z.boolean()), +const tokenSecuritySchema = z + .object({ + is_open_source: bit, + is_proxy: bit, + is_mintable: bit, + can_take_back_ownership: bit, + owner_change_balance: bit, + hidden_owner: bit, + selfdestruct: bit, + external_call: bit, + gas_abuse: bit, + buy_tax: z.optional( + z.preprocess( + (val) => (val === '' ? undefined : val !== '0'), + z.optional(z.boolean()), + ), ), - ), - sell_tax: z.optional( - z.preprocess( - (val) => (val === '' ? undefined : val !== '0'), - z.optional(z.boolean()), + sell_tax: z.optional( + z.preprocess( + (val) => (val === '' ? undefined : val !== '0'), + z.optional(z.boolean()), + ), ), - ), - cannot_buy: bit, - cannot_sell_all: bit, - slippage_modifiable: bit, - is_honeypot: bit, - transfer_pausable: bit, - is_blacklisted: bit, - is_whitelisted: bit, - is_anti_whale: bit, - trading_cooldown: bit, - is_true_token: bit, - is_airdrop_scam: bit, - trust_list: bit, -}) + cannot_buy: bit, + cannot_sell_all: bit, + slippage_modifiable: bit, + is_honeypot: bit, + transfer_pausable: bit, + is_blacklisted: bit, + is_whitelisted: bit, + is_anti_whale: bit, + trading_cooldown: bit, + is_true_token: bit, + is_airdrop_scam: bit, + trust_list: bit, + }) + .transform(({ cannot_buy, is_true_token, cannot_sell_all, ...data }) => ({ + ...data, + is_buyable: typeof cannot_buy !== 'undefined' ? !cannot_buy : undefined, + is_fake_token: + typeof is_true_token !== 'undefined' ? !is_true_token : undefined, + is_sell_limit: cannot_sell_all, + })) export type TokenSecurity = z.infer @@ -86,8 +94,8 @@ export const TokenSecurityLabel: Record = { gas_abuse: 'Abuses Gas', buy_tax: 'Buy Tax', sell_tax: 'Sell Tax', - cannot_buy: 'Not Buyable', - cannot_sell_all: 'Unable to Sell All', + is_buyable: 'Buyable', + is_sell_limit: 'Sell Limit', slippage_modifiable: 'Slippage Modifiable', is_honeypot: 'Honeypot', transfer_pausable: 'Transfer Pausable', @@ -95,7 +103,7 @@ export const TokenSecurityLabel: Record = { is_whitelisted: 'Includes Whitelist', is_anti_whale: 'Anti-whale Mechanism', trading_cooldown: 'Trading Cooldown', - is_true_token: 'True Token', + is_fake_token: 'Fake Token', is_airdrop_scam: 'Airdrop Scam', trust_list: 'Trusted', } @@ -125,9 +133,9 @@ export const TokenSecurityMessage: Record = { 'Whether or not this token contract includes a buy tax. A buy tax will cause the actual token value received to be less than the amount paid. An excessive buy tax may lead to heavy losses.', sell_tax: 'Whether or not this token contract includes a sell tax. A sell tax will cause the actual value received when selling a token to be less than expected, and too much sell tax may lead to large losses.', - cannot_buy: + is_buyable: 'Whether or not this token can be purchased directly by users. Generally, these unbuyable tokens would be found in Reward Tokens. Such tokens are issued as rewards for some on-chain applications.', - cannot_sell_all: + is_sell_limit: 'Whether or not a user can sell all of their tokens in a single sale.', slippage_modifiable: 'Whether or not the contract owner can modify the buy tax or sell tax of the token. This may cause some losses, especially since some contracts have unlimited modifiable tax rates, which would make the token untradeable.', @@ -144,7 +152,7 @@ export const TokenSecurityMessage: Record = { trading_cooldown: 'Whether the contract has a trading-cool-down mechanism that can limit the minimum time between two transactions.', //Info security - is_true_token: 'Whether or not this token is true or fake.', + is_fake_token: 'Whether or not this token is fake or real.', is_airdrop_scam: 'Whether or not this token is an airdrop scam', trust_list: 'Whether or not this token is a famous and trustworthy one.', } diff --git a/packages/wagmi/src/components/token-security-view.tsx b/packages/wagmi/src/components/token-security-view.tsx index 9f371e821a..687ca13b75 100644 --- a/packages/wagmi/src/components/token-security-view.tsx +++ b/packages/wagmi/src/components/token-security-view.tsx @@ -22,6 +22,7 @@ import { Token } from 'sushi/currency' const isTokenSecurityIssue = { // Contract security is_open_source: (value: TokenSecurity['is_open_source']) => value === false, + is_proxy: (value: TokenSecurity['is_proxy']) => value === true, is_mintable: (value: TokenSecurity['is_mintable']) => value === true, can_take_back_ownership: (value: TokenSecurity['can_take_back_ownership']) => value === true, @@ -31,8 +32,8 @@ const isTokenSecurityIssue = { // Trading security buy_tax: (value: TokenSecurity['buy_tax']) => value === true, sell_tax: (value: TokenSecurity['sell_tax']) => value === true, - cannot_buy: (value: TokenSecurity['cannot_buy']) => value === true, - cannot_sell_all: (value: TokenSecurity['cannot_sell_all']) => value === true, + is_buyable: (value: TokenSecurity['is_buyable']) => value === false, + is_sell_limit: (value: TokenSecurity['is_sell_limit']) => value === true, slippage_modifiable: (value: TokenSecurity['slippage_modifiable']) => value === true, is_honeypot: (value: TokenSecurity['is_honeypot']) => value === true, @@ -44,9 +45,12 @@ const isTokenSecurityIssue = { trading_cooldown: (value: TokenSecurity['trading_cooldown']) => value === true, // Info security - is_true_token: (value: TokenSecurity['is_true_token']) => value === false, + is_fake_token: (value: TokenSecurity['is_fake_token']) => value === true, is_airdrop_scam: (value: TokenSecurity['is_airdrop_scam']) => value === true, -} +} as Record< + keyof TokenSecurity, + (value: TokenSecurity[keyof TokenSecurity]) => boolean +> export const TokenSecurityView = ({ tokenSecurityResponse, @@ -62,16 +66,12 @@ export const TokenSecurityView = ({ const issues: (keyof TokenSecurity)[] = [] const nonIssues: (keyof TokenSecurity)[] = [] - tokenSecurity && - Object.entries(tokenSecurity).forEach(([key, value]) => { - if ( - key in isTokenSecurityIssue && - isTokenSecurityIssue[key as keyof typeof isTokenSecurityIssue](value) - ) - issues.push(key as keyof TokenSecurity) - else if (key in isTokenSecurityIssue) - nonIssues.push(key as keyof TokenSecurity) - }) + for (const [_key, value] of Object.entries(tokenSecurity || {})) { + const key = _key as keyof TokenSecurity + if (key in isTokenSecurityIssue && isTokenSecurityIssue[key](value)) + issues.push(key) + else nonIssues.push(key) + } return { tokenSecurity, issues, nonIssues } }, [tokenSecurityResponse, token]) @@ -125,8 +125,8 @@ export const TokenSecurityView = ({ {tokenSecurity?.[key] === undefined ? 'Unknown' : tokenSecurity[key] - ? 'Yes' - : 'No'} + ? 'Yes' + : 'No'}
Date: Thu, 25 Apr 2024 17:42:03 +0700 Subject: [PATCH 12/31] chore: cleanup --- apps/evm/src/app/pool/add/page.tsx | 10 ------- apps/evm/src/ui/pool/UnknownTokenAlert.tsx | 35 ++++++++-------------- 2 files changed, 12 insertions(+), 33 deletions(-) diff --git a/apps/evm/src/app/pool/add/page.tsx b/apps/evm/src/app/pool/add/page.tsx index 9e5f271594..f738274fc6 100644 --- a/apps/evm/src/app/pool/add/page.tsx +++ b/apps/evm/src/app/pool/add/page.tsx @@ -1,7 +1,5 @@ 'use client' -import { isTokenSecurityChainId } from '@sushiswap/react-query' -import { GoPlusLabsIcon } from '@sushiswap/ui' import { useAccount, useConcentratedPositionInfo } from '@sushiswap/wagmi' import { getV3FactoryContractConfig } from '@sushiswap/wagmi/hooks/contracts/useV3FactoryContract' import React, { FC, useMemo, useState } from 'react' @@ -205,14 +203,6 @@ const _Add: FC = () => { tokenId={tokenId} successLink={`/pools/${chainId}:${poolAddress}?activeTab=myPositions`} /> - {token0?.chainId && isTokenSecurityChainId(token0.chainId) ? ( -
- - Token security powered by GoPlus - - -
- ) : null} ) } diff --git a/apps/evm/src/ui/pool/UnknownTokenAlert.tsx b/apps/evm/src/ui/pool/UnknownTokenAlert.tsx index fc31944d8a..a6a4893055 100644 --- a/apps/evm/src/ui/pool/UnknownTokenAlert.tsx +++ b/apps/evm/src/ui/pool/UnknownTokenAlert.tsx @@ -2,12 +2,11 @@ import { Pool } from '@sushiswap/client' import { useCustomTokens } from '@sushiswap/hooks' -import { isTokenSecurityChainId } from '@sushiswap/react-query' -import { GoPlusLabsIcon, Message } from '@sushiswap/ui' +import { Message } from '@sushiswap/ui' import { useTokenWithCache } from '@sushiswap/wagmi' import { FC, useMemo } from 'react' -import { shortenAddress } from 'sushi' import { ChainId } from 'sushi/chain' +import { shortenAddress } from 'sushi/format' interface UnknownTokenAlert { pool: Pool @@ -17,7 +16,7 @@ const tokenName = (token: Pool['token0']) => token.name ? `${token.name} (${token.symbol})` : shortenAddress(token.address) export const UnknownTokenAlert: FC = ({ pool }) => { - const { token0, token1, protocol } = pool + const { token0, token1 } = pool const { hasToken } = useCustomTokens() @@ -56,24 +55,14 @@ export const UnknownTokenAlert: FC = ({ pool }) => { if (!(token0NotInList || token1NotInList)) return <> return ( - <> - - {`${ - token0NotInList && token1NotInList - ? `${tokenName(token0)} & ${tokenName(token1)} are unknown.` - : `${tokenName(token0NotInList ? token0 : token1)} is unknown.` - } Please conduct your own research before interacting with ${ - token0NotInList && token1NotInList ? 'these tokens.' : 'this token.' - }`} - {protocol === 'SUSHISWAP_V3' && - tokenFrom?.token?.chainId && - isTokenSecurityChainId(tokenFrom.token.chainId) ? ( -
- Token security powered by GoPlus - -
- ) : null} -
- + + {`${ + token0NotInList && token1NotInList + ? `${tokenName(token0)} & ${tokenName(token1)} are unknown.` + : `${tokenName(token0NotInList ? token0 : token1)} is unknown.` + } Please conduct your own research before interacting with ${ + token0NotInList && token1NotInList ? 'these tokens.' : 'this token.' + }`} + ) } From 17d245dd3409902c323105424e9e5540a4cad332 Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xMasayoshi@protonmail.com> Date: Fri, 26 Apr 2024 23:35:38 +0700 Subject: [PATCH 13/31] feat: transaction deadline settings --- packages/hooks/src/index.ts | 1 + packages/hooks/src/useSlippageTolerance.ts | 5 +- packages/hooks/src/useTTL.ts | 5 ++ .../settings/TransactionDeadline.tsx | 68 +++++++++++++++++++ packages/ui/src/components/settings/index.tsx | 10 +++ .../utils/hooks/useTransactionDeadline.ts | 28 ++++---- 6 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 packages/hooks/src/useTTL.ts create mode 100644 packages/ui/src/components/settings/TransactionDeadline.tsx diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts index 9a50aec626..91fdaf0de2 100644 --- a/packages/hooks/src/index.ts +++ b/packages/hooks/src/index.ts @@ -16,4 +16,5 @@ export * from './useOnClickOutside' export * from './usePinnedTokens' export * from './usePrevious' export * from './useSlippageTolerance' +export * from './useTTL' export * from './useWindowSize' diff --git a/packages/hooks/src/useSlippageTolerance.ts b/packages/hooks/src/useSlippageTolerance.ts index 5772a39fd9..60d8c0338b 100644 --- a/packages/hooks/src/useSlippageTolerance.ts +++ b/packages/hooks/src/useSlippageTolerance.ts @@ -3,4 +3,7 @@ import { useLocalStorage } from './useLocalStorage' export const useSlippageTolerance = (key?: string, defaultValue?: string) => - useLocalStorage(key || 'swapSlippage', defaultValue || '0.1') + useLocalStorage( + key ? `${key}-slippage` : 'swapSlippage', + defaultValue || '0.1', + ) diff --git a/packages/hooks/src/useTTL.ts b/packages/hooks/src/useTTL.ts new file mode 100644 index 0000000000..f99232d29a --- /dev/null +++ b/packages/hooks/src/useTTL.ts @@ -0,0 +1,5 @@ +import { useLocalStorage } from './useLocalStorage' + +export const useTTL = (key?: string) => { + return useLocalStorage(key ? `${key}-ttl` : 'ttl', 0) +} diff --git a/packages/ui/src/components/settings/TransactionDeadline.tsx b/packages/ui/src/components/settings/TransactionDeadline.tsx new file mode 100644 index 0000000000..9d7e2b1c72 --- /dev/null +++ b/packages/ui/src/components/settings/TransactionDeadline.tsx @@ -0,0 +1,68 @@ +import { InformationCircleIcon } from '@heroicons/react/20/solid' +import { useTTL } from '@sushiswap/hooks' +import classNames from 'classnames' +import React, { FC, useCallback } from 'react' + +import { CardDescription, CardHeader, CardTitle } from '../card' +import { + HoverCard, + HoverCardContent, + HoverCardPrimitive, + HoverCardTrigger, +} from '../hover-card' +import { Label } from '../label' +import { TextField } from '../text-field' + +export const TransactionDeadline: FC<{ + options?: { + storageKey?: string + defaultValue?: string + title?: string + } + className?: string +}> = ({ options, className }) => { + const [ttl, setTTL] = useTTL(options?.storageKey) + + const onChange = useCallback( + (value: string) => { + setTTL(Number(value)) + }, + [setTTL], + ) + + return ( + +
+
+
+ +
+ +
+
+
+ ) +} diff --git a/packages/ui/src/components/settings/index.tsx b/packages/ui/src/components/settings/index.tsx index 24d0807c4e..4ac0139dd7 100644 --- a/packages/ui/src/components/settings/index.tsx +++ b/packages/ui/src/components/settings/index.tsx @@ -23,12 +23,14 @@ import { import { CarbonOffset } from './CarbonOffset' import { ExpertMode } from './ExpertMode' import { SlippageTolerance } from './SlippageTolerance' +import { TransactionDeadline } from './TransactionDeadline' export enum SettingsModule { CarbonOffset = 'CarbonOffset', CustomTokens = 'CustomTokens', SlippageTolerance = 'SlippageTolerance', ExpertMode = 'ExpertMode', + TransactionDeadline = 'TransactionDeadline', } interface SettingsOverlayProps { @@ -41,6 +43,11 @@ interface SettingsOverlayProps { defaultValue?: string title?: string } + transactionDeadline?: { + storageKey?: string + defaultValue?: string + title?: string + } } } @@ -118,6 +125,9 @@ export const SettingsOverlay: FC = ({ {modules.includes(SettingsModule.CarbonOffset) && ( )} + {modules.includes(SettingsModule.TransactionDeadline) && ( + + )} {externalModules?.map((Module, index) => ( diff --git a/packages/wagmi/src/hooks/utils/hooks/useTransactionDeadline.ts b/packages/wagmi/src/hooks/utils/hooks/useTransactionDeadline.ts index c596197d12..4520911c16 100644 --- a/packages/wagmi/src/hooks/utils/hooks/useTransactionDeadline.ts +++ b/packages/wagmi/src/hooks/utils/hooks/useTransactionDeadline.ts @@ -1,43 +1,45 @@ -import { ChainId, chainsL2 } from 'sushi/chain' - +import { useTTL } from '@sushiswap/hooks' import { useQuery } from '@tanstack/react-query' -import { useCurrentBlockTimestamp } from '../..' +import { ChainId, chainsL2 } from 'sushi/chain' +import { useCurrentBlockTimestamp } from '../../block' -const L2_DEADLINE_FROM_NOW = 60n * 5n +const L2_TTL = 5n const TTL = 30n +export const getDefaultTTL = (chainId: ChainId) => { + return Object.keys(chainsL2).includes(chainId.toString()) ? L2_TTL : TTL +} + interface UseTransactionDeadline { chainId: ChainId enabled?: boolean + storageKey?: string } export const useTransactionDeadline = ({ chainId, enabled = true, + storageKey = 'ttl', }: UseTransactionDeadline) => { const { data: currentBlockTimestampQuery } = useCurrentBlockTimestamp( chainId, enabled, ) + const [_ttl] = useTTL(storageKey) + // currentBlockTimestampQuery is excluded from the dependencies array by design, // deadline should be updated every 60s, not on every block return useQuery({ - queryKey: ['useTransactionDeadline'], + queryKey: ['useTransactionDeadline', _ttl], queryFn: () => { const blockTimestamp = currentBlockTimestampQuery let data = undefined - if ( - blockTimestamp && - chainId && - Object.keys(chainsL2).includes(chainId.toString()) - ) { - data = blockTimestamp + L2_DEADLINE_FROM_NOW - } + const ttl = _ttl > 0 ? BigInt(_ttl) : getDefaultTTL(chainId) if (blockTimestamp) { - data = blockTimestamp + TTL * 60n + data = blockTimestamp + ttl * 60n } return data From 9060a6c675b7c6504ef1df519f2c8d85b0d28a91 Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xMasayoshi@protonmail.com> Date: Sat, 27 Apr 2024 00:20:45 +0700 Subject: [PATCH 14/31] feat: add transaction deadline settings to add, remove, & migrate liquidity --- .../AddSectionReviewModalConcentrated.tsx | 55 +++++++++++++++---- .../ui/pool/AddSectionReviewModalLegacy.tsx | 49 ++++++++++++++--- apps/evm/src/ui/pool/AddSectionWidget.tsx | 6 ++ .../ConcentratedLiquidityRemoveWidget.tsx | 53 +++++++++++++++--- apps/evm/src/ui/pool/MigrateTab.tsx | 40 +++++++++++++- apps/evm/src/ui/pool/PositionView.tsx | 6 ++ apps/evm/src/ui/pool/RemoveSectionLegacy.tsx | 1 + apps/evm/src/ui/pool/RemoveSectionWidget.tsx | 11 +++- 8 files changed, 190 insertions(+), 31 deletions(-) diff --git a/apps/evm/src/ui/pool/AddSectionReviewModalConcentrated.tsx b/apps/evm/src/ui/pool/AddSectionReviewModalConcentrated.tsx index b450ecafe1..a1570a906a 100644 --- a/apps/evm/src/ui/pool/AddSectionReviewModalConcentrated.tsx +++ b/apps/evm/src/ui/pool/AddSectionReviewModalConcentrated.tsx @@ -1,3 +1,4 @@ +import { CogIcon } from '@heroicons/react-v1/outline' import { Button, Currency, @@ -10,11 +11,15 @@ import { DialogReview, DialogTitle, Dots, + IconButton, List, + SettingsModule, + SettingsOverlay, } from '@sushiswap/ui' import { createErrorToast, createToast } from '@sushiswap/ui/components/toast' import { UseCallParameters, + getDefaultTTL, getV3NonFungiblePositionManagerContractConfig, useAccount, useCall, @@ -32,7 +37,6 @@ import { SushiSwapV3FeeAmount, isSushiSwapV3ChainId } from 'sushi/config' import { Amount, Type, tryParseAmount } from 'sushi/currency' import { NonfungiblePositionManager, Position } from 'sushi/pool' import { Hex, SendTransactionReturnType, UserRejectedRequestError } from 'viem' - import { useConcentratedDerivedMintInfo } from './ConcentratedLiquidityProvider' interface AddSectionReviewModalConcentratedProps @@ -74,7 +78,10 @@ export const AddSectionReviewModalConcentrated: FC< successLink, }) => { const { address, chain } = useAccount() - const { data: deadline } = useTransactionDeadline({ chainId }) + const { data: deadline } = useTransactionDeadline({ + storageKey: 'addLiquidity', + chainId, + }) const [slippageTolerance] = useSlippageTolerance('addLiquidity') const { [Bound.LOWER]: priceLower, [Bound.UPPER]: priceUpper } = pricesAtTicks const client = usePublicClient() @@ -245,15 +252,41 @@ export const AddSectionReviewModalConcentrated: FC< <> {children} - - - {token0?.symbol}/{token1?.symbol} - - - {' '} - {noLiquidity ? 'Create liquidity pool' : 'Add liquidity'} - - +
+ + + {token0?.symbol}/{token1?.symbol} + + + {' '} + {noLiquidity ? 'Create liquidity pool' : 'Add liquidity'} + + + + + +
diff --git a/apps/evm/src/ui/pool/AddSectionReviewModalLegacy.tsx b/apps/evm/src/ui/pool/AddSectionReviewModalLegacy.tsx index 844ebc2dee..9c6d48fa17 100644 --- a/apps/evm/src/ui/pool/AddSectionReviewModalLegacy.tsx +++ b/apps/evm/src/ui/pool/AddSectionReviewModalLegacy.tsx @@ -1,3 +1,4 @@ +import { CogIcon } from '@heroicons/react-v1/outline' import { DialogConfirm, DialogContent, @@ -8,6 +9,9 @@ import { DialogReview, DialogTitle, DialogTrigger, + IconButton, + SettingsModule, + SettingsOverlay, } from '@sushiswap/ui' import { Button } from '@sushiswap/ui/components/button' import { Dots } from '@sushiswap/ui/components/dots' @@ -15,6 +19,7 @@ import { createErrorToast, createToast } from '@sushiswap/ui/components/toast' import { SushiSwapV2PoolState, UseSimulateContractParameters, + getDefaultTTL, getSushiSwapRouterContractConfig, useAccount, usePublicClient, @@ -34,7 +39,6 @@ import { BentoBoxChainId } from 'sushi/config' import { Amount, Type } from 'sushi/currency' import { ZERO } from 'sushi/math' import { Address, UserRejectedRequestError } from 'viem' - import { AddSectionReviewModal } from './AddSectionReviewModal' interface UseAddSushiSwapV2 { @@ -285,7 +289,10 @@ export const AddSectionReviewModalLegacy: FC = children, onSuccess: _onSuccess, }) => { - const { data: deadline } = useTransactionDeadline({ chainId }) + const { data: deadline } = useTransactionDeadline({ + storageKey: 'addLiquidity', + chainId, + }) const { address } = useAccount() const { approved } = useApproved(APPROVE_TAG_ADD_LEGACY) const [slippageTolerance] = useSlippageTolerance('addLiquidity') @@ -390,12 +397,38 @@ export const AddSectionReviewModalLegacy: FC = <> {children} - - Add liquidity - - Please review your entered details. - - +
+ + Add liquidity + + Please review your entered details. + + + + + +
= ({ defaultValue: '0.1', title: 'Add Liquidity Slippage', }, + transactionDeadline: { + storageKey: 'addLiquidity', + defaultValue: getDefaultTTL(chainId).toString(), + }, }} modules={[ SettingsModule.CustomTokens, SettingsModule.SlippageTolerance, + SettingsModule.TransactionDeadline, ]} > ('0') const [slippageTolerance] = useSlippageTolerance('removeLiquidity') - const { data: deadline } = useTransactionDeadline({ chainId }) + const { data: deadline } = useTransactionDeadline({ + storageKey: 'removeLiquidity', + chainId, + }) const debouncedValue = useDebounce(value, 300) const _onChange = useCallback( @@ -338,8 +342,15 @@ export const ConcentratedLiquidityRemoveWidget: FC< defaultValue: '0.1', title: 'Remove Liquidity Slippage', }, + transactionDeadline: { + storageKey: 'removeLiquidity', + defaultValue: getDefaultTTL(chainId).toString(), + }, }} - modules={[SettingsModule.SlippageTolerance]} + modules={[ + SettingsModule.SlippageTolerance, + SettingsModule.TransactionDeadline, + ]} >
- - - {token0?.symbol}/{token1?.symbol} - - Remove Liquidity - +
+ + + {token0?.symbol}/{token1?.symbol} + + Remove Liquidity + + + + +
diff --git a/apps/evm/src/ui/pool/MigrateTab.tsx b/apps/evm/src/ui/pool/MigrateTab.tsx index 2b2491fb95..590bc3ca4b 100644 --- a/apps/evm/src/ui/pool/MigrateTab.tsx +++ b/apps/evm/src/ui/pool/MigrateTab.tsx @@ -1,5 +1,6 @@ 'use client' +import { CogIcon } from '@heroicons/react-v1/outline' import { SwitchHorizontalIcon } from '@heroicons/react-v1/solid' import { Pool } from '@sushiswap/client' import { @@ -24,14 +25,18 @@ import { DialogTitle, DialogTrigger, Dots, + IconButton, List, Message, Separator, + SettingsModule, + SettingsOverlay, } from '@sushiswap/ui' import { Button } from '@sushiswap/ui/components/button' import { V3MigrateChainId, V3MigrateContractConfig, + getDefaultTTL, getMasterChefContractConfig, useAccount, useMasterChefWithdraw, @@ -459,6 +464,7 @@ export const MigrateTab: FC<{ pool: Pool }> = withCheckerRoot(({ pool }) => { const { approved: approvedMigrate } = useApproved(APPROVE_TAG_MIGRATE) const { data: deadline } = useTransactionDeadline({ + storageKey: 'addLiquidity', chainId: pool.chainId as ChainId, }) @@ -718,9 +724,37 @@ export const MigrateTab: FC<{ pool: Pool }> = withCheckerRoot(({ pool }) => { - - Migrate Liquidity - +
+ + Migrate Liquidity + + + + +
{token0?.symbol}/{token1?.symbol} • SushiSwap V3 • {feeAmount / 10000}% diff --git a/apps/evm/src/ui/pool/PositionView.tsx b/apps/evm/src/ui/pool/PositionView.tsx index f596969930..9e466f2d2c 100644 --- a/apps/evm/src/ui/pool/PositionView.tsx +++ b/apps/evm/src/ui/pool/PositionView.tsx @@ -33,6 +33,7 @@ import { Button } from '@sushiswap/ui/components/button' import { FormattedPrice } from '@sushiswap/ui/components/formatted-price' import { SkeletonText } from '@sushiswap/ui/components/skeleton' import { + getDefaultTTL, useAccount, useConcentratedLiquidityPositionsFromTokenId, useConcentratedPositionInfo, @@ -242,10 +243,15 @@ const Component: FC<{ id: string }> = ({ id }) => { defaultValue: '0.1', title: 'Add Liquidity Slippage', }, + transactionDeadline: { + storageKey: 'addLiquidity', + defaultValue: getDefaultTTL(chainId).toString(), + }, }} modules={[ SettingsModule.CustomTokens, SettingsModule.SlippageTolerance, + SettingsModule.TransactionDeadline, ]} > = const client = usePublicClient() const { address, chain } = useAccount() const { data: deadline } = useTransactionDeadline({ + storageKey: 'removeLiquidity', chainId: _pool.chainId as ChainId, }) const contract = useSushiSwapRouterContract( diff --git a/apps/evm/src/ui/pool/RemoveSectionWidget.tsx b/apps/evm/src/ui/pool/RemoveSectionWidget.tsx index 74b5231ed0..8bcdbe76ff 100644 --- a/apps/evm/src/ui/pool/RemoveSectionWidget.tsx +++ b/apps/evm/src/ui/pool/RemoveSectionWidget.tsx @@ -27,6 +27,7 @@ import { ChainId } from 'sushi/chain' import { Amount, Type } from 'sushi/currency' import { ZERO } from 'sushi/math' +import { getDefaultTTL } from '@sushiswap/wagmi' import { usePoolPosition } from './PoolPositionProvider' interface RemoveSectionWidgetProps { @@ -42,6 +43,7 @@ interface RemoveSectionWidgetProps { } export const RemoveSectionWidget: FC = ({ + chainId, percentage, setPercentage, token0Minimum, @@ -65,8 +67,15 @@ export const RemoveSectionWidget: FC = ({ defaultValue: '0.1', title: 'Remove Liquidity Slippage', }, + transactionDeadline: { + storageKey: 'removeLiquidity', + defaultValue: getDefaultTTL(chainId).toString(), + }, }} - modules={[SettingsModule.SlippageTolerance]} + modules={[ + SettingsModule.SlippageTolerance, + SettingsModule.TransactionDeadline, + ]} > Date: Sat, 27 Apr 2024 17:39:58 +0200 Subject: [PATCH 15/31] chore(config/graph): update arbitrum v3 subgraph --- config/graph/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/graph/src/index.ts b/config/graph/src/index.ts index 0871d0cefe..aab7a325dd 100644 --- a/config/graph/src/index.ts +++ b/config/graph/src/index.ts @@ -343,7 +343,7 @@ export const SUSHISWAP_SUBGRAPH_URL: Record = { export const SUSHISWAP_V3_SUBGRAPH_URL: Record = { [ChainId.ARBITRUM_NOVA]: `${SUSHI_DEDICATED_GOLDSKY_HOST}/sushi-v3/v3-arbitrum-nova/gn`, - [ChainId.ARBITRUM]: `${GRAPH_HOST}/sushi-v3/v3-arbitrum`, + [ChainId.ARBITRUM]: `${STUDIO_HOST}/v3-arbitrum/v0.0.1`, [ChainId.AVALANCHE]: `${GRAPH_HOST}/sushi-v3/v3-avalanche`, [ChainId.BSC]: `${GRAPH_HOST}/sushi-v3/v3-bsc`, [ChainId.BOBA]: `${GRAPH_HOST}/sushi-v3/v3-boba`, From 4e18fcd715c3be3c8ca42f0daea6b68836b8e867 Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xMasayoshi@protonmail.com> Date: Mon, 29 Apr 2024 20:51:49 +0700 Subject: [PATCH 16/31] chore: move TokenSecurityChainIds to sushi/config --- ...ross-chain-swap-token-not-found-dialog.tsx | 7 ++--- .../simple-swap-token-not-found-dialog.tsx | 7 ++--- .../src/hooks/tokens/useTokenSecurity.ts | 29 +------------------ packages/sushi/src/config/index.ts | 1 + packages/sushi/src/config/token-security.ts | 28 ++++++++++++++++++ .../token-selector/TokenSelectorImportRow.tsx | 6 ++-- 6 files changed, 36 insertions(+), 42 deletions(-) create mode 100644 packages/sushi/src/config/token-security.ts diff --git a/apps/evm/src/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx b/apps/evm/src/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx index 7320e64b70..127faef1f6 100644 --- a/apps/evm/src/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx +++ b/apps/evm/src/ui/swap/cross-chain/cross-chain-swap-token-not-found-dialog.tsx @@ -1,10 +1,7 @@ 'use client' import { useCustomTokens } from '@sushiswap/hooks' -import { - isTokenSecurityChainId, - useTokenSecurity, -} from '@sushiswap/react-query' +import { useTokenSecurity } from '@sushiswap/react-query' import { Dialog, DialogContent, @@ -21,7 +18,7 @@ import { List } from '@sushiswap/ui/components/list' import { TokenSecurityView, useTokenWithCache } from '@sushiswap/wagmi' import React, { useCallback, useMemo } from 'react' import { Chain } from 'sushi/chain' -import { defaultQuoteCurrency } from 'sushi/config' +import { defaultQuoteCurrency, isTokenSecurityChainId } from 'sushi/config' import { Native, Token } from 'sushi/currency' import { shortenAddress } from 'sushi/format' import { useDerivedStateCrossChainSwap } from './derivedstate-cross-chain-swap-provider' diff --git a/apps/evm/src/ui/swap/simple/simple-swap-token-not-found-dialog.tsx b/apps/evm/src/ui/swap/simple/simple-swap-token-not-found-dialog.tsx index 5860afd615..427d21322f 100644 --- a/apps/evm/src/ui/swap/simple/simple-swap-token-not-found-dialog.tsx +++ b/apps/evm/src/ui/swap/simple/simple-swap-token-not-found-dialog.tsx @@ -1,10 +1,7 @@ 'use client' import { useCustomTokens } from '@sushiswap/hooks' -import { - isTokenSecurityChainId, - useTokenSecurity, -} from '@sushiswap/react-query' +import { useTokenSecurity } from '@sushiswap/react-query' import { Button, Dialog, @@ -19,7 +16,7 @@ import { import { TokenSecurityView, useTokenWithCache } from '@sushiswap/wagmi' import React, { useCallback, useMemo } from 'react' import { Chain } from 'sushi/chain' -import { defaultQuoteCurrency } from 'sushi/config' +import { defaultQuoteCurrency, isTokenSecurityChainId } from 'sushi/config' import { Native, Token } from 'sushi/currency' import { shortenAddress } from 'sushi/format' diff --git a/packages/react-query/src/hooks/tokens/useTokenSecurity.ts b/packages/react-query/src/hooks/tokens/useTokenSecurity.ts index 69e899f5ee..3075c85011 100644 --- a/packages/react-query/src/hooks/tokens/useTokenSecurity.ts +++ b/packages/react-query/src/hooks/tokens/useTokenSecurity.ts @@ -1,35 +1,8 @@ import { type UseQueryOptions, useQuery } from '@tanstack/react-query' -import { ChainId } from 'sushi/chain' +import { isTokenSecurityChainId } from 'sushi/config' import { Token } from 'sushi/currency' import { z } from 'zod' -const SUPPORTED_CHAIN_IDS = [ - ChainId.AVALANCHE, - ChainId.BSC, - ChainId.FANTOM, - ChainId.GNOSIS, - ChainId.HARMONY, - ChainId.ETHEREUM, - ChainId.HECO, - ChainId.ARBITRUM, - ChainId.OPTIMISM, - ChainId.ZKSYNC_ERA, - ChainId.LINEA, - ChainId.BASE, - ChainId.POLYGON, - ChainId.SCROLL, - ChainId.BLAST, -] as const - -export const TokenSecurityChainIds = SUPPORTED_CHAIN_IDS - -export type TokenSecurityChainId = (typeof SUPPORTED_CHAIN_IDS)[number] - -export const isTokenSecurityChainId = ( - chainId: ChainId, -): chainId is TokenSecurityChainId => - SUPPORTED_CHAIN_IDS.includes(chainId as TokenSecurityChainId) - const bit = z.optional(z.enum(['0', '1']).transform((val) => val !== '0')) const tokenSecuritySchema = z diff --git a/packages/sushi/src/config/index.ts b/packages/sushi/src/config/index.ts index 2f576865be..ba85839515 100644 --- a/packages/sushi/src/config/index.ts +++ b/packages/sushi/src/config/index.ts @@ -16,6 +16,7 @@ export * from './stargate.js' export * from './sushiswap-v2.js' export * from './sushiswap-v3.js' export * from './sushixswap.js' +export * from './token-security.js' export * from './trident.js' export * from './uniswap-v2.js' export * from './uniswap-v3.js' diff --git a/packages/sushi/src/config/token-security.ts b/packages/sushi/src/config/token-security.ts new file mode 100644 index 0000000000..4bce5552b2 --- /dev/null +++ b/packages/sushi/src/config/token-security.ts @@ -0,0 +1,28 @@ +import { ChainId } from '../chain' + +const SUPPORTED_CHAIN_IDS = [ + ChainId.AVALANCHE, + ChainId.BSC, + ChainId.FANTOM, + ChainId.GNOSIS, + ChainId.HARMONY, + ChainId.ETHEREUM, + ChainId.HECO, + ChainId.ARBITRUM, + ChainId.OPTIMISM, + ChainId.ZKSYNC_ERA, + ChainId.LINEA, + ChainId.BASE, + ChainId.POLYGON, + ChainId.SCROLL, + ChainId.BLAST, +] as const + +export const TokenSecurityChainIds = SUPPORTED_CHAIN_IDS + +export type TokenSecurityChainId = (typeof SUPPORTED_CHAIN_IDS)[number] + +export const isTokenSecurityChainId = ( + chainId: ChainId, +): chainId is TokenSecurityChainId => + SUPPORTED_CHAIN_IDS.includes(chainId as TokenSecurityChainId) diff --git a/packages/wagmi/src/components/token-selector/TokenSelectorImportRow.tsx b/packages/wagmi/src/components/token-selector/TokenSelectorImportRow.tsx index 67ab7be694..3eee3b2f53 100644 --- a/packages/wagmi/src/components/token-selector/TokenSelectorImportRow.tsx +++ b/packages/wagmi/src/components/token-selector/TokenSelectorImportRow.tsx @@ -1,7 +1,4 @@ -import { - isTokenSecurityChainId, - useTokenSecurity, -} from '@sushiswap/react-query' +import { useTokenSecurity } from '@sushiswap/react-query' import { Dialog, DialogContent, @@ -20,6 +17,7 @@ import { Chain } from 'sushi/chain' import { Token } from 'sushi/currency' import { shortenAddress } from 'sushi/format' +import { isTokenSecurityChainId } from 'sushi/config' import { TokenSecurityView } from '../token-security-view' interface TokenSelectorImportRow { From 109789098e2d852058a411dc823e1e3bcfec45de Mon Sep 17 00:00:00 2001 From: Matthew Lilley Date: Tue, 30 Apr 2024 11:07:45 +0100 Subject: [PATCH 17/31] fix: dep graph of ui package --- packages/react-query/package.json | 8 ++++++-- pnpm-lock.yaml | 27 ++++++++++++++------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/packages/react-query/package.json b/packages/react-query/package.json index 3e8c861f93..0b2ee29ebe 100644 --- a/packages/react-query/package.json +++ b/packages/react-query/package.json @@ -43,8 +43,6 @@ "test": "jest --passWithNoTests" }, "dependencies": { - "@sushiswap/client": "workspace:*", - "@sushiswap/database": "workspace:*", "@sushiswap/dexie": "workspace:*", "@sushiswap/hooks": "workspace:*", "@tanstack/react-query": "4.28.0", @@ -57,6 +55,8 @@ "devDependencies": { "@sentry/nextjs": "7.101.1", "@sushiswap/jest-config": "workspace:*", + "@sushiswap/client": "workspace:*", + "@sushiswap/database": "workspace:*", "@tsconfig/esm": "1.0.4", "@tsconfig/strictest": "2.0.2", "@types/ms": "0.7.34", @@ -73,6 +73,7 @@ }, "peerDependencies": { "@sentry/nextjs": "7.101.1", + "@sushiswap/client": "*", "next": "14.1.0", "react": "18.2.0", "react-dom": "18.2.0", @@ -81,6 +82,9 @@ "wagmi": "2.5.20" }, "peerDependenciesMeta": { + "@sushiswap/client": { + "optional": true + }, "react": { "optional": false }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index edd3eb462e..1b53c44625 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1652,12 +1652,6 @@ importers: packages/react-query: dependencies: - '@sushiswap/client': - specifier: workspace:* - version: link:../client - '@sushiswap/database': - specifier: workspace:* - version: link:../database '@sushiswap/dexie': specifier: workspace:* version: link:../dexie @@ -1686,6 +1680,12 @@ importers: '@sentry/nextjs': specifier: 7.101.1 version: 7.101.1(next@14.1.0)(react@18.2.0) + '@sushiswap/client': + specifier: workspace:* + version: link:../client + '@sushiswap/database': + specifier: workspace:* + version: link:../database '@sushiswap/jest-config': specifier: workspace:* version: link:../../config/jest @@ -1882,6 +1882,8 @@ importers: specifier: 3.21.4 version: 3.21.4 + packages/sushi/dist/_cjs: {} + packages/ui: dependencies: '@fontsource-variable/inter': @@ -23474,7 +23476,7 @@ packages: requiresBuild: true dependencies: bn.js: 4.12.0 - elliptic: 6.5.4 + elliptic: 6.5.5 optional: true /create-hash@1.2.0: @@ -25003,7 +25005,6 @@ packages: inherits: 2.0.4 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - optional: true /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -26639,7 +26640,7 @@ packages: deprecated: Deprecated in favor of '@metamask/eth-sig-util' dependencies: buffer: 5.7.1 - elliptic: 6.5.4 + elliptic: 6.5.5 ethereumjs-abi: 0.6.5 ethereumjs-util: 5.2.1 tweetnacl: 1.0.3 @@ -26816,7 +26817,7 @@ packages: dependencies: bn.js: 4.12.0 create-hash: 1.2.0 - elliptic: 6.5.4 + elliptic: 6.5.5 ethereum-cryptography: 0.1.3 rlp: 2.2.7 @@ -26825,7 +26826,7 @@ packages: dependencies: bn.js: 4.12.0 create-hash: 1.2.0 - elliptic: 6.5.4 + elliptic: 6.5.5 ethereum-cryptography: 0.1.3 ethjs-util: 0.1.6 rlp: 2.2.7 @@ -40263,12 +40264,12 @@ packages: resolution: {integrity: sha512-PBdZ03m1kBnQ5cjjO0ZvJMJS+QsbyIcFwi4hY4U76OQsCO9JrOYjbCFgIF76ccFg9xnJo7ZHPkqyj1GqmdS7MA==} dependencies: call-bind: 1.0.7 - define-properties: 1.2.0 + define-properties: 1.2.1 for-each: 0.3.3 has-proto: 1.0.3 has-symbols: 1.0.3 object.getownpropertydescriptors: 2.1.6 - safe-array-concat: 1.0.0 + safe-array-concat: 1.1.2 /util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} From 14daee50d4989a01238315177cd98d13d5b23946 Mon Sep 17 00:00:00 2001 From: matthewlilley Date: Tue, 30 Apr 2024 10:09:26 +0000 Subject: [PATCH 18/31] chore: format --- packages/react-query/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-query/package.json b/packages/react-query/package.json index 0b2ee29ebe..2235894d7a 100644 --- a/packages/react-query/package.json +++ b/packages/react-query/package.json @@ -54,9 +54,9 @@ }, "devDependencies": { "@sentry/nextjs": "7.101.1", - "@sushiswap/jest-config": "workspace:*", "@sushiswap/client": "workspace:*", "@sushiswap/database": "workspace:*", + "@sushiswap/jest-config": "workspace:*", "@tsconfig/esm": "1.0.4", "@tsconfig/strictest": "2.0.2", "@types/ms": "0.7.34", From ea4235fd1640f57665eafec3fa2ecb6b3f331605 Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xMasayoshi@protonmail.com> Date: Tue, 30 Apr 2024 17:48:42 +0700 Subject: [PATCH 19/31] chore: create StorageKey enums for slippage & ttl --- .../evm/src/lib/hooks/useSlippageTolerance.ts | 4 ++-- .../AddSectionReviewModalConcentrated.tsx | 11 ++++++---- .../ui/pool/AddSectionReviewModalLegacy.tsx | 11 ++++++---- .../ui/pool/AddSectionReviewModalTrident.tsx | 5 ++++- apps/evm/src/ui/pool/AddSectionWidget.tsx | 5 +++-- .../ConcentratedLiquidityRemoveWidget.tsx | 21 ++++++++++++------- apps/evm/src/ui/pool/MigrateTab.tsx | 13 ++++++++---- apps/evm/src/ui/pool/PositionView.tsx | 6 ++++-- apps/evm/src/ui/pool/RemoveSectionLegacy.tsx | 13 +++++++++--- apps/evm/src/ui/pool/RemoveSectionTrident.tsx | 6 ++++-- apps/evm/src/ui/pool/RemoveSectionWidget.tsx | 5 +++-- .../Add/SteerPositionAddReviewModal.tsx | 9 ++++++-- .../Remove/SteerPositionRemove.tsx | 12 ++++++++--- packages/hooks/src/useSlippageTolerance.ts | 17 ++++++++++----- packages/hooks/src/useTTL.ts | 9 ++++++-- .../components/settings/SlippageTolerance.tsx | 7 +++++-- .../settings/TransactionDeadline.tsx | 8 +++---- packages/ui/src/components/settings/index.tsx | 17 +++++++++------ .../utils/hooks/useTransactionDeadline.ts | 6 +++--- 19 files changed, 125 insertions(+), 60 deletions(-) diff --git a/apps/evm/src/lib/hooks/useSlippageTolerance.ts b/apps/evm/src/lib/hooks/useSlippageTolerance.ts index 9ca85d4ac8..40d04f7383 100644 --- a/apps/evm/src/lib/hooks/useSlippageTolerance.ts +++ b/apps/evm/src/lib/hooks/useSlippageTolerance.ts @@ -1,11 +1,11 @@ 'use client' -import { useLocalStorage } from '@sushiswap/hooks' +import { SlippageToleranceStorageKey, useLocalStorage } from '@sushiswap/hooks' import { useMemo } from 'react' import { Percent } from 'sushi/math' export const useSlippageTolerance = ( - key: string | undefined = 'swapSlippage', + key: SlippageToleranceStorageKey = SlippageToleranceStorageKey.Swap, ) => { const [slippageTolerance, setSlippageTolerance] = useLocalStorage< number | string diff --git a/apps/evm/src/ui/pool/AddSectionReviewModalConcentrated.tsx b/apps/evm/src/ui/pool/AddSectionReviewModalConcentrated.tsx index a1570a906a..b66b9e1831 100644 --- a/apps/evm/src/ui/pool/AddSectionReviewModalConcentrated.tsx +++ b/apps/evm/src/ui/pool/AddSectionReviewModalConcentrated.tsx @@ -1,4 +1,5 @@ import { CogIcon } from '@heroicons/react-v1/outline' +import { SlippageToleranceStorageKey, TTLStorageKey } from '@sushiswap/hooks' import { Button, Currency, @@ -79,10 +80,12 @@ export const AddSectionReviewModalConcentrated: FC< }) => { const { address, chain } = useAccount() const { data: deadline } = useTransactionDeadline({ - storageKey: 'addLiquidity', + storageKey: TTLStorageKey.AddLiquidity, chainId, }) - const [slippageTolerance] = useSlippageTolerance('addLiquidity') + const [slippageTolerance] = useSlippageTolerance( + SlippageToleranceStorageKey.AddLiquidity, + ) const { [Bound.LOWER]: priceLower, [Bound.UPPER]: priceUpper } = pricesAtTicks const client = usePublicClient() @@ -265,12 +268,12 @@ export const AddSectionReviewModalConcentrated: FC< = onSuccess: _onSuccess, }) => { const { data: deadline } = useTransactionDeadline({ - storageKey: 'addLiquidity', + storageKey: TTLStorageKey.AddLiquidity, chainId, }) const { address } = useAccount() const { approved } = useApproved(APPROVE_TAG_ADD_LEGACY) - const [slippageTolerance] = useSlippageTolerance('addLiquidity') + const [slippageTolerance] = useSlippageTolerance( + SlippageToleranceStorageKey.AddLiquidity, + ) const client = usePublicClient() const onSuccess = useCallback( @@ -407,12 +410,12 @@ export const AddSectionReviewModalLegacy: FC = [token0, token1], [token0, token1]) const { data: rebases } = useBentoBoxTotals({ chainId, currencies: tokens }) const contract = useTridentRouterContract(chainId) - const [slippageTolerance] = useSlippageTolerance('addLiquidity') + const [slippageTolerance] = useSlippageTolerance( + SlippageToleranceStorageKey.AddLiquidity, + ) const [minAmount0, minAmount1] = useMemo(() => { return [ input0 diff --git a/apps/evm/src/ui/pool/AddSectionWidget.tsx b/apps/evm/src/ui/pool/AddSectionWidget.tsx index 57738b2316..ed21f098a6 100644 --- a/apps/evm/src/ui/pool/AddSectionWidget.tsx +++ b/apps/evm/src/ui/pool/AddSectionWidget.tsx @@ -1,5 +1,6 @@ import { CogIcon } from '@heroicons/react-v1/outline' import { PlusIcon } from '@heroicons/react-v1/solid' +import { SlippageToleranceStorageKey, TTLStorageKey } from '@sushiswap/hooks' import { WidgetAction, WidgetDescription, @@ -55,12 +56,12 @@ export const AddSectionWidget: FC = ({ ('0') - const [slippageTolerance] = useSlippageTolerance('removeLiquidity') + const [slippageTolerance] = useSlippageTolerance( + SlippageToleranceStorageKey.RemoveLiquidity, + ) const { data: deadline } = useTransactionDeadline({ - storageKey: 'removeLiquidity', + storageKey: TTLStorageKey.RemoveLiquidity, chainId, }) const debouncedValue = useDebounce(value, 300) @@ -338,12 +344,13 @@ export const ConcentratedLiquidityRemoveWidget: FC< = withCheckerRoot(({ pool }) => { ) const [invertPrice, setInvertPrice] = useState(false) const [invertTokens, setInvertTokens] = useState(false) - const [slippageTolerance] = useSlippageTolerance('addLiquidity') + const [slippageTolerance] = useSlippageTolerance( + SlippageToleranceStorageKey.AddLiquidity, + ) const { data: { token0: _token0, token1: _token1, liquidityToken }, @@ -464,7 +467,7 @@ export const MigrateTab: FC<{ pool: Pool }> = withCheckerRoot(({ pool }) => { const { approved: approvedMigrate } = useApproved(APPROVE_TAG_MIGRATE) const { data: deadline } = useTransactionDeadline({ - storageKey: 'addLiquidity', + storageKey: TTLStorageKey.AddLiquidity, chainId: pool.chainId as ChainId, }) @@ -731,12 +734,14 @@ export const MigrateTab: FC<{ pool: Pool }> = withCheckerRoot(({ pool }) => { = ({ id }) => { = const client = usePublicClient() const { address, chain } = useAccount() const { data: deadline } = useTransactionDeadline({ - storageKey: 'removeLiquidity', + storageKey: TTLStorageKey.RemoveLiquidity, chainId: _pool.chainId as ChainId, }) const contract = useSushiSwapRouterContract( _pool.chainId as SushiSwapV2ChainId, ) - const [slippageTolerance] = useSlippageTolerance('removeLiquidity') + const [slippageTolerance] = useSlippageTolerance( + SlippageToleranceStorageKey.RemoveLiquidity, + ) const [percentage, setPercentage] = useState('0') const percentToRemove = useMemo( diff --git a/apps/evm/src/ui/pool/RemoveSectionTrident.tsx b/apps/evm/src/ui/pool/RemoveSectionTrident.tsx index 304c196660..ad2e0cc2e5 100644 --- a/apps/evm/src/ui/pool/RemoveSectionTrident.tsx +++ b/apps/evm/src/ui/pool/RemoveSectionTrident.tsx @@ -1,7 +1,7 @@ 'use client' import { Pool, Protocol } from '@sushiswap/client' -import { useIsMounted } from '@sushiswap/hooks' +import { SlippageToleranceStorageKey, useIsMounted } from '@sushiswap/hooks' import { Button } from '@sushiswap/ui/components/button' import { Dots } from '@sushiswap/ui/components/dots' import { createToast } from '@sushiswap/ui/components/toast' @@ -67,7 +67,9 @@ export const RemoveSectionTrident: FC = const { signature } = useSignature(APPROVE_TAG_REMOVE_TRIDENT) const { setSignature } = useApprovedActions(APPROVE_TAG_REMOVE_TRIDENT) const contract = useTridentRouterContract(_pool.chainId as TridentChainId) - const [slippageTolerance] = useSlippageTolerance('removeLiquidity') + const [slippageTolerance] = useSlippageTolerance( + SlippageToleranceStorageKey.RemoveLiquidity, + ) const [percentage, setPercentage] = useState('0') const percentToRemove = useMemo( diff --git a/apps/evm/src/ui/pool/RemoveSectionWidget.tsx b/apps/evm/src/ui/pool/RemoveSectionWidget.tsx index 8bcdbe76ff..61a69612fb 100644 --- a/apps/evm/src/ui/pool/RemoveSectionWidget.tsx +++ b/apps/evm/src/ui/pool/RemoveSectionWidget.tsx @@ -27,6 +27,7 @@ import { ChainId } from 'sushi/chain' import { Amount, Type } from 'sushi/currency' import { ZERO } from 'sushi/math' +import { SlippageToleranceStorageKey, TTLStorageKey } from '@sushiswap/hooks' import { getDefaultTTL } from '@sushiswap/wagmi' import { usePoolPosition } from './PoolPositionProvider' @@ -63,12 +64,12 @@ export const RemoveSectionWidget: FC = ({ = const client = usePublicClient() const { address, chain } = useAccount() - const [slippageTolerance] = useSlippageTolerance('addSteerLiquidity') + const [slippageTolerance] = useSlippageTolerance( + SlippageToleranceStorageKey.AddSteerLiquidity, + ) const { approved } = useApproved(APPROVE_TAG_STEER) const { data: accountPosition } = useSteerAccountPosition({ diff --git a/apps/evm/src/ui/pool/Steer/SteerLiquidityManagement/Remove/SteerPositionRemove.tsx b/apps/evm/src/ui/pool/Steer/SteerLiquidityManagement/Remove/SteerPositionRemove.tsx index fcc4dd405d..66ab09a73d 100644 --- a/apps/evm/src/ui/pool/Steer/SteerLiquidityManagement/Remove/SteerPositionRemove.tsx +++ b/apps/evm/src/ui/pool/Steer/SteerLiquidityManagement/Remove/SteerPositionRemove.tsx @@ -2,7 +2,11 @@ import { CogIcon } from '@heroicons/react-v1/solid' import { SteerVault } from '@sushiswap/client' -import { useDebounce, useSlippageTolerance } from '@sushiswap/hooks' +import { + SlippageToleranceStorageKey, + useDebounce, + useSlippageTolerance, +} from '@sushiswap/hooks' import { isSteerChainId } from '@sushiswap/steer-sdk' import { steerMultiPositionManager } from '@sushiswap/steer-sdk/abi' import { @@ -50,7 +54,9 @@ export const SteerPositionRemove: FC = ({ const client = usePublicClient() const { address: account, chain } = useAccount() const [value, setValue] = useState('0') - const [slippageTolerance] = useSlippageTolerance('removeSteerLiquidity') + const [slippageTolerance] = useSlippageTolerance( + SlippageToleranceStorageKey.RemoveSteerLiquidity, + ) const debouncedValue = useDebounce(value, 300) const slippagePercent = useMemo(() => { @@ -241,7 +247,7 @@ export const SteerPositionRemove: FC = ({ - useLocalStorage( - key ? `${key}-slippage` : 'swapSlippage', - defaultValue || '0.1', - ) +export enum SlippageToleranceStorageKey { + Swap = 'slippage-swap', + AddLiquidity = 'slippage-add-liquidity', + RemoveLiquidity = 'slippage-remove-liquidity', + AddSteerLiquidity = 'slippage-add-steer-liquidity', + RemoveSteerLiquidity = 'slippage-remove-steer-liquidity', +} + +export const useSlippageTolerance = ( + key: SlippageToleranceStorageKey = SlippageToleranceStorageKey.Swap, + defaultValue?: string, +) => useLocalStorage(key, defaultValue || '0.1') diff --git a/packages/hooks/src/useTTL.ts b/packages/hooks/src/useTTL.ts index f99232d29a..4072f9ae7b 100644 --- a/packages/hooks/src/useTTL.ts +++ b/packages/hooks/src/useTTL.ts @@ -1,5 +1,10 @@ import { useLocalStorage } from './useLocalStorage' -export const useTTL = (key?: string) => { - return useLocalStorage(key ? `${key}-ttl` : 'ttl', 0) +export enum TTLStorageKey { + AddLiquidity = 'ttl-add-liquidity', + RemoveLiquidity = 'ttl-remove-liquidity', +} + +export const useTTL = (key: TTLStorageKey) => { + return useLocalStorage(key, 0) } diff --git a/packages/ui/src/components/settings/SlippageTolerance.tsx b/packages/ui/src/components/settings/SlippageTolerance.tsx index ec95f97945..67047cf716 100644 --- a/packages/ui/src/components/settings/SlippageTolerance.tsx +++ b/packages/ui/src/components/settings/SlippageTolerance.tsx @@ -1,6 +1,9 @@ import { RadioGroup } from '@headlessui/react' import { InformationCircleIcon } from '@heroicons/react/20/solid' -import { useSlippageTolerance } from '@sushiswap/hooks' +import { + SlippageToleranceStorageKey, + useSlippageTolerance, +} from '@sushiswap/hooks' import classNames from 'classnames' import React, { FC, useCallback } from 'react' @@ -23,7 +26,7 @@ const TABS = ['0.1', '0.5', '1.0'] export const SlippageTolerance: FC<{ options?: { - storageKey?: string + storageKey?: SlippageToleranceStorageKey defaultValue?: string title?: string } diff --git a/packages/ui/src/components/settings/TransactionDeadline.tsx b/packages/ui/src/components/settings/TransactionDeadline.tsx index 9d7e2b1c72..f08e01ddf5 100644 --- a/packages/ui/src/components/settings/TransactionDeadline.tsx +++ b/packages/ui/src/components/settings/TransactionDeadline.tsx @@ -1,5 +1,5 @@ import { InformationCircleIcon } from '@heroicons/react/20/solid' -import { useTTL } from '@sushiswap/hooks' +import { TTLStorageKey, useTTL } from '@sushiswap/hooks' import classNames from 'classnames' import React, { FC, useCallback } from 'react' @@ -14,14 +14,14 @@ import { Label } from '../label' import { TextField } from '../text-field' export const TransactionDeadline: FC<{ - options?: { - storageKey?: string + options: { + storageKey: TTLStorageKey defaultValue?: string title?: string } className?: string }> = ({ options, className }) => { - const [ttl, setTTL] = useTTL(options?.storageKey) + const [ttl, setTTL] = useTTL(options.storageKey) const onChange = useCallback( (value: string) => { diff --git a/packages/ui/src/components/settings/index.tsx b/packages/ui/src/components/settings/index.tsx index 4ac0139dd7..a84319e1c7 100644 --- a/packages/ui/src/components/settings/index.tsx +++ b/packages/ui/src/components/settings/index.tsx @@ -1,7 +1,11 @@ 'use client' import { Cog6ToothIcon, XMarkIcon } from '@heroicons/react/24/outline' -import { useSlippageTolerance } from '@sushiswap/hooks' +import { + SlippageToleranceStorageKey, + TTLStorageKey, + useSlippageTolerance, +} from '@sushiswap/hooks' import React, { FC, ReactNode, useState } from 'react' import { Button } from '../button' @@ -39,12 +43,12 @@ interface SettingsOverlayProps { externalModules?: FC[] options?: { slippageTolerance?: { - storageKey?: string + storageKey?: SlippageToleranceStorageKey defaultValue?: string title?: string } transactionDeadline?: { - storageKey?: string + storageKey: TTLStorageKey defaultValue?: string title?: string } @@ -125,9 +129,10 @@ export const SettingsOverlay: FC = ({ {modules.includes(SettingsModule.CarbonOffset) && ( )} - {modules.includes(SettingsModule.TransactionDeadline) && ( - - )} + {modules.includes(SettingsModule.TransactionDeadline) && + options?.transactionDeadline && ( + + )}
{externalModules?.map((Module, index) => ( diff --git a/packages/wagmi/src/hooks/utils/hooks/useTransactionDeadline.ts b/packages/wagmi/src/hooks/utils/hooks/useTransactionDeadline.ts index 4520911c16..dce1c96283 100644 --- a/packages/wagmi/src/hooks/utils/hooks/useTransactionDeadline.ts +++ b/packages/wagmi/src/hooks/utils/hooks/useTransactionDeadline.ts @@ -1,4 +1,4 @@ -import { useTTL } from '@sushiswap/hooks' +import { TTLStorageKey, useTTL } from '@sushiswap/hooks' import { useQuery } from '@tanstack/react-query' import { ChainId, chainsL2 } from 'sushi/chain' import { useCurrentBlockTimestamp } from '../../block' @@ -13,13 +13,13 @@ export const getDefaultTTL = (chainId: ChainId) => { interface UseTransactionDeadline { chainId: ChainId enabled?: boolean - storageKey?: string + storageKey: TTLStorageKey } export const useTransactionDeadline = ({ chainId, enabled = true, - storageKey = 'ttl', + storageKey, }: UseTransactionDeadline) => { const { data: currentBlockTimestampQuery } = useCurrentBlockTimestamp( chainId, From e6f092fda787ed76882ff0300260ce2d6324a5f0 Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xMasayoshi@protonmail.com> Date: Fri, 3 May 2024 05:40:43 +0700 Subject: [PATCH 20/31] chore: replace FormattedPrice with FormattedNumber and handle large numbers --- apps/evm/src/ui/pool/PositionView.tsx | 8 +-- apps/evm/src/ui/pool/PriceRangeCell.tsx | 8 +-- .../SteerStrategies/SteerBaseStrategy.tsx | 6 +- apps/evm/src/ui/pool/columns.tsx | 67 ++++++++++++++----- .../src/ui/swap/simple/simple-swap-header.tsx | 4 +- packages/sushi/src/format/number.ts | 19 +++++- ...rmatted-price.tsx => formatted-number.tsx} | 10 +-- packages/ui/src/components/index.ts | 2 +- 8 files changed, 87 insertions(+), 37 deletions(-) rename packages/ui/src/components/{formatted-price.tsx => formatted-number.tsx} (78%) diff --git a/apps/evm/src/ui/pool/PositionView.tsx b/apps/evm/src/ui/pool/PositionView.tsx index a1d95921aa..39ed693143 100644 --- a/apps/evm/src/ui/pool/PositionView.tsx +++ b/apps/evm/src/ui/pool/PositionView.tsx @@ -31,7 +31,7 @@ import { classNames, } from '@sushiswap/ui' import { Button } from '@sushiswap/ui/components/button' -import { FormattedPrice } from '@sushiswap/ui/components/formatted-price' +import { FormattedNumber } from '@sushiswap/ui/components/formatted-number' import { SkeletonText } from '@sushiswap/ui/components/skeleton' import { getDefaultTTL, @@ -478,7 +478,7 @@ const Component: FC<{ id: string }> = ({ id }) => { title={ <> 1 {unwrapToken(currencyBase)?.symbol} ={' '} - = ({ id }) => { '0' ) : ( <> - = ({ id }) => { '∞' ) : ( <> - > = {fullRange ? ( '0' ) : ( - > = {fullRange ? ( '∞' ) : ( - > =
Current:{' '} - Minimum price - {' '} + {' '} {vault.token0.symbol}/{vault.token1.symbol} Maximum price - {' '} + {' '} {vault.token0.symbol}/{vault.token1.symbol} diff --git a/apps/evm/src/ui/pool/columns.tsx b/apps/evm/src/ui/pool/columns.tsx index 1cbc41544f..059819c2c4 100644 --- a/apps/evm/src/ui/pool/columns.tsx +++ b/apps/evm/src/ui/pool/columns.tsx @@ -1,6 +1,7 @@ import { Pool, Protocol } from '@sushiswap/client' import { AngleRewardsPool } from '@sushiswap/react-query' import { + FormattedNumber, NetworkIcon, Tooltip, TooltipContent, @@ -485,14 +486,20 @@ export const TX_AMOUNT_IN_V2_COLUMN = ( cell: ({ row }) => { switch (row.original.type) { case TransactionType.Swap: - return `${row.original.amountIn.toPrecision(2)} ${ - row.original.tokenIn.symbol - }` + return ( + + {' '} + {row.original.tokenIn.symbol} + + ) case TransactionType.Mint: case TransactionType.Burn: - return `${row.original.amount0.toPrecision(6)} ${ - row.original.pool.token0.symbol - }` + return ( + + {' '} + {row.original.pool.token0.symbol} + + ) } }, meta: { @@ -508,14 +515,22 @@ export const TX_AMOUNT_OUT_V2_COLUMN = ( cell: ({ row }) => { switch (row.original.type) { case TransactionType.Swap: - return `${Math.abs(row.original.amountOut).toFixed(2)} ${ - row.original.tokenOut.symbol - }` + return ( + + {' '} + {row.original.tokenOut.symbol} + + ) case TransactionType.Mint: case TransactionType.Burn: - return `${row.original.amount1.toFixed(2)} ${ - row.original.pool.token1.symbol - }` + return ( + + {' '} + {row.original.pool.token1.symbol} + + ) } }, meta: { @@ -580,12 +595,22 @@ export const TX_AMOUNT_IN_V3_COLUMN = ( ? [row.pool.token0, row.pool.token1] : [row.pool.token1, row.pool.token0] - return `${Math.abs(amounts[0]).toPrecision(6)} ${tokens[0].symbol}` + return ( + + {' '} + {tokens[0].symbol} + + ) } case TransactionTypeV3.Mint: case TransactionTypeV3.Burn: case TransactionTypeV3.Collect: - return `${row.amount0.toPrecision(6)} ${row.pool.token0.symbol}` + return ( + + {' '} + {row.pool.token0.symbol} + + ) } }, meta: { @@ -611,12 +636,22 @@ export const TX_AMOUNT_OUT_V3_COLUMN = ( ? [row.pool.token0, row.pool.token1] : [row.pool.token1, row.pool.token0] - return `${Math.abs(amounts[1]).toFixed(2)} ${tokens[1].symbol}` + return ( + + {' '} + {tokens[1].symbol} + + ) } case TransactionTypeV3.Mint: case TransactionTypeV3.Burn: case TransactionTypeV3.Collect: - return `${row.amount1.toFixed(2)} ${row.pool.token1.symbol}` + return ( + + {' '} + {row.pool.token1.symbol} + + ) } }, meta: { diff --git a/apps/evm/src/ui/swap/simple/simple-swap-header.tsx b/apps/evm/src/ui/swap/simple/simple-swap-header.tsx index 4ebe23bf05..c5519cec74 100644 --- a/apps/evm/src/ui/swap/simple/simple-swap-header.tsx +++ b/apps/evm/src/ui/swap/simple/simple-swap-header.tsx @@ -4,7 +4,7 @@ import { ArrowTrendingUpIcon } from '@heroicons/react/20/solid' import { usePrices } from '@sushiswap/react-query' import { Button, - FormattedPrice, + FormattedNumber, SkeletonText, typographyVariants, } from '@sushiswap/ui' @@ -75,7 +75,7 @@ export const SimpleSwapHeader = () => { ({formatUSD(invert ? token0FiatPrice : token1FiatPrice)}) {' '} = - {' '} + {' '} {invert ? token1.symbol : token0.symbol}{' '} ({formatUSD(invert ? token1FiatPrice : token0FiatPrice)}) diff --git a/packages/sushi/src/format/number.ts b/packages/sushi/src/format/number.ts index d54ee469a0..fd3c7cc22e 100644 --- a/packages/sushi/src/format/number.ts +++ b/packages/sushi/src/format/number.ts @@ -1,7 +1,22 @@ import numeral from 'numeral' -export const formatNumber = (value: any) => { - return numeral(value).format('(0.00a)') +export const formatNumber = ( + value: number | string, + inputString = '0.[00]a', +) => { + if (typeof value === 'string') value = Number(value) + + let negative = false + if (value < 0) { + negative = true + value = Math.abs(value) + } + + if (value === 0) return '0.00' + if (value < 0.0001) return numeral(value).format('0.000000a') + if (value < 0.001) return numeral(value).format('0.0000a') + if (value < 0.01) return numeral(value).format('0.000a') + return `${negative ? '-' : ''}${numeral(value).format(inputString)}` } /** diff --git a/packages/ui/src/components/formatted-price.tsx b/packages/ui/src/components/formatted-number.tsx similarity index 78% rename from packages/ui/src/components/formatted-price.tsx rename to packages/ui/src/components/formatted-number.tsx index 5232394308..1585a803b2 100644 --- a/packages/ui/src/components/formatted-price.tsx +++ b/packages/ui/src/components/formatted-number.tsx @@ -1,11 +1,11 @@ import React from 'react' -import { withoutScientificNotation } from 'sushi' +import { formatNumber, withoutScientificNotation } from 'sushi' -interface FormattedPriceProps { +interface FormattedNumberProps { number: string | undefined } -const FormattedPrice: React.FC = ({ number }) => { +const FormattedNumber: React.FC = ({ number }) => { if (typeof number === 'undefined') return undefined const numberStr = withoutScientificNotation(number) @@ -28,7 +28,7 @@ const FormattedPrice: React.FC = ({ number }) => { zeroGroups[0].length === fractionalPart.length ) { // If no zero groups found or less than or equal to 4 zeros, return regular rendering - return {numberStr} + return {formatNumber(numberStr)} } const zeroCount = zeroGroups[0].length @@ -42,4 +42,4 @@ const FormattedPrice: React.FC = ({ number }) => { ) } -export { FormattedPrice } +export { FormattedNumber } diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 4ecf40307e..5ed9d3e9fc 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -23,7 +23,7 @@ export * from './dots' export * from './dropdown-menu' export * from './dropzone' export * from './explainer' -export * from './formatted-price' +export * from './formatted-number' export * from './form' export * from './global-footer' export * from './hover-card' From 32fe2cb97c4aba216a3a10df59cbbee45109b0b7 Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xMasayoshi@protonmail.com> Date: Fri, 3 May 2024 15:05:45 +0700 Subject: [PATCH 21/31] chore: replace numeral with numbro --- apps/evm/package.json | 1 - packages/graph-client/package.json | 1 - packages/sushi/package.json | 3 +-- packages/sushi/src/format/number.ts | 7 ++----- packages/sushi/src/format/percent.ts | 2 +- packages/sushi/src/format/price.ts | 2 +- pnpm-lock.yaml | 31 +++++++--------------------- 7 files changed, 12 insertions(+), 35 deletions(-) diff --git a/apps/evm/package.json b/apps/evm/package.json index f94437dcff..ff2656f74b 100644 --- a/apps/evm/package.json +++ b/apps/evm/package.json @@ -90,7 +90,6 @@ "@types/lodash.once": "4.1.9", "@types/lodash.zip": "4.2.9", "@types/node": "20", - "@types/numeral": "2.0.2", "@types/react": "18.2.14", "@types/react-dom": "18.2.6", "@types/react-slider": "1.3.1", diff --git a/packages/graph-client/package.json b/packages/graph-client/package.json index af55f31cf9..7a34af28b2 100644 --- a/packages/graph-client/package.json +++ b/packages/graph-client/package.json @@ -60,7 +60,6 @@ "@sushiswap/typescript-config": "workspace:*", "@types/jest": "29.5.5", "@types/node": "20", - "@types/numeral": "2.0.2", "@wagmi/core": "2.6.17", "graphql": "16.6.0", "jest": "29.7.0", diff --git a/packages/sushi/package.json b/packages/sushi/package.json index 2fa74f6ecf..25cd6a9be0 100644 --- a/packages/sushi/package.json +++ b/packages/sushi/package.json @@ -202,7 +202,7 @@ "date-fns": "3.3.1", "decimal.js-light": "2.5.1", "lodash.flatmap": "4.5.0", - "numeral": "2.0.6", + "numbro": "2.5.0", "seedrandom": "3.0.5", "serialijse": "0.3.0", "tiny-invariant": "1.3.1", @@ -216,7 +216,6 @@ "@tsconfig/strictest": "2.0.2", "@types/big.js": "6.2.0", "@types/lodash.flatmap": "4.5.9", - "@types/numeral": "2.0.3", "@types/seedrandom": "3.0.1", "@wagmi/core": "2.6.17", "ts-node": "10.9.2", diff --git a/packages/sushi/src/format/number.ts b/packages/sushi/src/format/number.ts index fd3c7cc22e..1b69946510 100644 --- a/packages/sushi/src/format/number.ts +++ b/packages/sushi/src/format/number.ts @@ -1,9 +1,6 @@ -import numeral from 'numeral' +import numeral from 'numbro' -export const formatNumber = ( - value: number | string, - inputString = '0.[00]a', -) => { +export const formatNumber = (value: number | string, inputString = '0.00a') => { if (typeof value === 'string') value = Number(value) let negative = false diff --git a/packages/sushi/src/format/percent.ts b/packages/sushi/src/format/percent.ts index a396009bee..bdcf5b3385 100644 --- a/packages/sushi/src/format/percent.ts +++ b/packages/sushi/src/format/percent.ts @@ -1,4 +1,4 @@ -import numeral from 'numeral' +import numeral from 'numbro' export const formatPercent = (value: any) => { let negative = false diff --git a/packages/sushi/src/format/price.ts b/packages/sushi/src/format/price.ts index 4c611d97be..02d13ec1cb 100644 --- a/packages/sushi/src/format/price.ts +++ b/packages/sushi/src/format/price.ts @@ -1,4 +1,4 @@ -import numeral from 'numeral' +import numeral from 'numbro' export const formatUSD = ( value: string | number, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b53c44625..eafb65cc1a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -708,9 +708,6 @@ importers: '@types/node': specifier: '20' version: 20.10.0 - '@types/numeral': - specifier: 2.0.2 - version: 2.0.2 '@types/react': specifier: 18.2.14 version: 18.2.14 @@ -1576,9 +1573,6 @@ importers: '@types/node': specifier: '20' version: 20.10.0 - '@types/numeral': - specifier: 2.0.2 - version: 2.0.2 '@wagmi/core': specifier: 2.6.17 version: 2.6.17(@types/react@18.2.14)(react@18.2.0)(typescript@5.2.2)(viem@2.8.14)(zod@3.21.4) @@ -1820,9 +1814,9 @@ importers: lodash.flatmap: specifier: 4.5.0 version: 4.5.0 - numeral: - specifier: 2.0.6 - version: 2.0.6 + numbro: + specifier: 2.5.0 + version: 2.5.0 seedrandom: specifier: 3.0.5 version: 3.0.5 @@ -1857,9 +1851,6 @@ importers: '@types/lodash.flatmap': specifier: 4.5.9 version: 4.5.9 - '@types/numeral': - specifier: 2.0.3 - version: 2.0.3 '@types/seedrandom': specifier: 3.0.1 version: 3.0.1 @@ -1882,8 +1873,6 @@ importers: specifier: 3.21.4 version: 3.21.4 - packages/sushi/dist/_cjs: {} - packages/ui: dependencies: '@fontsource-variable/inter': @@ -18159,14 +18148,6 @@ packages: /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} - /@types/numeral@2.0.2: - resolution: {integrity: sha512-A8F30k2gYJ/6e07spSCPpkuZu79LCnkPTvqmIWQzNGcrzwFKpVOydG41lNt5wZXjSI149qjyzC2L1+F2PD/NUA==} - dev: true - - /@types/numeral@2.0.3: - resolution: {integrity: sha512-37VDYvFbx/I+Ve1kwcDUq5+qvgNl+uy9YUZhBMJ6YWHBtr2+d6CAgdn4R81NqBlZbTpvLxJOTUhwbki7+90+qQ==} - dev: true - /@types/parse-json@4.0.2: resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -33740,8 +33721,10 @@ packages: bn.js: 4.11.6 strip-hex-prefix: 1.0.0 - /numeral@2.0.6: - resolution: {integrity: sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==} + /numbro@2.5.0: + resolution: {integrity: sha512-xDcctDimhzko/e+y+Q2/8i3qNC9Svw1QgOkSkQoO0kIPI473tR9QRbo2KP88Ty9p8WbPy+3OpTaAIzehtuHq+A==} + dependencies: + bignumber.js: 9.1.1 dev: false /nwsapi@2.2.7: From e5a23b276e78409d4764a03074ba2cad8745acc5 Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xMasayoshi@protonmail.com> Date: Fri, 3 May 2024 16:42:29 +0700 Subject: [PATCH 22/31] fix: build --- packages/sushi/src/format/number.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sushi/src/format/number.ts b/packages/sushi/src/format/number.ts index 1b69946510..9736578c52 100644 --- a/packages/sushi/src/format/number.ts +++ b/packages/sushi/src/format/number.ts @@ -1,6 +1,6 @@ import numeral from 'numbro' -export const formatNumber = (value: number | string, inputString = '0.00a') => { +export const formatNumber = (value: any, inputString = '0.00a') => { if (typeof value === 'string') value = Number(value) let negative = false From 1db3f556abdf53e886c69aedf93a5410d3991a9a Mon Sep 17 00:00:00 2001 From: 0xMasayoshi <0xMasayoshi@protonmail.com> Date: Fri, 3 May 2024 18:50:17 +0700 Subject: [PATCH 23/31] fix: formatNumber format str --- packages/sushi/src/format/number.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/sushi/src/format/number.ts b/packages/sushi/src/format/number.ts index 9736578c52..6713155398 100644 --- a/packages/sushi/src/format/number.ts +++ b/packages/sushi/src/format/number.ts @@ -1,6 +1,6 @@ import numeral from 'numbro' -export const formatNumber = (value: any, inputString = '0.00a') => { +export const formatNumber = (value: any, inputString = '0.[00]a') => { if (typeof value === 'string') value = Number(value) let negative = false @@ -10,9 +10,9 @@ export const formatNumber = (value: any, inputString = '0.00a') => { } if (value === 0) return '0.00' - if (value < 0.0001) return numeral(value).format('0.000000a') - if (value < 0.001) return numeral(value).format('0.0000a') - if (value < 0.01) return numeral(value).format('0.000a') + if (value < 0.0001) return numeral(value).format('0.[000000]a') + if (value < 0.001) return numeral(value).format('0.[0000]a') + if (value < 0.01) return numeral(value).format('0.[000]a') return `${negative ? '-' : ''}${numeral(value).format(inputString)}` } From c15beab80b814133de280c2b1c6aa6808f4ba5f3 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Sat, 4 May 2024 20:59:59 +0000 Subject: [PATCH 24/31] init --- flake.lock | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 22 ++++ 2 files changed, 322 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..89f23ddbe0 --- /dev/null +++ b/flake.lock @@ -0,0 +1,300 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_3": { + "locked": { + "lastModified": 1644229661, + "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_4": { + "inputs": { + "systems": "systems_3" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_5": { + "inputs": { + "systems": "systems_4" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "foundry": { + "inputs": { + "flake-utils": "flake-utils_3", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1714727549, + "narHash": "sha256-CWXRTxxcgMfQubJugpeg3yVWIfm70MYTtgaKWKgD60U=", + "owner": "shazow", + "repo": "foundry.nix", + "rev": "47cf189ec395eda4b3e0623179d1075c8027ca97", + "type": "github" + }, + "original": { + "owner": "shazow", + "ref": "monthly", + "repo": "foundry.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1666753130, + "narHash": "sha256-Wff1dGPFSneXJLI2c0kkdWTgxnQ416KE6X4KnFkgPYQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f540aeda6f677354f1e7144ab04352f61aaa0118", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1714764285, + "narHash": "sha256-oeevp27kMeDjKdxaTyXyS14TLjJrpJhXp7UVEUdYqYs=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "d121ce778b2609ae9e749a711295ffca013a82c4", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1706487304, + "narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "90f456026d284c22b3e3497be980b2e47d0b28ac", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1708564076, + "narHash": "sha256-KKkqoxlgx9n3nwST7O2kM8tliDOijiSSNaWuSkiozdQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "98b00b6947a9214381112bdb6f89c25498db4959", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "rainix": { + "inputs": { + "flake-utils": "flake-utils_2", + "foundry": "foundry", + "nixpkgs": "nixpkgs_2", + "rust-overlay": "rust-overlay", + "solc": "solc" + }, + "locked": { + "lastModified": 1714764843, + "narHash": "sha256-7ae0pHvjlInc1V3QxjW5iNhXAYerO1/fhLgzQnPNPjE=", + "owner": "rainprotocol", + "repo": "rainix", + "rev": "f3bdb28f353f8cba28b2bba86f79c362dd65ee53", + "type": "github" + }, + "original": { + "owner": "rainprotocol", + "repo": "rainix", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "rainix": "rainix" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils_4", + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1714702555, + "narHash": "sha256-/NoUbE5S5xpK1FU3nlHhQ/tL126+JcisXdzy3Ng4pDU=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "7f0e3ef7b7fbed78e12e5100851175d28af4b7c6", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "solc": { + "inputs": { + "flake-utils": "flake-utils_5", + "nixpkgs": "nixpkgs_4" + }, + "locked": { + "lastModified": 1711538161, + "narHash": "sha256-rETVdEIQ2PyEcNgzXXFSiYAYl0koCeGDIWp9XYBTxoQ=", + "owner": "hellwolf", + "repo": "solc.nix", + "rev": "a995838545a7383a0b37776e969743b1346d5479", + "type": "github" + }, + "original": { + "owner": "hellwolf", + "repo": "solc.nix", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_4": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..b58bce3335 --- /dev/null +++ b/flake.nix @@ -0,0 +1,22 @@ +{ + inputs = { + flake-utils.url = "github:numtide/flake-utils"; + rainix.url = "github:rainprotocol/rainix"; + }; + + outputs = { self, flake-utils, rainix }: + + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = rainix.pkgs.${system}; + in { + # For `nix develop`: + devShell = pkgs.mkShell { + nativeBuildInputs = [ + rainix.node-build-inputs.${system} + pkgs.nodePackages.pnpm + ]; + }; + } + ); +} \ No newline at end of file From c958637dc07091672621f05c6e2bb93d5d0503d5 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Sat, 4 May 2024 21:41:21 +0000 Subject: [PATCH 25/31] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6d643bcb16..e4fbc713b3 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "packageManager": "pnpm@8.15.3", "engines": { "node": ">=20.x", - "pnpm": "8.15.3" + "pnpm": ">=8.15.3" }, "pnpm": { "overrides": { From 41e108633d6fce4ab273a7f23f6b90d8670c542a Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Sat, 4 May 2024 21:52:22 +0000 Subject: [PATCH 26/31] Update pnpm-lock.yaml --- pnpm-lock.yaml | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0bad597063..30a918a596 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1814,6 +1814,9 @@ importers: lodash.flatmap: specifier: 4.5.0 version: 4.5.0 + memoize-fs: + specifier: github:rouzwelt/memoize-fs#e5fcc9f6effc4ad087514372a53a49d380520ad5 + version: github.com/rouzwelt/memoize-fs/e5fcc9f6effc4ad087514372a53a49d380520ad5 numbro: specifier: 2.5.0 version: 2.5.0 @@ -14979,7 +14982,6 @@ packages: estree-walker: 2.0.2 picomatch: 2.3.1 rollup: 3.29.4 - dev: false /@rushstack/eslint-patch@1.3.3: resolution: {integrity: sha512-0xd7qez0AQ+MbHatZTlI1gu5vkG8r7MYRUJAHPAHJBmGLs16zpkrpAVLvjQKQOqaXPDUBwOiJzNc00znHSCVBw==} @@ -16889,7 +16891,7 @@ packages: vite: ^3.0.0 || ^4.0.0 dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.2.2)(vite@4.3.9) - '@rollup/pluginutils': 5.0.4(rollup@2.78.0) + '@rollup/pluginutils': 5.0.4(rollup@3.29.4) '@storybook/builder-vite': 7.4.6(typescript@5.2.2)(vite@4.3.9) '@storybook/react': 7.4.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) '@vitejs/plugin-react': 3.1.0(vite@4.3.9) @@ -32185,6 +32187,11 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + /meriyah@4.4.2: + resolution: {integrity: sha512-fENZIbs4tscI3IGRGtPrCoW4H4oGzVQrQCVCGRv+92kFXKkvxr52ZNR684ICvDC/UBWg9ioGc2X6pMnWOtRYwA==} + engines: {node: '>=10.4.0'} + dev: false + /merkle-patricia-tree@2.3.2: resolution: {integrity: sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g==} dependencies: @@ -41300,6 +41307,7 @@ packages: /web3-provider-engine@14.2.1: resolution: {integrity: sha512-iSv31h2qXkr9vrL6UZDm4leZMc32SjWJFGOp/D92JXfcEboCqraZyuExDkpxKw8ziTufXieNM7LSXNHzszYdJw==} + deprecated: 'This package has been deprecated, see the README for details: https://github.com/MetaMask/web3-provider-engine' dependencies: async: 2.6.4 backoff: 2.5.0 @@ -42338,4 +42346,15 @@ packages: version: 0.6.8 dependencies: bn.js: 4.12.0 - ethereumjs-util: 6.2.1 \ No newline at end of file + ethereumjs-util: 6.2.1 + + github.com/rouzwelt/memoize-fs/e5fcc9f6effc4ad087514372a53a49d380520ad5: + resolution: {tarball: https://codeload.github.com/rouzwelt/memoize-fs/tar.gz/e5fcc9f6effc4ad087514372a53a49d380520ad5} + name: memoize-fs + version: 3.0.0 + engines: {node: '>= 10.13.0', npm: '>= 6.0.0'} + prepare: true + requiresBuild: true + dependencies: + meriyah: 4.4.2 + dev: false From 49e25a6ef3c9339d6d4a32874898a7c598fda820 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Sat, 4 May 2024 22:00:09 +0000 Subject: [PATCH 27/31] update --- packages/sushi/src/router/memoizer.test.ts | 53 ---------------------- packages/sushi/src/router/memoizer.ts | 34 -------------- 2 files changed, 87 deletions(-) delete mode 100644 packages/sushi/src/router/memoizer.test.ts delete mode 100644 packages/sushi/src/router/memoizer.ts diff --git a/packages/sushi/src/router/memoizer.test.ts b/packages/sushi/src/router/memoizer.test.ts deleted file mode 100644 index d1f9a07eaa..0000000000 --- a/packages/sushi/src/router/memoizer.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import fs from 'fs' -import { describe, expect, it } from 'vitest' -import { memoizer } from './memoizer.js' - -describe('Memoizer', async () => { - it('should serialize, memoize, read from cache, deserialize', async () => { - let didHitCacheOnce = false - const testFn = (value1: any) => { - didHitCacheOnce = !didHitCacheOnce - return { - ...value1, - someOtherValue: 'some data', - } - } - const testMemoizer = await memoizer.fn(testFn) - - const testValue = { - bigint: 12345n, - string: 'some text', - number: 123, - bool: true, - obj: { - prop: 'some prop', - }, - } - const noCacheHitReturnedValue = await testMemoizer(testValue) - const cacheHitReturnedValue = await testMemoizer(testValue) - const expectedReturnedValue = { - bigint: 12345n, - string: 'some text', - number: 123, - bool: true, - obj: { - prop: 'some prop', - }, - someOtherValue: 'some data', - } - - expect(didHitCacheOnce).toEqual(true) - expect(noCacheHitReturnedValue).toStrictEqual(expectedReturnedValue) - expect(cacheHitReturnedValue).toStrictEqual(expectedReturnedValue) - - // read cached file content - const cacheFileContent = fs.readFileSync( - `./mem-cache/${fs.readdirSync('./mem-cache')[0]}`, - { encoding: 'utf-8' }, - ) - const expectedCachedContent = - '{"data":{"bigint":"12345n","string":"some text","number":123,"bool":true,"obj":{"prop":"some prop"},"someOtherValue":"some data"}}' - - expect(cacheFileContent).toEqual(expectedCachedContent) - }) -}) diff --git a/packages/sushi/src/router/memoizer.ts b/packages/sushi/src/router/memoizer.ts deleted file mode 100644 index a8810d85f4..0000000000 --- a/packages/sushi/src/router/memoizer.ts +++ /dev/null @@ -1,34 +0,0 @@ -import memoize from 'memoize-fs' - -const serialize = (val: any) => { - const circRefColl: any[] = [] - return JSON.stringify(val, (_name, value) => { - if (typeof value === 'function') { - return // ignore arguments and attributes of type function silently - } - if (typeof value === 'object' && value !== null) { - if (circRefColl.indexOf(value) !== -1) { - // circular reference has been found, discard key - return - } - // store value in collection - circRefColl.push(value) - } - if (typeof value === 'bigint') return `${value.toString()}n` - return value - }) -} - -const deserialize = (val: string) => { - return JSON.parse(val, (_key, value) => { - if (typeof value === 'string' && /^\d+n$/.test(value)) { - return BigInt(value.slice(0, -1)) - } else return value - }).data -} - -export const memoizer = memoize({ - cachePath: './mem-cache', - serialize, - deserialize, -}) From a4594b0d803f5ef0a9c6d0036ca5882848c85163 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Sat, 4 May 2024 22:04:00 +0000 Subject: [PATCH 28/31] update --- flake.lock | 2 +- packages/sushi/package.json | 1 - pnpm-lock.yaml | 25 +++---------------------- 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/flake.lock b/flake.lock index 70761849f8..89f23ddbe0 100644 --- a/flake.lock +++ b/flake.lock @@ -297,4 +297,4 @@ }, "root": "root", "version": 7 -} \ No newline at end of file +} diff --git a/packages/sushi/package.json b/packages/sushi/package.json index 13d5264f0d..83d71a609b 100644 --- a/packages/sushi/package.json +++ b/packages/sushi/package.json @@ -202,7 +202,6 @@ "date-fns": "3.3.1", "decimal.js-light": "2.5.1", "lodash.flatmap": "4.5.0", - "memoize-fs": "github:rouzwelt/memoize-fs#e5fcc9f6effc4ad087514372a53a49d380520ad5", "numbro": "2.5.0", "seedrandom": "3.0.5", "serialijse": "0.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 30a918a596..0bad597063 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1814,9 +1814,6 @@ importers: lodash.flatmap: specifier: 4.5.0 version: 4.5.0 - memoize-fs: - specifier: github:rouzwelt/memoize-fs#e5fcc9f6effc4ad087514372a53a49d380520ad5 - version: github.com/rouzwelt/memoize-fs/e5fcc9f6effc4ad087514372a53a49d380520ad5 numbro: specifier: 2.5.0 version: 2.5.0 @@ -14982,6 +14979,7 @@ packages: estree-walker: 2.0.2 picomatch: 2.3.1 rollup: 3.29.4 + dev: false /@rushstack/eslint-patch@1.3.3: resolution: {integrity: sha512-0xd7qez0AQ+MbHatZTlI1gu5vkG8r7MYRUJAHPAHJBmGLs16zpkrpAVLvjQKQOqaXPDUBwOiJzNc00znHSCVBw==} @@ -16891,7 +16889,7 @@ packages: vite: ^3.0.0 || ^4.0.0 dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.2.2)(vite@4.3.9) - '@rollup/pluginutils': 5.0.4(rollup@3.29.4) + '@rollup/pluginutils': 5.0.4(rollup@2.78.0) '@storybook/builder-vite': 7.4.6(typescript@5.2.2)(vite@4.3.9) '@storybook/react': 7.4.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) '@vitejs/plugin-react': 3.1.0(vite@4.3.9) @@ -32187,11 +32185,6 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - /meriyah@4.4.2: - resolution: {integrity: sha512-fENZIbs4tscI3IGRGtPrCoW4H4oGzVQrQCVCGRv+92kFXKkvxr52ZNR684ICvDC/UBWg9ioGc2X6pMnWOtRYwA==} - engines: {node: '>=10.4.0'} - dev: false - /merkle-patricia-tree@2.3.2: resolution: {integrity: sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g==} dependencies: @@ -41307,7 +41300,6 @@ packages: /web3-provider-engine@14.2.1: resolution: {integrity: sha512-iSv31h2qXkr9vrL6UZDm4leZMc32SjWJFGOp/D92JXfcEboCqraZyuExDkpxKw8ziTufXieNM7LSXNHzszYdJw==} - deprecated: 'This package has been deprecated, see the README for details: https://github.com/MetaMask/web3-provider-engine' dependencies: async: 2.6.4 backoff: 2.5.0 @@ -42346,15 +42338,4 @@ packages: version: 0.6.8 dependencies: bn.js: 4.12.0 - ethereumjs-util: 6.2.1 - - github.com/rouzwelt/memoize-fs/e5fcc9f6effc4ad087514372a53a49d380520ad5: - resolution: {tarball: https://codeload.github.com/rouzwelt/memoize-fs/tar.gz/e5fcc9f6effc4ad087514372a53a49d380520ad5} - name: memoize-fs - version: 3.0.0 - engines: {node: '>= 10.13.0', npm: '>= 6.0.0'} - prepare: true - requiresBuild: true - dependencies: - meriyah: 4.4.2 - dev: false + ethereumjs-util: 6.2.1 \ No newline at end of file From 0ef79d9fc922d0b40d8755973ea4d93aca5b4009 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Sat, 4 May 2024 22:05:10 +0000 Subject: [PATCH 29/31] Update pnpm-lock.yaml --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0bad597063..eafb65cc1a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42338,4 +42338,4 @@ packages: version: 0.6.8 dependencies: bn.js: 4.12.0 - ethereumjs-util: 6.2.1 \ No newline at end of file + ethereumjs-util: 6.2.1 From f385170f998c8e095c464bfaa1f1e40faad85e0f Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Sat, 4 May 2024 22:15:31 +0000 Subject: [PATCH 30/31] init --- packages/sushi/package.json | 1 + packages/sushi/src/router/memoizer.test.ts | 53 ++++++++++++++++++++++ packages/sushi/src/router/memoizer.ts | 34 ++++++++++++++ pnpm-lock.yaml | 20 ++++++++ 4 files changed, 108 insertions(+) create mode 100644 packages/sushi/src/router/memoizer.test.ts create mode 100644 packages/sushi/src/router/memoizer.ts diff --git a/packages/sushi/package.json b/packages/sushi/package.json index 83d71a609b..13d5264f0d 100644 --- a/packages/sushi/package.json +++ b/packages/sushi/package.json @@ -202,6 +202,7 @@ "date-fns": "3.3.1", "decimal.js-light": "2.5.1", "lodash.flatmap": "4.5.0", + "memoize-fs": "github:rouzwelt/memoize-fs#e5fcc9f6effc4ad087514372a53a49d380520ad5", "numbro": "2.5.0", "seedrandom": "3.0.5", "serialijse": "0.3.0", diff --git a/packages/sushi/src/router/memoizer.test.ts b/packages/sushi/src/router/memoizer.test.ts new file mode 100644 index 0000000000..d1f9a07eaa --- /dev/null +++ b/packages/sushi/src/router/memoizer.test.ts @@ -0,0 +1,53 @@ +import fs from 'fs' +import { describe, expect, it } from 'vitest' +import { memoizer } from './memoizer.js' + +describe('Memoizer', async () => { + it('should serialize, memoize, read from cache, deserialize', async () => { + let didHitCacheOnce = false + const testFn = (value1: any) => { + didHitCacheOnce = !didHitCacheOnce + return { + ...value1, + someOtherValue: 'some data', + } + } + const testMemoizer = await memoizer.fn(testFn) + + const testValue = { + bigint: 12345n, + string: 'some text', + number: 123, + bool: true, + obj: { + prop: 'some prop', + }, + } + const noCacheHitReturnedValue = await testMemoizer(testValue) + const cacheHitReturnedValue = await testMemoizer(testValue) + const expectedReturnedValue = { + bigint: 12345n, + string: 'some text', + number: 123, + bool: true, + obj: { + prop: 'some prop', + }, + someOtherValue: 'some data', + } + + expect(didHitCacheOnce).toEqual(true) + expect(noCacheHitReturnedValue).toStrictEqual(expectedReturnedValue) + expect(cacheHitReturnedValue).toStrictEqual(expectedReturnedValue) + + // read cached file content + const cacheFileContent = fs.readFileSync( + `./mem-cache/${fs.readdirSync('./mem-cache')[0]}`, + { encoding: 'utf-8' }, + ) + const expectedCachedContent = + '{"data":{"bigint":"12345n","string":"some text","number":123,"bool":true,"obj":{"prop":"some prop"},"someOtherValue":"some data"}}' + + expect(cacheFileContent).toEqual(expectedCachedContent) + }) +}) diff --git a/packages/sushi/src/router/memoizer.ts b/packages/sushi/src/router/memoizer.ts new file mode 100644 index 0000000000..a8810d85f4 --- /dev/null +++ b/packages/sushi/src/router/memoizer.ts @@ -0,0 +1,34 @@ +import memoize from 'memoize-fs' + +const serialize = (val: any) => { + const circRefColl: any[] = [] + return JSON.stringify(val, (_name, value) => { + if (typeof value === 'function') { + return // ignore arguments and attributes of type function silently + } + if (typeof value === 'object' && value !== null) { + if (circRefColl.indexOf(value) !== -1) { + // circular reference has been found, discard key + return + } + // store value in collection + circRefColl.push(value) + } + if (typeof value === 'bigint') return `${value.toString()}n` + return value + }) +} + +const deserialize = (val: string) => { + return JSON.parse(val, (_key, value) => { + if (typeof value === 'string' && /^\d+n$/.test(value)) { + return BigInt(value.slice(0, -1)) + } else return value + }).data +} + +export const memoizer = memoize({ + cachePath: './mem-cache', + serialize, + deserialize, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eafb65cc1a..2988565ef7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1814,6 +1814,9 @@ importers: lodash.flatmap: specifier: 4.5.0 version: 4.5.0 + memoize-fs: + specifier: github:rouzwelt/memoize-fs#e5fcc9f6effc4ad087514372a53a49d380520ad5 + version: github.com/rouzwelt/memoize-fs/e5fcc9f6effc4ad087514372a53a49d380520ad5 numbro: specifier: 2.5.0 version: 2.5.0 @@ -32185,6 +32188,11 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + /meriyah@4.4.2: + resolution: {integrity: sha512-fENZIbs4tscI3IGRGtPrCoW4H4oGzVQrQCVCGRv+92kFXKkvxr52ZNR684ICvDC/UBWg9ioGc2X6pMnWOtRYwA==} + engines: {node: '>=10.4.0'} + dev: false + /merkle-patricia-tree@2.3.2: resolution: {integrity: sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g==} dependencies: @@ -41300,6 +41308,7 @@ packages: /web3-provider-engine@14.2.1: resolution: {integrity: sha512-iSv31h2qXkr9vrL6UZDm4leZMc32SjWJFGOp/D92JXfcEboCqraZyuExDkpxKw8ziTufXieNM7LSXNHzszYdJw==} + deprecated: 'This package has been deprecated, see the README for details: https://github.com/MetaMask/web3-provider-engine' dependencies: async: 2.6.4 backoff: 2.5.0 @@ -42339,3 +42348,14 @@ packages: dependencies: bn.js: 4.12.0 ethereumjs-util: 6.2.1 + + github.com/rouzwelt/memoize-fs/e5fcc9f6effc4ad087514372a53a49d380520ad5: + resolution: {tarball: https://codeload.github.com/rouzwelt/memoize-fs/tar.gz/e5fcc9f6effc4ad087514372a53a49d380520ad5} + name: memoize-fs + version: 3.0.0 + engines: {node: '>= 10.13.0', npm: '>= 6.0.0'} + prepare: true + requiresBuild: true + dependencies: + meriyah: 4.4.2 + dev: false From 1f3530d7bc53ec6997992bded151d101bb20f819 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Sun, 5 May 2024 20:30:14 +0000 Subject: [PATCH 31/31] Update report --- packages/sushi/test/tines/snapshots/report | 169 +++++++++++++++++++-- 1 file changed, 159 insertions(+), 10 deletions(-) diff --git a/packages/sushi/test/tines/snapshots/report b/packages/sushi/test/tines/snapshots/report index f8d28db60a..306557668b 100644 --- a/packages/sushi/test/tines/snapshots/report +++ b/packages/sushi/test/tines/snapshots/report @@ -1,42 +1,191 @@ 1:"bridge-1": 5528.94 -> 5528.94 (0%) avg:0% 2:"bridge-2": 7663 -> 7663 (0%) avg:0% -151:"top20-150": 0 -> 0 (0%) avg:0% -152:"top20-151": 481 -> 481 (0%) avg:0% 3:"bridge-3": 11953.94 -> 11953.94 (0%) avg:0% 4:"bridge-4": 5496.94 -> 5496.94 (0%) avg:0% 5:"bridge-5-1": 4260.964 -> 4260.964 (0%) avg:0% +2:"top20-1": 1.1195509365387262e+37 -> 1.1195509365387262e+37 (0%) avg:0% 6:"bridge-5-2": 5473.94 -> 5473.94 (0%) avg:0% 7:"bridge-5-3": 5501.94 -> 5501.94 (0%) avg:0% 8:"bridge-5-5": 5528.94 -> 5528.94 (0%) avg:0% 9:"bridge-5-10": 5528.94 -> 5528.94 (0%) avg:0% 10:"bridge-5-30": 5528.94 -> 5528.94 (0%) avg:0% 11:"bridge-5-100": 5528.94 -> 5528.94 (0%) avg:0% +3:"top20-2": 96249232830474440000 -> 96249232830474440000 (0%) avg:0% +4:"top20-3": 467011582838851800000 -> 467011582838851800000 (0%) avg:0% +5:"top20-4": 14263706377454.916 -> 14263706377454.916 (0%) avg:0% 12:"bridge-5-300": 5528.94 -> 5528.94 (0%) avg:0% -153:"top20-152": 0 -> 0 (0%) avg:0% -154:"top20-153": 1.3468577317796694e+32 -> 1.3468577317796694e+32 (0%) avg:0% +6:"top20-5": 111208992328025 -> 111208992328025 (0%) avg:0% +7:"top20-6": 184056193599 -> 184056193599 (0%) avg:0% 13:"bridge-5-1000": 5528.94 -> 5528.94 (0%) avg:0% -155:"top20-154": 7978807883992.964 -> 7978807883992.964 (0%) avg:0% +8:"top20-7": 1.949010266471403e+21 -> 1.949010266471403e+21 (0%) avg:0% 14:"bridge-6": 579.9911997249991 -> 579.9911997249991 (0%) avg:0% -156:"top20-155": 75460.9999999988 -> 75460.9999999988 (0%) avg:0% 15:"bridge-7-1": 567.9947198349995 -> 567.9947198349995 (0%) avg:0% 16:"bridge-7-2": 567.9947198349995 -> 567.9947198349995 (0%) avg:0% 17:"bridge-7-3": 569.9929597799993 -> 569.9929597799993 (0%) avg:0% -157:"top20-156": 1433531033857000200 -> 1433531033857000200 (0%) avg:0% +9:"top20-8": 5147698251 -> 5147698251 (0%) avg:0% 18:"bridge-7-5": 577.9911997249991 -> 577.9911997249991 (0%) avg:0% 19:"bridge-7-10": 579.9911997249991 -> 579.9911997249991 (0%) avg:0% 20:"bridge-7-30": 579.9911997249991 -> 579.9911997249991 (0%) avg:0% -158:"top20-157": 5729885219 -> 5729885219 (0%) avg:0% +10:"top20-9": 2.3146866045567974e+29 -> 2.3146866045567974e+29 (0%) avg:0% +11:"top20-10": 2.000939725061475e+25 -> 2.000939725061475e+25 (0%) avg:0% 21:"bridge-7-100": 579.9911997249991 -> 579.9911997249991 (0%) avg:0% +12:"top20-11": 0 -> 0 (0%) avg:0% +22:"bridge-7-300": 579.9911997249991 -> 579.9911997249991 (0%) avg:0% +13:"top20-12": 5.998729978399937e+23 -> 5.998729978399937e+23 (0%) avg:0% +14:"top20-13": 14.999999999999975 -> 14.999999999999975 (0%) avg:0% +15:"top20-14": 242561332730 -> 242561332730 (0%) avg:0% +16:"top20-15": 20057219983855420 -> 20057219983855420 (0%) avg:0% +17:"top20-16": 1371193183.507006 -> 1371193183.507006 (0%) avg:0% +18:"top20-17": 618784869519468900000 -> 618784869519468900000 (0%) avg:0% +19:"top20-18": 13965130914 -> 13965130914 (0%) avg:0% +23:"bridge-7-1000": 578.9911997249991 -> 578.9911997249991 (0%) avg:0% +20:"top20-19": 1719221840266011.2 -> 1719221840266011.2 (0%) avg:0% +21:"top20-20": 1.1539072915253164e+27 -> 1.1539072915253164e+27 (0%) avg:0% +22:"top20-21": 1.4857010533172297e+22 -> 1.4857010533172297e+22 (0%) avg:0% +23:"top20-22": 13216695381.96823 -> 13216695381.96823 (0%) avg:0% +24:"top20-23": 71.99999999995467 -> 71.99999999995467 (0%) avg:0% +25:"top20-24": 4.257526126994545e+23 -> 4.257526126994545e+23 (0%) avg:0% +26:"top20-25": 9.20707200216008e+22 -> 9.20707200216008e+22 (0%) avg:0% +27:"top20-26": 119377775 -> 119377775 (0%) avg:0% +28:"top20-27": 4.5396775085904846e+33 -> 4.5396775085904846e+33 (0%) avg:0% +29:"top20-28": 224764614280864 -> 224764614280864 (0%) avg:0% +30:"top20-29": 137910092016871140 -> 137910092016871140 (0%) avg:0% +31:"top20-30": 38414127759.999916 -> 38414127759.999916 (0%) avg:0% +32:"top20-31": 3.9150404497784796e+29 -> 3.9150404497784796e+29 (0%) avg:0% +33:"top20-32": 44939721212060795000 -> 44939721212060795000 (0%) avg:0% +34:"top20-33": 1.4260244914513205e+22 -> 1.4260244914513205e+22 (0%) avg:0% +35:"top20-34": 1.5495747386928312e+21 -> 1.5495747386928312e+21 (0%) avg:0% +36:"top20-35": 943239170088233300000 -> 943239170088233300000 (0%) avg:0% +37:"top20-36": 6121749117436.458 -> 6121749117436.458 (0%) avg:0% +38:"top20-37": 696747 -> 696747 (0%) avg:0% +39:"top20-38": 0 -> 0 (0%) avg:0% +40:"top20-39": 2.3426635283232224e+24 -> 2.3426635283232224e+24 (0%) avg:0% +41:"top20-40": 48254932363.26666 -> 48254932363.26666 (0%) avg:0% +42:"top20-41": 37860138974211.35 -> 37860138974211.35 (0%) avg:0% +43:"top20-42": 6.848076980412381e+22 -> 6.848076980412381e+22 (0%) avg:0% +44:"top20-43": 4320914881 -> 4320914881 (0%) avg:0% +45:"top20-44": 30713427089 -> 30713427089 (0%) avg:0% +46:"top20-45": 198661729955810 -> 198661729955810 (0%) avg:0% +47:"top20-46": 4003462547412852.5 -> 4003462547412852.5 (0%) avg:0% +48:"top20-47": 121585 -> 121585 (0%) avg:0% +49:"top20-48": 0 -> 0 (0%) avg:0% +50:"top20-49": 5.999999999999976 -> 5.999999999999976 (0%) avg:0% +51:"top20-50": 968129348392.8033 -> 968129348392.8033 (0%) avg:0% +52:"top20-51": 17468164450890598000 -> 17468164450890598000 (0%) avg:0% +53:"top20-52": 3370 -> 3370 (0%) avg:0% +54:"top20-53": 2.8669052288613494e+26 -> 2.8669052288613494e+26 (0%) avg:0% +55:"top20-54": 212397690771150670000 -> 212397690771150670000 (0%) avg:0% +56:"top20-55": 114722123950.99864 -> 114722123950.99864 (0%) avg:0% +57:"top20-56": 6237611807.9999895 -> 6237611807.9999895 (0%) avg:0% +58:"top20-57": 173 -> 173 (0%) avg:0% +59:"top20-58": 8.653786678134718e+34 -> 8.653786678134718e+34 (0%) avg:0% +60:"top20-59": 584843.9943957697 -> 584843.9943957697 (0%) avg:0% +61:"top20-60": 83746.99999922093 -> 83746.99999922093 (0%) avg:0% +62:"top20-61": 1.2108029911483686e+28 -> 1.2108029911483686e+28 (0%) avg:0% +63:"top20-62": 1.1321762736810075e+23 -> 1.1321762736810075e+23 (0%) avg:0% +64:"top20-63": 18624850720 -> 18624850720 (0%) avg:0% +65:"top20-64": 6558802213825504000 -> 6558802213825504000 (0%) avg:0% +66:"top20-65": 3006323408104.839 -> 3006323408104.839 (0%) avg:0% +67:"top20-66": 7.25699759360167e+33 -> 7.25699759360167e+33 (0%) avg:0% +68:"top20-67": 26997497976784.977 -> 26997497976784.977 (0%) avg:0% +69:"top20-68": 2.271898658800524e+29 -> 2.271898658800524e+29 (0%) avg:0% +70:"top20-69": 3320690.9992622463 -> 3320690.9992622463 (0%) avg:0% +71:"top20-70": 12137990973481046000 -> 12137990973481046000 (0%) avg:0% +72:"top20-71": 4.2988353121158716e+26 -> 4.2988353121158716e+26 (0%) avg:0% +73:"top20-72": 6.3575495792381146e+28 -> 6.3575495792381146e+28 (0%) avg:0% +74:"top20-73": 1.2313497939167126e+26 -> 1.2313497939167126e+26 (0%) avg:0% +75:"top20-74": 27707599790097 -> 27707599790097 (0%) avg:0% +76:"top20-75": 7124308 -> 7124308 (0%) avg:0% +77:"top20-76": 0 -> 0 (0%) avg:0% +78:"top20-77": 117608543724585440000 -> 117608543724585440000 (0%) avg:0% +79:"top20-78": 274576852204457 -> 274576852204457 (0%) avg:0% +80:"top20-79": 4.1679251723404575e+23 -> 4.1679251723404575e+23 (0%) avg:0% +81:"top20-80": 1.169123327125618e+23 -> 1.169123327125618e+23 (0%) avg:0% +82:"top20-81": 1.256638691159724e+26 -> 1.256638691159724e+26 (0%) avg:0% +83:"top20-82": 0 -> 0 (0%) avg:0% +84:"top20-83": 2.0149283251435153e+32 -> 2.0149283251435153e+32 (0%) avg:0% +85:"top20-84": 6.510078466693012e+35 -> 6.510078466693012e+35 (0%) avg:0% +86:"top20-85": 0 -> 0 (0%) avg:0% +87:"top20-86": 541712645104171 -> 541712645104171 (0%) avg:0% +88:"top20-87": 2935458414464351700 -> 2935458414464351700 (0%) avg:0% +89:"top20-88": 1672225 -> 1672225 (0%) avg:0% +90:"top20-89": 0 -> 0 (0%) avg:0% +91:"top20-90": 0 -> 0 (0%) avg:0% +92:"top20-91": 98143784 -> 98143784 (0%) avg:0% +93:"top20-92": 8692029076033.435 -> 8692029076033.435 (0%) avg:0% +94:"top20-93": 51562257056054440 -> 51562257056054440 (0%) avg:0% +95:"top20-94": 25641964396823.965 -> 25641964396823.965 (0%) avg:0% +96:"top20-95": 526418678128563400 -> 526418678128563400 (0%) avg:0% +97:"top20-96": 42011945995000000 -> 42011945995000000 (0%) avg:0% +98:"top20-97": 0 -> 0 (0%) avg:0% +99:"top20-98": 578210769514157600 -> 578210769514157600 (0%) avg:0% +100:"top20-99": 23757288573225 -> 23757288573225 (0%) avg:0% +101:"top20-100": 3519054200.963973 -> 3519054200.963973 (0%) avg:0% +102:"top20-101": 1.2366902841804613e+26 -> 1.2366902841804613e+26 (0%) avg:0% +103:"top20-102": 3.0616038596056797e+32 -> 3.0616038596056797e+32 (0%) avg:0% +104:"top20-103": 398198791124206 -> 398198791124206 (0%) avg:0% +105:"top20-104": 3434281999000000 -> 3434281999000000 (0%) avg:0% +106:"top20-105": 436386366610963040000 -> 436386366610963040000 (0%) avg:0% +107:"top20-106": 7814 -> 7814 (0%) avg:0% +108:"top20-107": 4.387432445585929e+34 -> 4.387432445585929e+34 (0%) avg:0% +109:"top20-108": 0 -> 0 (0%) avg:0% +110:"top20-109": 0 -> 0 (0%) avg:0% +111:"top20-110": 180771390372608.16 -> 180771390372608.16 (0%) avg:0% +112:"top20-111": 1.53356324718735e+23 -> 1.53356324718735e+23 (0%) avg:0% +113:"top20-112": 0 -> 0 (0%) avg:0% +114:"top20-113": 441807728976948040000 -> 441807728976948040000 (0%) avg:0% +115:"top20-114": 5.872905678780656e+30 -> 5.872905678780656e+30 (0%) avg:0% +116:"top20-115": 1.9999999999999998 -> 1.9999999999999998 (0%) avg:0% +117:"top20-116": 0 -> 0 (0%) avg:0% +118:"top20-117": 103625.91934739704 -> 103625.91934739704 (0%) avg:0% +119:"top20-118": 3994 -> 3994 (0%) avg:0% +120:"top20-119": 5481554822 -> 5481554822 (0%) avg:0% +121:"top20-120": 2148662632935361000 -> 2148662632935361000 (0%) avg:0% +122:"top20-121": 780768139203693200000 -> 780768139203693200000 (0%) avg:0% +123:"top20-122": 0 -> 0 (0%) avg:0% +124:"top20-123": 74390557839848180 -> 74390557839848180 (0%) avg:0% +125:"top20-124": 5.2767009412144705e+23 -> 5.2767009412144705e+23 (0%) avg:0% +126:"top20-125": 12999322599.999998 -> 12999322599.999998 (0%) avg:0% +127:"top20-126": 1.166563599584172e+25 -> 1.166563599584172e+25 (0%) avg:0% +128:"top20-127": 0 -> 0 (0%) avg:0% +129:"top20-128": 2.381239814048817e+29 -> 2.381239814048817e+29 (0%) avg:0% +130:"top20-129": 1.773617547666896e+21 -> 1.773617547666896e+21 (0%) avg:0% +131:"top20-130": 1043374941775 -> 1043374941775 (0%) avg:0% +132:"top20-131": 3 -> 3 (0%) avg:0% +133:"top20-132": 3523544256 -> 3523544256 (0%) avg:0% +134:"top20-133": 30660569440069612 -> 30660569440069612 (0%) avg:0% +135:"top20-134": 0 -> 0 (0%) avg:0% +136:"top20-135": 46215289309 -> 46215289309 (0%) avg:0% +137:"top20-136": 157666012290841060 -> 157666012290841060 (0%) avg:0% +138:"top20-137": 0 -> 0 (0%) avg:0% +139:"top20-138": 94486154517 -> 94486154517 (0%) avg:0% +140:"top20-139": 5381047.578903644 -> 5381047.578903644 (0%) avg:0% +141:"top20-140": 2904868891031402 -> 2904868891031402 (0%) avg:0% +142:"top20-141": 349815735970433660000 -> 349815735970433660000 (0%) avg:0% +143:"top20-142": 1.61321284630438e+29 -> 1.61321284630438e+29 (0%) avg:0% +144:"top20-143": 0 -> 0 (0%) avg:0% +145:"top20-144": 1.8715935050611244e+27 -> 1.8715935050611244e+27 (0%) avg:0% +146:"top20-145": 2329.999987331977 -> 2329.999987331977 (0%) avg:0% +147:"top20-146": 1.5754421469670991e+28 -> 1.5754421469670991e+28 (0%) avg:0% +148:"top20-147": 2.6838048385825768e+32 -> 2.6838048385825768e+32 (0%) avg:0% +149:"top20-148": 2.7141809250137436e+32 -> 2.7141809250137436e+32 (0%) avg:0% +150:"top20-149": 17399664148369718 -> 17399664148369718 (0%) avg:0% +151:"top20-150": 0 -> 0 (0%) avg:0% +152:"top20-151": 481 -> 481 (0%) avg:0% +153:"top20-152": 0 -> 0 (0%) avg:0% +154:"top20-153": 1.3468577317796694e+32 -> 1.3468577317796694e+32 (0%) avg:0% +155:"top20-154": 7978807883992.964 -> 7978807883992.964 (0%) avg:0% +156:"top20-155": 75460.9999999988 -> 75460.9999999988 (0%) avg:0% +157:"top20-156": 1433531033857000200 -> 1433531033857000200 (0%) avg:0% +158:"top20-157": 5729885219 -> 5729885219 (0%) avg:0% 159:"top20-158": 3.5295876698920206e+21 -> 3.5295876698920206e+21 (0%) avg:0% 160:"top20-159": 2752089826200724500 -> 2752089826200724500 (0%) avg:0% 161:"top20-160": 1.3721043284746797e+27 -> 1.3721043284746797e+27 (0%) avg:0% 162:"top20-161": 2628873 -> 2628873 (0%) avg:0% -22:"bridge-7-300": 579.9911997249991 -> 579.9911997249991 (0%) avg:0% 163:"top20-162": 0 -> 0 (0%) avg:0% 164:"top20-163": 7.139942836800165e+35 -> 7.139942836800165e+35 (0%) avg:0% 165:"top20-164": 588671138.9993676 -> 588671138.9993676 (0%) avg:0% 166:"top20-165": 141491517.99997458 -> 141491517.99997458 (0%) avg:0% -23:"bridge-7-1000": 578.9911997249991 -> 578.9911997249991 (0%) avg:0% 167:"top20-166": 9.906334905865037e+36 -> 9.906334905865037e+36 (0%) avg:0% 168:"top20-167": 6.127970056928261e+23 -> 6.127970056928261e+23 (0%) avg:0% 169:"top20-168": 0 -> 0 (0%) avg:0%