Compare commits

...

26 Commits

Author SHA1 Message Date
Vignesh Mohankumar
5979635939 chore: update coinbase-wallet sdk (#6037) 2023-02-27 17:18:21 -05:00
dependabot[bot]
5399bdb550 chore(deps): bump @uniswap/widgets from 2.39.0 to 2.40.0 (#6032)
Bumps [@uniswap/widgets](https://github.com/Uniswap/widgets) from 2.39.0 to 2.40.0.
- [Release notes](https://github.com/Uniswap/widgets/releases)
- [Changelog](https://github.com/Uniswap/widgets/blob/main/.releaserc.json)
- [Commits](https://github.com/Uniswap/widgets/compare/v2.39.0...v2.40.0)

---
updated-dependencies:
- dependency-name: "@uniswap/widgets"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-27 15:01:55 -05:00
Charles Bachmeier
1df9da9eff chore: Refactor ListingButton with deprecation of Listing V1 (#6023)
* remove unused wrapper

* remove old listing modal

* simplify button styling

* deprecate outdated listing logic and move button to styled components

* remove unused linting protection

* remove unused functions from sellAssets hook

* undo and save this refactor for different PR

* remove more unused items

* hook dependencies

* more trash cleanup

* styled continue button

* slight continue button tweaks

* cleanup

* add new standard hover state

* no mixed conditionals

* lint

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-27 10:42:30 -08:00
Jordan Frankfurt
b1e6d0ab7a chore: schema changed gen types (#6033)
schema changed gen types
2023-02-27 13:03:20 -05:00
eddie
a7fcbb4cfc revert: dont hideConnectionUI for widget (#6030)
Revert "fix: dont hideConnectionUI for widget (#6010)"

This reverts commit c18522159b.
2023-02-27 09:18:47 -08:00
Moody Salem
a5a6a037e5 ci: references to old id upload (#6034)
fix: references to old id `upload`

`upload` seems to have been changed to `pinata`
2023-02-27 09:09:50 -08:00
Andrew MacPherson
8bfebd37a2 Updating .env.production file to also include the same comment as .env about reporting these public API keys 2023-02-27 10:00:22 -05:00
Vignesh Mohankumar
4029819090 chore: update widget (#6029) 2023-02-24 17:27:08 -05:00
Vignesh Mohankumar
021ae5e74e fix: reset tokens, amounts when switching chains (#6026)
* fix: reset tokens when switching chains on widget

* clear amounts
2023-02-24 16:35:53 -05:00
eddie
2b9720705f feat: log errors on swap page (#6008)
* feat: log error from widget

* fix: upgrade analytics

* feat: add swap error to main Error Boundary too
2023-02-24 12:09:41 -08:00
blairmason
8f1ea32e5e feat: config statsig init to use reverse proxy (#6018)
* config statsig init to use reverse proxy

* formatting fixes

* update statsig proxy endpoints to prod url
2023-02-24 09:28:01 -08:00
eddie
23acb3b395 feat: add statsig feature gate for widget (#6011)
* feat: add statsig feature gate for widget

* feat: move statsig check into featureFlag infra

* fix: move statsig check deeper into feature flag code

* Update src/featureFlags/flags/featureFlags.ts

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>

* Update src/featureFlags/flags/dummyFeatureGate.ts

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>

---------

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2023-02-23 12:47:26 -08:00
Charles Bachmeier
772416cc7a feat: add collection blocklist for ip infringement (#6022)
Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-23 10:44:27 -08:00
Jordan Frankfurt
f15e5725f1 fix: token list load issue WEB-2083 (#6019) 2023-02-22 22:24:30 -06:00
Jordan Frankfurt
83c8393f19 fix: back arrow from migration should take you to v3 pool page (#6020) 2023-02-22 22:24:22 -06:00
Jack Short
1348eb3322 fix: token selector correct zIndex on mobile for pwat (#6017) 2023-02-22 20:43:35 -05:00
eddie
a2271ba428 fix: widget defaultChainId (#6012) 2023-02-22 14:14:12 -08:00
Charles Bachmeier
1845cb3b7b chore: remove listv2 feature flag (#6009)
* remove listv2 feature flag

* no longer used styles and icons

* missed one

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-22 09:43:39 -08:00
Jordan Frankfurt
6a02bde8e0 fix: darkmode FoR iframe wrapper background color (#5993)
* fix: darkmode FoR iframe wrapper background color

* pr feedback
2023-02-22 10:38:52 -06:00
eddie
c18522159b fix: dont hideConnectionUI for widget (#6010) 2023-02-22 09:23:56 -06:00
Jack Short
5ea7b1de3f feat: enable pay with any token (#6002) 2023-02-22 08:25:09 -05:00
Charles Bachmeier
6efe8f3260 feat: [ListV2] Several Small Polish Changes (#5998)
* NFT-1075 update floor and last price to use body primary

* NFT-1080 update image thumbnail size and adjusted row accordingly

* NFT-1081 force page refresh on success screen

* NFT-1089 update same price icons

* NFT-1082 show warning colors over focus

* remove unused state var

* replace margin with padding for sticky header

* NFT-1109 properly calc if listing date is over 6mo

* NFT-1076 change listing button text to light and mobile profile bar to backgroundSurface

* NFT-1079 persist row data when listing to multiple markets

* perf improvement

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-21 13:56:26 -08:00
lynn
9ac28a4571 fix: token selector scroll margin (#6005)
* fix

* update tests

* remove gql file

* fix breakng snapshot
2023-02-21 16:40:36 -05:00
eddie
bde1421ffb fix: use ipfs token list as fallback for unsupported list (#6007) 2023-02-21 12:55:40 -08:00
eddie
3dceb45d9e fix: use ipfs token list temporarily (#6006)
fix: use ipfs token list as fallback
2023-02-21 12:19:39 -08:00
Jack Short
7b589561bc fix: not using stablecoin to calc price impact pwat (#5997)
* feat: implementing graphql endpoint

* changing from hook to function call

* initial gql routing works

* feat: initial pwatRouting setup

* sending correct amount

* removing console

* it is working

* sufficient balance

* 0 if no inputCurrency

* removing value to send if erc20

* removing console

* permit2 optional flag

* removing not necessary stuff

* mobile fixes

* overlay needs to be here

* changing swap amount to pool reserves

* refactoring routing logic

* no route found button state

* better price loading for insufficient liquidity

* refactoring graphql routing code

* overflow

* initial comments

* resetting bag status on input currency change

* locking

* done

* remove helper text for eth

* fix: pay with any token routing bug

* reordering button

* zindex

* price updated

* keeping debounce

* feat: adding amplitude events for pwat

* bumping analytics version

* types and hooks

* moving erc20 flag to useSendTransaction

* why did i put it in a hook

* no return

* fix: not using usdc value to calculate price impact

* refactor + loading state
2023-02-21 12:55:35 -05:00
54 changed files with 851 additions and 1506 deletions

2
.env
View File

@@ -1,5 +1,6 @@
# These API keys are intentionally public. Please do not report them - thank you for your concern.
REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy"
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
REACT_APP_AWS_API_REGION="us-east-2"
REACT_APP_AWS_API_ENDPOINT="https://beta.api.uniswap.org/v1/graphql"
REACT_APP_TEMP_API_URL="https://temp.api.uniswap.org/v1"
@@ -10,4 +11,3 @@ REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
REACT_APP_MOONPAY_API="https://api.moonpay.com"
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLinkStaging?platform=web"
REACT_APP_MOONPAY_PUBLISHABLE_KEY="pk_test_DycfESRid31UaSxhI5yWKe1r5E5kKSz"
REACT_APP_STATSIG_API_KEY="client-1rY92WZGidd2hgW4x1lsZ7afqm1Qfr3sJfH3A5b8eJa"

View File

@@ -1,4 +1,6 @@
# These API keys are intentionally public. Please do not report them - thank you for your concern.
REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy"
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
REACT_APP_AWS_API_ENDPOINT="https://api.uniswap.org/v1/graphql"
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8"
@@ -9,4 +11,3 @@ REACT_APP_MOONPAY_PUBLISHABLE_KEY="pk_live_uQG4BJC4w3cxnqpcSqAfohdBFDTsY6E"
REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0"
THE_GRAPH_SCHEMA_ENDPOINT="https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3"
REACT_APP_SENTRY_ENABLED=false
REACT_APP_STATSIG_API_KEY="client-1rY92WZGidd2hgW4x1lsZ7afqm1Qfr3sJfH3A5b8eJa"

View File

@@ -62,7 +62,7 @@ jobs:
continue-on-error: true
timeout-minutes: 2
with:
cid: ${{ steps.upload.outputs.hash }}
cid: ${{ steps.pinata.outputs.hash }}
seeds: ${{ secrets.CRUST_SEEDS }}
- name: Convert CIDv0 to CIDv1
@@ -93,7 +93,7 @@ jobs:
IPFS gateways:
- https://${{ steps.convert-cidv0.outputs.cidv1 }}.ipfs.dweb.link/
- https://${{ steps.convert-cidv0.outputs.cidv1 }}.ipfs.cf-ipfs.com/
- [ipfs://${{ steps.upload.outputs.hash }}/](ipfs://${{ steps.pinata.outputs.hash }}/)
- [ipfs://${{ steps.pinata.outputs.hash }}/](ipfs://${{ steps.pinata.outputs.hash }}/)
${{ needs.tag.outputs.changelog }}

View File

@@ -111,7 +111,7 @@
},
"dependencies": {
"@apollo/client": "^3.7.2",
"@coinbase/wallet-sdk": "^3.3.0",
"@coinbase/wallet-sdk": "^3.6.4",
"@fontsource/ibm-plex-mono": "^4.5.1",
"@fontsource/inter": "^4.5.1",
"@graphql-codegen/cli": "^2.15.0",
@@ -133,8 +133,8 @@
"@reduxjs/toolkit": "^1.6.1",
"@sentry/react": "^7.29.0",
"@types/react-window-infinite-loader": "^1.0.6",
"@uniswap/analytics": "^1.3.0",
"@uniswap/analytics-events": "^2.3.0",
"@uniswap/analytics": "^1.3.1",
"@uniswap/analytics-events": "^2.4.0",
"@uniswap/conedison": "^1.3.0",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",
@@ -152,7 +152,7 @@
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.9.0",
"@uniswap/widgets": "^2.29.3",
"@uniswap/widgets": "^2.40.0",
"@vanilla-extract/css": "^1.7.2",
"@vanilla-extract/css-utils": "^0.1.2",
"@vanilla-extract/dynamic": "^2.0.2",

View File

@@ -1,10 +1,13 @@
import { Trans } from '@lingui/macro'
import * as Sentry from '@sentry/react'
import { sendAnalyticsEvent } from '@uniswap/analytics'
import { SwapEventName } from '@uniswap/analytics-events'
import { ButtonLight, SmallButtonPrimary } from 'components/Button'
import { ChevronUpIcon } from 'nft/components/icons'
import { useIsMobile } from 'nft/hooks'
import React, { PropsWithChildren, useState } from 'react'
import { Copy } from 'react-feather'
import { useLocation } from 'react-router-dom'
import styled from 'styled-components/macro'
import { isSentryEnabled } from 'utils/env'
@@ -217,13 +220,19 @@ const updateServiceWorkerInBackground = async () => {
}
export default function ErrorBoundary({ children }: PropsWithChildren): JSX.Element {
const { pathname } = useLocation()
return (
<Sentry.ErrorBoundary
fallback={({ error, eventId }) => <Fallback error={error} eventId={eventId} />}
beforeCapture={(scope) => {
scope.setLevel('fatal')
}}
onError={updateServiceWorkerInBackground}
onError={(error) => {
updateServiceWorkerInBackground()
if (pathname === '/swap') {
sendAnalyticsEvent(SwapEventName.SWAP_ERROR, { error })
}
}}
>
{children}
</Sentry.ErrorBoundary>

View File

@@ -1,6 +1,5 @@
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
import { GqlRoutingVariant, useGqlRoutingFlag } from 'featureFlags/flags/gqlRouting'
import { NftListV2Variant, useNftListV2Flag } from 'featureFlags/flags/nftListV2'
import { PayWithAnyTokenVariant, usePayWithAnyTokenFlag } from 'featureFlags/flags/payWithAnyToken'
import { Permit2Variant, usePermit2Flag } from 'featureFlags/flags/permit2'
import { SwapWidgetVariant, useSwapWidgetFlag } from 'featureFlags/flags/swapWidget'
@@ -212,12 +211,6 @@ export default function FeatureFlagModal() {
featureFlag={FeatureFlag.permit2}
label="Permit 2 / Universal Router"
/>
<FeatureFlagOption
variant={NftListV2Variant}
value={useNftListV2Flag()}
featureFlag={FeatureFlag.nftListV2}
label="NFT Listing Page v2"
/>
<FeatureFlagOption
variant={PayWithAnyTokenVariant}
value={usePayWithAnyTokenFlag()}

View File

@@ -10,8 +10,10 @@ import { CustomLightSpinner, ThemedText } from 'theme'
import Circle from '../../assets/images/blue-loader.svg'
import Modal from '../Modal'
const Wrapper = styled.div`
background-color: ${({ theme }) => theme.white};
const MOONPAY_DARK_BACKGROUND = '#1c1c1e'
const Wrapper = styled.div<{ isDarkMode: boolean }>`
// #1c1c1e is the background color for the darkmode moonpay iframe as of 2/16/2023
background-color: ${({ isDarkMode, theme }) => (isDarkMode ? MOONPAY_DARK_BACKGROUND : theme.white)};
border-radius: 20px;
box-shadow: ${({ theme }) => theme.deepShadow};
display: flex;
@@ -29,8 +31,9 @@ const ErrorText = styled(ThemedText.BodyPrimary)`
text-align: center;
width: 90%;
`
const StyledIframe = styled.iframe`
background-color: ${({ theme }) => theme.white};
const StyledIframe = styled.iframe<{ isDarkMode: boolean }>`
// #1c1c1e is the background color for the darkmode moonpay iframe as of 2/16/2023
background-color: ${({ isDarkMode, theme }) => (isDarkMode ? MOONPAY_DARK_BACKGROUND : theme.white)};
border-radius: 12px;
bottom: 0;
left: 0;
@@ -123,7 +126,7 @@ export default function FiatOnrampModal() {
return (
<Modal isOpen={fiatOnrampModalOpen} onDismiss={closeModal} maxHeight={720}>
<Wrapper data-testid="fiat-onramp-modal">
<Wrapper data-testid="fiat-onramp-modal" isDarkMode={isDarkMode}>
{error ? (
<>
<ThemedText.MediumHeader>
@@ -138,7 +141,12 @@ export default function FiatOnrampModal() {
) : loading ? (
<StyledSpinner src={Circle} alt="loading spinner" size="90px" />
) : (
<StyledIframe src={signedIframeUrl ?? ''} frameBorder="0" title="fiat-onramp-iframe" />
<StyledIframe
src={signedIframeUrl ?? ''}
frameBorder="0"
title="fiat-onramp-iframe"
isDarkMode={isDarkMode}
/>
)}
</Wrapper>
</Modal>

View File

@@ -1,7 +1,6 @@
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import Web3Status from 'components/Web3Status'
import { NftListV2Variant, useNftListV2Flag } from 'featureFlags/flags/nftListV2'
import { chainIdToBackendName } from 'graphql/data/util'
import { useIsNftPage } from 'hooks/useIsNftPage'
import { Box } from 'nft/components/Box'
@@ -82,7 +81,6 @@ export const PageTabs = () => {
const Navbar = () => {
const isNftPage = useIsNftPage()
const sellPageState = useProfilePageState((state) => state.state)
const isNftListV2 = useNftListV2Flag() === NftListV2Variant.Enabled
const navigate = useNavigate()
return (
@@ -124,7 +122,7 @@ const Navbar = () => {
<Box display={{ sm: 'none', lg: 'flex' }}>
<MenuDropdown />
</Box>
{isNftPage && (!isNftListV2 || sellPageState !== ProfilePageStateType.LISTING) && <Bag />}
{isNftPage && sellPageState !== ProfilePageStateType.LISTING && <Bag />}
{!isNftPage && (
<Box display={{ sm: 'none', lg: 'flex' }}>
<ChainSelector />

View File

@@ -105,7 +105,7 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
}
<div
style="padding-right: 8px; padding-top: 8px;"
style="padding-right: 4px;"
>
<div
class="CurrencyList_scrollbarStyle__1pi21y70"
@@ -388,7 +388,7 @@ exports[`renders loading rows when isLoading is true 1`] = `
}
<div
style="padding-right: 8px; padding-top: 8px;"
style="padding-right: 4px;"
>
<div
class="CurrencyList_scrollbarStyle__1pi21y70"

View File

@@ -290,7 +290,7 @@ export default function CurrencyList({
}, [])
return (
<div style={{ paddingRight: '8px', paddingTop: '8px' }}>
<div style={{ paddingRight: '4px' }}>
{isLoading ? (
<FixedSizeList
className={styles.scrollbarStyle}

View File

@@ -3,11 +3,10 @@ import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { NATIVE_CHAIN_ID } from 'constants/tokens'
import { FeatureGate } from 'featureFlags/flags/featureFlags'
import { useDummyGateEnabled } from 'featureFlags/flags/dummyFeatureGate'
import { CHAIN_ID_TO_BACKEND_NAME } from 'graphql/data/util'
import { useStablecoinValue } from 'hooks/useStablecoinPrice'
import useCurrencyBalance from 'lib/hooks/useCurrencyBalance'
import { useGate } from 'statsig-react'
import styled from 'styled-components/macro'
import { StyledInternalLink } from 'theme'
@@ -89,7 +88,7 @@ export default function MobileBalanceSummaryFooter({ token }: { token: Currency
const formattedBalance = formatCurrencyAmount(balance, NumberType.TokenNonTx)
const formattedUsdValue = formatCurrencyAmount(useStablecoinValue(balance), NumberType.FiatTokenStats)
const chain = CHAIN_ID_TO_BACKEND_NAME[token.chainId].toLowerCase()
const { value: isDummyGateFlagEnabled } = useGate(FeatureGate.DUMMY)
const isDummyGateFlagEnabled = useDummyGateEnabled()
return (
<Wrapper>

View File

@@ -57,7 +57,7 @@ export default function Widget({
onDefaultTokenChange,
onReviewSwapClick,
}: WidgetProps) {
const { connector, provider } = useWeb3React()
const { connector, provider, chainId } = useWeb3React()
const locale = useActiveLocale()
const theme = useWidgetTheme()
const { inputs, tokenSelector } = useSyncWidgetInputs({
@@ -169,7 +169,7 @@ export default function Widget({
locale={locale}
theme={theme}
width={width}
// defaultChainId is excluded - it is always inferred from the passed provider
defaultChainId={chainId}
onConnectWalletClick={onConnectWalletClick}
provider={provider}
onSwitchChain={onSwitchChain}
@@ -183,6 +183,9 @@ export default function Widget({
onSwapApprove={onApproveToken}
onInitialSwapQuote={onInitialSwapQuote}
onSwapPriceUpdateAck={onSwapPriceUpdateAck}
onError={(error, errorInfo) => {
sendAnalyticsEvent(SwapEventName.SWAP_ERROR, { error, errorInfo, ...trace })
}}
/>
{tokenSelector}
</>

View File

@@ -1,7 +1,9 @@
import { sendAnalyticsEvent, useTrace } from '@uniswap/analytics'
import { InterfaceSectionName, SwapEventName } from '@uniswap/analytics-events'
import { Currency, Field, SwapController, SwapEventHandlers, TradeType } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal'
import usePrevious from 'hooks/usePrevious'
import { useCallback, useEffect, useMemo, useState } from 'react'
const EMPTY_AMOUNT = ''
@@ -29,24 +31,35 @@ export function useSyncWidgetInputs({
}) {
const trace = useTrace({ section: InterfaceSectionName.WIDGET })
const { chainId } = useWeb3React()
const previousChainId = usePrevious(chainId)
const [type, setType] = useState<SwapValue['type']>(TradeType.EXACT_INPUT)
const [amount, setAmount] = useState<SwapValue['amount']>(EMPTY_AMOUNT)
const [tokens, setTokens] = useState<SwapTokens>(defaultTokens)
useEffect(() => {
if (!tokens[Field.INPUT] && !tokens[Field.OUTPUT]) {
setTokens((tokens) => {
const update = {
...tokens,
[Field.INPUT]: defaultTokens[Field.INPUT] ?? tokens[Field.INPUT],
[Field.OUTPUT]: defaultTokens[Field.OUTPUT] ?? tokens[Field.OUTPUT] ?? defaultTokens.default,
default: defaultTokens.default,
}
return update
setTokens({
...tokens,
[Field.INPUT]: defaultTokens[Field.INPUT] ?? tokens[Field.INPUT],
[Field.OUTPUT]: defaultTokens[Field.OUTPUT] ?? tokens[Field.OUTPUT] ?? defaultTokens.default,
default: defaultTokens.default,
})
}
}, [defaultTokens, tokens])
useEffect(() => {
if (chainId !== previousChainId && !!previousChainId) {
setTokens({
...tokens,
[Field.INPUT]: undefined,
[Field.OUTPUT]: undefined,
})
setAmount(EMPTY_AMOUNT)
}
}, [chainId, previousChainId, tokens])
const onAmountChange = useCallback(
(field: Field, amount: string, origin?: 'max') => {
if (origin === 'max') {

View File

@@ -9,8 +9,12 @@ class TokenLogoLookupTable {
initialize() {
const dict: { [key: string]: string[] | undefined } = {}
DEFAULT_LIST_OF_LISTS.forEach((list) =>
store.getState().lists.byUrl[list].current?.tokens.forEach((token) => {
DEFAULT_LIST_OF_LISTS.forEach((list) => {
const listData = store.getState().lists.byUrl[list]
if (!listData) {
return
}
listData.current?.tokens.forEach((token) => {
if (token.logoURI) {
const lowercaseAddress = token.address.toLowerCase()
const currentEntry = dict[lowercaseAddress + ':' + token.chainId]
@@ -21,7 +25,7 @@ class TokenLogoLookupTable {
}
}
})
)
})
this.dict = dict
this.initialized = true
}

View File

@@ -1,6 +1,6 @@
export const UNI_LIST = 'https://tokens.uniswap.org'
export const UNI_EXTENDED_LIST = 'https://extendedtokens.uniswap.org/'
const UNI_UNSUPPORTED_LIST = 'https://unsupportedtokens.uniswap.org/'
export const UNI_LIST = 'https://gateway.ipfs.io/ipns/tokens.uniswap.org'
export const UNI_EXTENDED_LIST = 'https://gateway.ipfs.io/ipns/extendedtokens.uniswap.org'
const UNI_UNSUPPORTED_LIST = 'https://gateway.ipfs.io/ipns/unsupportedtokens.uniswap.org'
const AAVE_LIST = 'tokenlist.aave.eth'
const BA_LIST = 'https://raw.githubusercontent.com/The-Blockchain-Association/sec-notice-list/master/ba-sec-list.json'
const CMC_ALL_LIST = 'https://api.coinmarketcap.com/data-api/v3/uniswap/all.json'

View File

@@ -0,0 +1,9 @@
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
function useDummyGateFlag(): BaseVariant {
return useBaseFlag(FeatureFlag.statsigDummy)
}
export function useDummyGateEnabled(): boolean {
return useDummyGateFlag() === BaseVariant.Enabled
}

View File

@@ -1,12 +1,11 @@
/**
* The value here must match the value in the statsig dashboard, if you plan to use statsig.
*/
export enum FeatureFlag {
traceJsonRpc = 'traceJsonRpc',
permit2 = 'permit2',
nftListV2 = 'nftListV2',
payWithAnyToken = 'payWithAnyToken',
swapWidget = 'swapWidget',
swapWidget = 'swap_widget_replacement_enabled',
gqlRouting = 'gqlRouting',
}
export enum FeatureGate {
DUMMY = 'web_dummy_gate_amplitude_id',
statsigDummy = 'web_dummy_gate_amplitude_id',
}

View File

@@ -1,7 +1,7 @@
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
export function useGqlRoutingFlag(): BaseVariant {
return useBaseFlag(FeatureFlag.gqlRouting)
return useBaseFlag(FeatureFlag.gqlRouting, BaseVariant.Enabled)
}
export { BaseVariant as GqlRoutingVariant }

View File

@@ -1,7 +0,0 @@
import { BaseVariant } from '../index'
export function useNftListV2Flag(): BaseVariant {
return BaseVariant.Enabled
}
export { BaseVariant as NftListV2Variant }

View File

@@ -4,7 +4,7 @@ import { useWeb3React } from '@web3-react/core'
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
export function usePayWithAnyTokenFlag(): BaseVariant {
return useBaseFlag(FeatureFlag.payWithAnyToken)
return useBaseFlag(FeatureFlag.payWithAnyToken, BaseVariant.Enabled)
}
export function usePayWithAnyTokenEnabled(): boolean {

View File

@@ -1,5 +1,6 @@
import { atomWithStorage, useAtomValue, useUpdateAtom } from 'jotai/utils'
import { createContext, ReactNode, useCallback, useContext } from 'react'
import { useGate } from 'statsig-react'
export { FeatureFlag } from './flags/featureFlags'
interface FeatureFlagsContextType {
@@ -56,7 +57,12 @@ export enum BaseVariant {
}
export function useBaseFlag(flag: string, defaultValue = BaseVariant.Control): BaseVariant {
switch (useFeatureFlagsContext().flags[flag]) {
const { value: statsigValue } = useGate(flag) // non-existent gates return false
const featureFlagsContext = useFeatureFlagsContext()
if (statsigValue) {
return BaseVariant.Enabled
}
switch (featureFlagsContext.flags[flag]) {
case 'enabled':
return BaseVariant.Enabled
case 'control':

View File

@@ -86,6 +86,10 @@ export enum Chain {
UnknownChain = 'UNKNOWN_CHAIN'
}
export enum CollectionSortableField {
Volume = 'VOLUME'
}
export type ContractInput = {
address?: InputMaybe<Scalars['String']>;
chain: Chain;
@@ -96,11 +100,6 @@ export enum Currency {
Usd = 'USD'
}
export enum DatasourceProvider {
Alternate = 'ALTERNATE',
Legacy = 'LEGACY'
}
export type Dimensions = {
__typename?: 'Dimensions';
height?: Maybe<Scalars['Float']>;
@@ -145,6 +144,49 @@ export enum MarketSortableField {
Volume = 'VOLUME'
}
export type NftActivity = {
__typename?: 'NftActivity';
address: Scalars['String'];
asset?: Maybe<NftAsset>;
fromAddress: Scalars['String'];
id: Scalars['ID'];
marketplace?: Maybe<NftMarketplace>;
orderStatus?: Maybe<OrderStatus>;
price?: Maybe<Amount>;
quantity?: Maybe<Scalars['Int']>;
timestamp: Scalars['Int'];
toAddress?: Maybe<Scalars['String']>;
tokenId?: Maybe<Scalars['String']>;
transactionHash?: Maybe<Scalars['String']>;
type: NftActivityType;
url?: Maybe<Scalars['String']>;
};
export type NftActivityConnection = {
__typename?: 'NftActivityConnection';
edges: Array<NftActivityEdge>;
pageInfo: PageInfo;
};
export type NftActivityEdge = {
__typename?: 'NftActivityEdge';
cursor: Scalars['String'];
node: NftActivity;
};
export type NftActivityFilterInput = {
activityTypes?: InputMaybe<Array<NftActivityType>>;
address?: InputMaybe<Scalars['String']>;
tokenId?: InputMaybe<Scalars['String']>;
};
export enum NftActivityType {
CancelListing = 'CANCEL_LISTING',
Listing = 'LISTING',
Sale = 'SALE',
Transfer = 'TRANSFER'
}
export type NftApproval = {
__typename?: 'NftApproval';
approvedAddress: Scalars['String'];
@@ -196,7 +238,6 @@ export type NftAssetListingsArgs = {
after?: InputMaybe<Scalars['String']>;
asc?: InputMaybe<Scalars['Boolean']>;
before?: InputMaybe<Scalars['String']>;
datasource?: InputMaybe<DatasourceProvider>;
first?: InputMaybe<Scalars['Int']>;
last?: InputMaybe<Scalars['Int']>;
};
@@ -311,7 +352,6 @@ export type NftCollection = {
export type NftCollectionMarketsArgs = {
currencies: Array<Currency>;
datasource?: InputMaybe<DatasourceProvider>;
};
export type NftCollectionConnection = {
@@ -335,6 +375,8 @@ export type NftCollectionMarket = {
marketplaces?: Maybe<Array<NftCollectionMarketplace>>;
nftContracts?: Maybe<Array<NftContract>>;
owners?: Maybe<Scalars['Int']>;
percentListed?: Maybe<TimestampedAmount>;
percentUniqueOwners?: Maybe<TimestampedAmount>;
sales?: Maybe<TimestampedAmount>;
totalVolume?: Maybe<TimestampedAmount>;
volume?: Maybe<TimestampedAmount>;
@@ -602,6 +644,7 @@ export type PortfolioTokensTotalDenominatedValueChangeArgs = {
export type Query = {
__typename?: 'Query';
nftActivity?: Maybe<NftActivityConnection>;
nftAssets?: Maybe<NftAssetConnection>;
nftBalances?: Maybe<NftBalanceConnection>;
nftCollections?: Maybe<NftCollectionConnection>;
@@ -613,17 +656,25 @@ export type Query = {
token?: Maybe<Token>;
tokenProjects?: Maybe<Array<Maybe<TokenProject>>>;
tokens?: Maybe<Array<Maybe<Token>>>;
topCollections?: Maybe<NftCollectionConnection>;
topTokens?: Maybe<Array<Maybe<Token>>>;
};
export type QueryNftActivityArgs = {
chain?: InputMaybe<Chain>;
cursor?: InputMaybe<Scalars['String']>;
filter?: InputMaybe<NftActivityFilterInput>;
limit?: InputMaybe<Scalars['Int']>;
};
export type QueryNftAssetsArgs = {
address: Scalars['String'];
after?: InputMaybe<Scalars['String']>;
asc?: InputMaybe<Scalars['Boolean']>;
before?: InputMaybe<Scalars['String']>;
chain?: InputMaybe<Chain>;
datasource?: InputMaybe<DatasourceProvider>;
filter?: InputMaybe<NftAssetsFilterInput>;
first?: InputMaybe<Scalars['Int']>;
last?: InputMaybe<Scalars['Int']>;
@@ -636,7 +687,6 @@ export type QueryNftBalancesArgs = {
before?: InputMaybe<Scalars['String']>;
chain?: InputMaybe<Chain>;
cursor?: InputMaybe<Scalars['String']>;
datasource?: InputMaybe<DatasourceProvider>;
filter?: InputMaybe<NftBalancesFilterInput>;
first?: InputMaybe<Scalars['Int']>;
last?: InputMaybe<Scalars['Int']>;
@@ -646,12 +696,10 @@ export type QueryNftBalancesArgs = {
export type QueryNftCollectionsArgs = {
after?: InputMaybe<Scalars['String']>;
before?: InputMaybe<Scalars['String']>;
datasource?: InputMaybe<DatasourceProvider>;
chain?: InputMaybe<Chain>;
cursor?: InputMaybe<Scalars['String']>;
filter?: InputMaybe<NftCollectionsFilterInput>;
first?: InputMaybe<Scalars['Int']>;
last?: InputMaybe<Scalars['Int']>;
limit?: InputMaybe<Scalars['Int']>;
};
@@ -670,7 +718,6 @@ export type QueryNftRouteArgs = {
export type QueryPortfoliosArgs = {
ownerAddresses: Array<Scalars['String']>;
useAltDataSource?: InputMaybe<Scalars['Boolean']>;
};
@@ -700,6 +747,15 @@ export type QueryTokensArgs = {
};
export type QueryTopCollectionsArgs = {
chains?: InputMaybe<Array<Chain>>;
cursor?: InputMaybe<Scalars['String']>;
duration?: InputMaybe<HistoryDuration>;
limit?: InputMaybe<Scalars['Int']>;
orderBy?: InputMaybe<CollectionSortableField>;
};
export type QueryTopTokensArgs = {
chain?: InputMaybe<Chain>;
orderBy?: InputMaybe<TokenSortableField>;

View File

@@ -112,7 +112,7 @@ export function useSearchInactiveTokenLists(search: string | undefined, minResul
const result: WrappedTokenInfo[] = []
const addressSet: { [address: string]: true } = {}
for (const url of inactiveUrls) {
const list = lists[url].current
const list = lists[url]?.current
if (!list) continue
for (const tokenInfo of list.tokens) {
if (tokenInfo.chainId === chainId && tokenFilter(tokenInfo)) {

View File

@@ -1,18 +1,16 @@
import { BigNumber } from '@ethersproject/bignumber'
import { Trans } from '@lingui/macro'
import { sendAnalyticsEvent } from '@uniswap/analytics'
import { NFTEventName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import { GqlRoutingVariant, useGqlRoutingFlag } from 'featureFlags/flags/gqlRouting'
import { NftListV2Variant, useNftListV2Flag } from 'featureFlags/flags/nftListV2'
import { useNftRouteLazyQuery } from 'graphql/data/__generated__/types-and-hooks'
import { useIsNftDetailsPage, useIsNftPage, useIsNftProfilePage } from 'hooks/useIsNftPage'
import { BagFooter } from 'nft/components/bag/BagFooter'
import ListingModal from 'nft/components/bag/profile/ListingModal'
import { Box } from 'nft/components/Box'
import { Portal } from 'nft/components/common/Portal'
import { Column } from 'nft/components/Flex'
import { Overlay } from 'nft/components/modals/Overlay'
import { buttonTextMedium, commonButtonStyles } from 'nft/css/common.css'
import {
useBag,
useIsMobile,
@@ -66,7 +64,7 @@ const BagContainer = styled.div<{ raiseZIndex: boolean; isProfilePage: boolean }
border-radius: 16px;
box-shadow: ${({ theme }) => theme.shallowShadow};
z-index: ${({ raiseZIndex, isProfilePage }) =>
raiseZIndex ? (isProfilePage ? Z_INDEX.modalOverTooltip : Z_INDEX.modalBackdrop + 2) : 3};
raiseZIndex ? (isProfilePage ? Z_INDEX.modalOverTooltip : Z_INDEX.modalBackdrop - 1) : 3};
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
right: 0px;
@@ -90,6 +88,24 @@ const DetailsPageBackground = styled.div`
height: 100%;
`
const ContinueButton = styled.div`
background: ${({ theme }) => theme.accentAction};
color: ${({ theme }) => theme.accentTextLightPrimary};
margin: 32px 28px 16px;
padding: 10px 0px;
border-radius: 12px;
text-align: center;
font-size: 16px;
font-weight: 600;
line-height: 20px;
cursor: pointer;
transition: ${({ theme }) => theme.transition.duration.medium};
:hover {
opacity: ${({ theme }) => theme.opacity.hover};
}
`
const ScrollingIndicator = ({ top, show }: SeparatorProps) => (
<Box
marginX="24"
@@ -114,10 +130,7 @@ const Bag = () => {
shallow
)
const { profilePageState, setProfilePageState } = useProfilePageState(
({ setProfilePageState, state }) => ({ profilePageState: state, setProfilePageState }),
shallow
)
const { setProfilePageState } = useProfilePageState(({ setProfilePageState }) => ({ setProfilePageState }))
const {
bagStatus,
@@ -139,7 +152,6 @@ const Bag = () => {
const isDetailsPage = useIsNftDetailsPage()
const isNFTPage = useIsNftPage()
const isMobile = useIsMobile()
const isNftListV2 = useNftListV2Flag() === NftListV2Variant.Enabled
const usingGqlRouting = useGqlRoutingFlag() === GqlRoutingVariant.Enabled
const sendTransaction = useSendTransaction((state) => state.sendTransaction)
@@ -398,48 +410,34 @@ const Bag = () => {
return (
<Portal>
<BagContainer data-testid="nft-bag" raiseZIndex={isMobile || isModalOpen} isProfilePage={isProfilePage}>
{!(isProfilePage && profilePageState === ProfilePageStateType.LISTING) ? (
<>
<BagHeader
numberOfAssets={isProfilePage ? sellAssets.length : itemsInBag.length}
closeBag={handleCloseBag}
resetFlow={isProfilePage ? resetSellAssets : reset}
isProfilePage={isProfilePage}
/>
{shouldRenderEmptyState && <EmptyState />}
<ScrollingIndicator top show={userCanScroll && scrollProgress > 0} />
<Column ref={scrollRef} className={styles.assetsContainer} onScroll={scrollHandler} gap="12">
{isProfilePage ? <ProfileBagContent /> : <BagContent />}
</Column>
{hasAssetsToShow && !isProfilePage && (
<BagFooter totalEthPrice={totalEthPrice} fetchAssets={fetchAssets} eventProperties={eventProperties} />
)}
{isSellingAssets && isProfilePage && (
<Box
marginTop="32"
marginX="28"
marginBottom="16"
paddingY="10"
className={`${buttonTextMedium} ${commonButtonStyles}`}
backgroundColor="accentAction"
color="white"
textAlign="center"
onClick={() => {
;(isMobile || isNftListV2) && toggleBag()
setProfilePageState(ProfilePageStateType.LISTING)
sendAnalyticsEvent(NFTEventName.NFT_PROFILE_PAGE_START_SELL, {
list_quantity: sellAssets.length,
collection_addresses: sellAssets.map((asset) => asset.asset_contract.address),
token_ids: sellAssets.map((asset) => asset.tokenId),
})
}}
>
Continue
</Box>
)}
</>
) : (
<ListingModal />
<BagHeader
numberOfAssets={isProfilePage ? sellAssets.length : itemsInBag.length}
closeBag={handleCloseBag}
resetFlow={isProfilePage ? resetSellAssets : reset}
isProfilePage={isProfilePage}
/>
{shouldRenderEmptyState && <EmptyState />}
<ScrollingIndicator top show={userCanScroll && scrollProgress > 0} />
<Column ref={scrollRef} className={styles.assetsContainer} onScroll={scrollHandler} gap="12">
{isProfilePage ? <ProfileBagContent /> : <BagContent />}
</Column>
{hasAssetsToShow && !isProfilePage && (
<BagFooter totalEthPrice={totalEthPrice} fetchAssets={fetchAssets} eventProperties={eventProperties} />
)}
{isSellingAssets && isProfilePage && (
<ContinueButton
onClick={() => {
toggleBag()
setProfilePageState(ProfilePageStateType.LISTING)
sendAnalyticsEvent(NFTEventName.NFT_PROFILE_PAGE_START_SELL, {
list_quantity: sellAssets.length,
collection_addresses: sellAssets.map((asset) => asset.asset_contract.address),
token_ids: sellAssets.map((asset) => asset.tokenId),
})
}}
>
<Trans>Continue</Trans>
</ContinueButton>
)}
</BagContainer>

View File

@@ -4,8 +4,7 @@ import { parseEther } from '@ethersproject/units'
import { t, Trans } from '@lingui/macro'
import { sendAnalyticsEvent, TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, NFTEventName } from '@uniswap/analytics-events'
import { formatPriceImpact } from '@uniswap/conedison/format'
import { Currency, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core'
import { Currency, CurrencyAmount, Token, TradeType } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import Column from 'components/Column'
import Loader from 'components/Loader'
@@ -25,6 +24,7 @@ import { useBag } from 'nft/hooks/useBag'
import useDerivedPayWithAnyTokenSwapInfo from 'nft/hooks/useDerivedPayWithAnyTokenSwapInfo'
import usePayWithAnyTokenSwap from 'nft/hooks/usePayWithAnyTokenSwap'
import usePermit2Approval from 'nft/hooks/usePermit2Approval'
import { PriceImpact, usePriceImpact } from 'nft/hooks/usePriceImpact'
import { useTokenInput } from 'nft/hooks/useTokenInput'
import { useWalletBalance } from 'nft/hooks/useWalletBalance'
import { BagStatus } from 'nft/types'
@@ -35,14 +35,9 @@ import { useToggleWalletModal } from 'state/application/hooks'
import { InterfaceTrade, TradeState } from 'state/routing/types'
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme'
import { computeFiatValuePriceImpact } from 'utils/computeFiatValuePriceImpact'
import { warningSeverity } from 'utils/prices'
import { switchChain } from 'utils/switchChain'
import shallow from 'zustand/shallow'
const LOW_SEVERITY_THRESHOLD = 1
const MEDIUM_SEVERITY_THRESHOLD = 3
const FooterContainer = styled.div`
padding: 0px 12px;
`
@@ -124,7 +119,7 @@ const PayButton = styled.button<{ $backgroundColor: string; $color: string }>`
const FiatLoadingBubble = styled(LoadingBubble)`
border-radius: 4px;
width: 4rem;
height: 1rem;
height: 20px;
align-self: end;
`
const PriceImpactContainer = styled(Row)`
@@ -240,13 +235,11 @@ const InputCurrencyValue = ({
const FiatValue = ({
usdcValue,
priceImpact,
priceImpactColor,
tradeState,
usingPayWithAnyToken,
}: {
usdcValue: CurrencyAmount<Token> | null
priceImpact: Percent | undefined
priceImpactColor: string | undefined
priceImpact: PriceImpact | undefined
tradeState: TradeState
usingPayWithAnyToken: boolean
}) => {
@@ -260,13 +253,13 @@ const FiatValue = ({
return (
<PriceImpactContainer>
{priceImpact && priceImpactColor && (
{priceImpact && (
<>
<MouseoverTooltip text={t`The estimated difference between the USD values of input and output amounts.`}>
<PriceImpactRow>
<AlertTriangle color={priceImpactColor} size="16px" />
<ThemedText.BodySmall style={{ color: priceImpactColor }} lineHeight="20px">
(<Trans>{formatPriceImpact(priceImpact)}</Trans>)
<AlertTriangle color={priceImpact.priceImpactSeverity.color} size="16px" />
<ThemedText.BodySmall style={{ color: priceImpact.priceImpactSeverity.color }} lineHeight="20px">
(<Trans>{priceImpact.displayPercentage()}</Trans>)
</ThemedText.BodySmall>
</PriceImpactRow>
</MouseoverTooltip>
@@ -342,30 +335,11 @@ export const BagFooter = ({ totalEthPrice, fetchAssets, eventProperties }: BagFo
shouldUsePayWithAnyToken
)
usePayWithAnyTokenSwap(trade, allowance, allowedSlippage)
const priceImpact = usePriceImpact(trade)
const fiatValueTradeInput = useStablecoinValue(trade?.inputAmount)
const fiatValueTradeOutput = useStablecoinValue(parsedOutputAmount)
const usdcValue = usingPayWithAnyToken ? fiatValueTradeInput : fiatValueTradeOutput
const stablecoinPriceImpact = useMemo(
() =>
tradeState === TradeState.SYNCING || !trade
? undefined
: computeFiatValuePriceImpact(fiatValueTradeInput, fiatValueTradeOutput),
[fiatValueTradeInput, fiatValueTradeOutput, tradeState, trade]
)
const { priceImpactWarning, priceImpactColor } = useMemo(() => {
const severity = warningSeverity(stablecoinPriceImpact)
if (severity < LOW_SEVERITY_THRESHOLD) {
return { priceImpactWarning: false, priceImpactColor: undefined }
}
if (severity < MEDIUM_SEVERITY_THRESHOLD) {
return { priceImpactWarning: false, priceImpactColor: theme.accentWarning }
}
return { priceImpactWarning: true, priceImpactColor: theme.accentCritical }
}, [stablecoinPriceImpact, theme.accentCritical, theme.accentWarning])
const { balance: balanceInEth } = useWalletBalance()
const sufficientBalance = useMemo(() => {
@@ -468,11 +442,11 @@ export const BagFooter = ({ totalEthPrice, fetchAssets, eventProperties }: BagFo
warningTextColor = theme.accentAction
warningText = <Trans>Price updated</Trans>
buttonText = <Trans>Pay</Trans>
} else if (priceImpactWarning && priceImpactColor) {
} else if (priceImpact && priceImpact.priceImpactSeverity.type === 'error') {
disabled = false
buttonColor = priceImpactColor
buttonColor = priceImpact.priceImpactSeverity.color
helperText = <Trans>Price impact warning</Trans>
helperTextColor = priceImpactColor
helperTextColor = priceImpact.priceImpactSeverity.color
buttonText = <Trans>Pay Anyway</Trans>
} else if (sufficientBalance === true) {
disabled = false
@@ -506,8 +480,7 @@ export const BagFooter = ({ totalEthPrice, fetchAssets, eventProperties }: BagFo
usingPayWithAnyToken,
tradeState,
allowance.state,
priceImpactWarning,
priceImpactColor,
priceImpact,
connector,
toggleWalletModal,
setBagExpanded,
@@ -522,6 +495,8 @@ export const BagFooter = ({ totalEthPrice, fetchAssets, eventProperties }: BagFo
...eventProperties,
}
console.log(bagStatus)
return (
<FooterContainer>
<Footer>
@@ -562,8 +537,7 @@ export const BagFooter = ({ totalEthPrice, fetchAssets, eventProperties }: BagFo
</CurrencyRow>
<FiatValue
usdcValue={usdcValue}
priceImpact={stablecoinPriceImpact}
priceImpactColor={priceImpactColor}
priceImpact={priceImpact}
tradeState={tradeState}
usingPayWithAnyToken={usingPayWithAnyToken}
/>
@@ -584,8 +558,7 @@ export const BagFooter = ({ totalEthPrice, fetchAssets, eventProperties }: BagFo
</Row>
<FiatValue
usdcValue={usdcValue}
priceImpact={stablecoinPriceImpact}
priceImpactColor={priceImpactColor}
priceImpact={priceImpact}
tradeState={tradeState}
usingPayWithAnyToken={usingPayWithAnyToken}
/>
@@ -602,7 +575,7 @@ export const BagFooter = ({ totalEthPrice, fetchAssets, eventProperties }: BagFo
<Helper color={helperTextColor}>{helperText}</Helper>
<ActionButton
onClick={handleClick}
disabled={disabled}
disabled={disabled || isPending}
backgroundColor={buttonColor}
textColor={buttonTextColor}
>

View File

@@ -1,333 +0,0 @@
import { Plural, t } from '@lingui/macro'
import { NftListV2Variant, useNftListV2Flag } from 'featureFlags/flags/nftListV2'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import ms from 'ms.macro'
import { Box } from 'nft/components/Box'
import { Row } from 'nft/components/Flex'
import { ArrowRightIcon, HazardIcon, LoadingIcon, XMarkIcon } from 'nft/components/icons'
import { BelowFloorWarningModal } from 'nft/components/profile/list/Modal/BelowFloorWarningModal'
import { bodySmall } from 'nft/css/common.css'
import { themeVars } from 'nft/css/sprinkles.css'
import { useNFTList, useSellAsset } from 'nft/hooks'
import { Listing, ListingStatus, WalletAsset } from 'nft/types'
import { pluralize } from 'nft/utils/roundAndPluralize'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTheme } from 'styled-components/macro'
import shallow from 'zustand/shallow'
import * as styles from './ListingModal.css'
import { getListings } from './utils'
const BELOW_FLOOR_PRICE_THRESHOLD = 0.8
interface ListingButtonProps {
onClick: () => void
buttonText: string
showWarningOverride?: boolean
}
export const ListingButton = ({ onClick, buttonText, showWarningOverride = false }: ListingButtonProps) => {
const {
addMarketplaceWarning,
sellAssets,
removeAllMarketplaceWarnings,
showResolveIssues,
toggleShowResolveIssues,
issues,
setIssues,
} = useSellAsset(
({
addMarketplaceWarning,
sellAssets,
removeAllMarketplaceWarnings,
showResolveIssues,
toggleShowResolveIssues,
issues,
setIssues,
}) => ({
addMarketplaceWarning,
sellAssets,
removeAllMarketplaceWarnings,
showResolveIssues,
toggleShowResolveIssues,
issues,
setIssues,
}),
shallow
)
const { listingStatus, setListingStatus, setListings, setCollectionsRequiringApproval } = useNFTList(
({ listingStatus, setListingStatus, setListings, setCollectionsRequiringApproval }) => ({
listingStatus,
setListingStatus,
setListings,
setCollectionsRequiringApproval,
}),
shallow
)
const isNftListV2 = useNftListV2Flag() === NftListV2Variant.Enabled
const [showWarning, setShowWarning] = useState(false)
const [canContinue, setCanContinue] = useState(false)
const theme = useTheme()
const warningRef = useRef<HTMLDivElement>(null)
useOnClickOutside(warningRef, () => {
!isNftListV2 && setShowWarning(false)
})
useEffect(() => {
const [newCollectionsToApprove, newListings] = getListings(sellAssets)
setListings(newListings)
setCollectionsRequiringApproval(newCollectionsToApprove)
setListingStatus(ListingStatus.DEFINED)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [sellAssets])
const [
noMarketplacesSelected,
missingExpiration,
invalidExpiration,
overMaxExpiration,
listingsMissingPrice,
listingsBelowFloor,
listingsAboveSellOrderFloor,
invalidPrices,
] = useMemo(() => {
const noMarketplacesSelected = sellAssets.some((asset: WalletAsset) => asset.marketplaces === undefined)
const missingExpiration = sellAssets.some((asset) => {
return (
asset.expirationTime != null &&
(isNaN(asset.expirationTime) || asset.expirationTime * 1000 - Date.now() < ms`60 seconds`)
)
})
const invalidExpiration = sellAssets.some((asset) => {
return asset.expirationTime != null && isNaN(asset.expirationTime)
})
const overMaxExpiration = sellAssets.some((asset) => {
return asset.expirationTime != null && asset.expirationTime - Date.now() > ms`180 days`
})
const listingsMissingPrice: [WalletAsset, Listing][] = []
const listingsBelowFloor: [WalletAsset, Listing][] = []
const listingsAboveSellOrderFloor: [WalletAsset, Listing][] = []
const invalidPrices: [WalletAsset, Listing][] = []
for (const asset of sellAssets) {
if (asset.newListings) {
for (const listing of asset.newListings) {
if (!listing.price) listingsMissingPrice.push([asset, listing])
else if (isNaN(listing.price) || listing.price < 0) invalidPrices.push([asset, listing])
else if (
listing.price < (asset?.floorPrice ?? 0) * BELOW_FLOOR_PRICE_THRESHOLD &&
!listing.overrideFloorPrice
)
listingsBelowFloor.push([asset, listing])
else if (asset.floor_sell_order_price && listing.price >= asset.floor_sell_order_price)
listingsAboveSellOrderFloor.push([asset, listing])
}
}
}
// set number of issues
if (isNftListV2) {
const foundIssues =
Number(missingExpiration) +
Number(overMaxExpiration) +
listingsMissingPrice.length +
listingsAboveSellOrderFloor.length
setIssues(foundIssues)
!foundIssues && showResolveIssues && toggleShowResolveIssues()
// Only show Resolve Issue text if there was a user submitted error (ie not when page loads with no prices set)
if ((missingExpiration || overMaxExpiration || listingsAboveSellOrderFloor.length) && !showResolveIssues)
toggleShowResolveIssues()
}
const continueCheck = listingsBelowFloor.length === 0 && listingsAboveSellOrderFloor.length === 0
setCanContinue(continueCheck)
return [
noMarketplacesSelected,
missingExpiration,
invalidExpiration,
overMaxExpiration,
listingsMissingPrice,
listingsBelowFloor,
listingsAboveSellOrderFloor,
invalidPrices,
]
}, [isNftListV2, sellAssets, setIssues, showResolveIssues, toggleShowResolveIssues])
const [disableListButton, warningMessage] = useMemo(() => {
const disableListButton =
noMarketplacesSelected ||
missingExpiration ||
invalidExpiration ||
overMaxExpiration ||
invalidPrices.length > 0 ||
listingsMissingPrice.length > 0
const warningMessage = noMarketplacesSelected
? 'No marketplaces selected'
: missingExpiration
? 'Set duration'
: invalidExpiration
? 'Invalid duration'
: overMaxExpiration
? 'Max duration is 6 months'
: listingsMissingPrice.length > 0
? `${listingsMissingPrice.length} item price${pluralize(listingsMissingPrice.length)} not set`
: invalidPrices.length > 0
? `${invalidPrices.length} price${pluralize(invalidPrices.length)} are invalid`
: listingsBelowFloor.length > 0
? `${listingsBelowFloor.length} item${pluralize(listingsBelowFloor.length)} listed below floor`
: listingsAboveSellOrderFloor.length > 0
? `${listingsAboveSellOrderFloor.length} item${pluralize(listingsAboveSellOrderFloor.length)} already listed`
: ''
return [disableListButton, warningMessage]
}, [
noMarketplacesSelected,
missingExpiration,
invalidExpiration,
overMaxExpiration,
listingsMissingPrice,
invalidPrices,
listingsBelowFloor,
listingsAboveSellOrderFloor,
])
useEffect(() => {
setShowWarning(false)
}, [warningMessage])
const addWarningMessages = () => {
removeAllMarketplaceWarnings()
if (!missingExpiration && !noMarketplacesSelected) {
if (listingsMissingPrice.length > 0) {
for (const [asset, listing] of listingsMissingPrice) {
addMarketplaceWarning(asset, {
message: 'PLEASE SET A PRICE',
marketplace: listing.marketplace,
})
}
} else if (invalidPrices.length > 0) {
for (const [asset, listing] of invalidPrices) {
!listing.overrideFloorPrice &&
addMarketplaceWarning(asset, {
message: `INVALID PRICE`,
marketplace: listing.marketplace,
})
}
}
}
setShowWarning(true)
}
const warningWrappedClick = () => {
if ((!disableListButton && canContinue) || showWarningOverride) {
if (issues && isNftListV2) !showResolveIssues && toggleShowResolveIssues()
else if (listingsBelowFloor.length) setShowWarning(true)
else onClick()
} else addWarningMessages()
}
return (
<>
<Box position="relative">
{!showWarningOverride && showWarning && warningMessage.length > 0 && (
<Row
className={`${bodySmall} ${styles.warningTooltip}`}
transition="250"
onClick={() => setShowWarning(false)}
color="textSecondary"
zIndex="3"
borderRadius="4"
backgroundColor="backgroundSurface"
height={!disableListButton ? '64' : '36'}
maxWidth="276"
position="absolute"
left="24"
bottom="52"
flexWrap={!disableListButton ? 'wrap' : 'nowrap'}
style={{ maxWidth: !disableListButton ? '225px' : '' }}
ref={warningRef}
>
<HazardIcon />
<Box marginLeft="4" marginRight="8">
{warningMessage}
</Box>
{disableListButton ? (
<Box paddingTop="6">
<XMarkIcon fill={themeVars.colors.textSecondary} height="20" width="20" />
</Box>
) : (
<Row
marginLeft="72"
cursor="pointer"
color="accentAction"
onClick={() => {
setShowWarning(false)
setCanContinue(true)
onClick()
}}
>
Continue
<ArrowRightIcon height="20" width="20" />
</Row>
)}
</Row>
)}
<Box
as="button"
border="none"
backgroundColor={showResolveIssues ? 'accentFailure' : 'accentAction'}
cursor={
[ListingStatus.APPROVED, ListingStatus.PENDING, ListingStatus.SIGNING].includes(listingStatus) ||
disableListButton
? 'default'
: 'pointer'
}
className={styles.button}
onClick={() => listingStatus !== ListingStatus.APPROVED && warningWrappedClick()}
type="button"
style={{
color: showResolveIssues ? theme.accentTextDarkPrimary : theme.white,
opacity:
![ListingStatus.DEFINED, ListingStatus.FAILED, ListingStatus.CONTINUE].includes(listingStatus) ||
(disableListButton && !showResolveIssues)
? 0.3
: 1,
}}
>
{listingStatus === ListingStatus.SIGNING || listingStatus === ListingStatus.PENDING ? (
isNftListV2 ? (
listingStatus === ListingStatus.PENDING ? (
'Pending'
) : (
'Proceed in wallet'
)
) : (
<Row gap="8">
<LoadingIcon stroke="backgroundSurface" height="20" width="20" />
{listingStatus === ListingStatus.PENDING ? 'Pending' : 'Proceed in wallet'}
</Row>
)
) : listingStatus === ListingStatus.APPROVED ? (
'Complete!'
) : listingStatus === ListingStatus.PAUSED ? (
'Paused'
) : listingStatus === ListingStatus.FAILED ? (
'Try again'
) : listingStatus === ListingStatus.CONTINUE ? (
'Continue'
) : showResolveIssues ? (
<Plural value={issues !== 1 ? 2 : 1} _1="Resolve issue" other={t`Resolve ${issues} issues`} />
) : (
buttonText
)}
</Box>
</Box>
{showWarning && (
<BelowFloorWarningModal
listingsBelowFloor={listingsBelowFloor}
closeModal={() => setShowWarning(false)}
startListing={onClick}
/>
)}
</>
)
}

View File

@@ -1,79 +0,0 @@
import { style } from '@vanilla-extract/css'
import { sprinkles } from 'nft/css/sprinkles.css'
export const chevron = style([
sprinkles({
height: '28',
width: '28',
transition: '250',
marginLeft: 'auto',
marginRight: '0',
}),
])
export const chevronDown = style({
transform: 'rotate(180deg)',
cursor: 'pointer',
})
export const sectionDivider = style([
sprinkles({
borderRadius: '20',
marginTop: '8',
width: 'full',
borderWidth: '0.5px',
borderStyle: 'solid',
borderColor: 'backgroundOutline',
}),
])
export const button = style([
sprinkles({
paddingX: { sm: '12', md: '16' },
paddingY: { sm: '10', md: '16' },
textAlign: 'center',
fontWeight: 'semibold',
fontSize: { sm: '16', md: '20' },
lineHeight: { sm: '20', md: '24' },
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
alignSelf: 'flex-end',
borderRadius: '12',
}),
])
export const listingModalIcon = style([
sprinkles({
borderWidth: '1px',
borderStyle: 'solid',
borderColor: 'backgroundSurface',
}),
{
boxSizing: 'border-box',
marginLeft: '-2px',
marginRight: '4px',
},
])
export const warningTooltip = style([
sprinkles({
paddingTop: '8',
paddingRight: '8',
paddingBottom: '8',
paddingLeft: '12',
}),
{
boxShadow: '0px 4px 16px rgba(10, 10, 59, 0.2)',
},
])
export const listingSectionBorder = style([
sprinkles({
padding: '8',
borderRadius: '8',
borderColor: 'backgroundOutline',
borderStyle: 'solid',
borderWidth: '1px',
}),
])

View File

@@ -1,322 +0,0 @@
import { sendAnalyticsEvent, Trace, useTrace } from '@uniswap/analytics'
import { InterfaceModalName, NFTEventName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { ChevronLeftIcon, XMarkIcon } from 'nft/components/icons'
import { caption, headlineSmall, subhead, subheadSmall } from 'nft/css/common.css'
import { themeVars } from 'nft/css/sprinkles.css'
import { useBag, useIsMobile, useNFTList, useSellAsset } from 'nft/hooks'
import { logListing, looksRareNonceFetcher } from 'nft/queries'
import { AssetRow, CollectionRow, ListingRow, ListingStatus } from 'nft/types'
import { fetchPrice } from 'nft/utils/fetchPrice'
import { pluralize } from 'nft/utils/roundAndPluralize'
import { Dispatch, useEffect, useMemo, useRef, useState } from 'react'
import shallow from 'zustand/shallow'
import { ListingButton } from './ListingButton'
import * as styles from './ListingModal.css'
import { ListingSection } from './ListingSection'
import { approveCollectionRow, getTotalEthValue, pauseRow, resetRow, signListingRow, verifyStatus } from './utils'
const ListingModal = () => {
const { provider } = useWeb3React()
const sellAssets = useSellAsset((state) => state.sellAssets)
const {
listingStatus,
setListingStatus,
setListings,
setCollectionsRequiringApproval,
setListingStatusAndCallback,
setCollectionStatusAndCallback,
looksRareNonce,
setLooksRareNonce,
getLooksRareNonce,
collectionsRequiringApproval,
listings,
} = useNFTList(
({
listingStatus,
setListingStatus,
setListings,
setCollectionsRequiringApproval,
setListingStatusAndCallback,
setCollectionStatusAndCallback,
looksRareNonce,
setLooksRareNonce,
getLooksRareNonce,
collectionsRequiringApproval,
listings,
}) => ({
listingStatus,
setListingStatus,
setListings,
setCollectionsRequiringApproval,
setListingStatusAndCallback,
setCollectionStatusAndCallback,
looksRareNonce,
setLooksRareNonce,
getLooksRareNonce,
collectionsRequiringApproval,
listings,
}),
shallow
)
const signer = provider?.getSigner()
const [openIndex, setOpenIndex] = useState(0)
const [allCollectionsApproved, setAllCollectionsApproved] = useState(false)
const toggleCart = useBag((state) => state.toggleBag)
const looksRareNonceRef = useRef(looksRareNonce)
const isMobile = useIsMobile()
const trace = useTrace({ modal: InterfaceModalName.NFT_LISTING })
useEffect(() => {
useNFTList.subscribe((state) => (looksRareNonceRef.current = state.looksRareNonce))
}, [])
const totalEthListingValue = useMemo(() => getTotalEthValue(sellAssets), [sellAssets])
const [ethPriceInUSD, setEthPriceInUSD] = useState(0)
useEffect(() => {
fetchPrice().then((price) => {
setEthPriceInUSD(price || 0)
})
}, [])
const startListingEventProperties = {
collection_addresses: sellAssets.map((asset) => asset.asset_contract.address),
token_ids: sellAssets.map((asset) => asset.tokenId),
marketplaces: Array.from(new Set(listings.map((asset) => asset.marketplace.name))),
list_quantity: listings.length,
usd_value: ethPriceInUSD * totalEthListingValue,
...trace,
}
// when all collections have been approved, auto start the signing process
useEffect(() => {
collectionsRequiringApproval?.length &&
setAllCollectionsApproved(
collectionsRequiringApproval.every((collection: CollectionRow) => collection.status === ListingStatus.APPROVED)
)
if (
allCollectionsApproved &&
(listingStatus === ListingStatus.PENDING ||
listingStatus === ListingStatus.CONTINUE ||
listingStatus === ListingStatus.SIGNING)
) {
resetAllRows()
signListings()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [collectionsRequiringApproval, allCollectionsApproved])
const allCollectionsApprovedOrPaused = useMemo(
() =>
collectionsRequiringApproval.every(
(collection: CollectionRow) =>
collection.status === ListingStatus.APPROVED || collection.status === ListingStatus.PAUSED
),
[collectionsRequiringApproval]
)
const allListingsApprovedOrPaused = useMemo(
() =>
listings.every(
(listing: ListingRow) => listing.status === ListingStatus.APPROVED || listing.status === ListingStatus.PAUSED
),
[listings]
)
// go back to a ready state after a successful retry
useEffect(() => {
if (listingStatus === ListingStatus.SIGNING && allCollectionsApprovedOrPaused && allListingsApprovedOrPaused) {
resetAllRows()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [allCollectionsApprovedOrPaused, allListingsApprovedOrPaused])
// handles the modal wide listing state based on conglomeration of the wallet, collection, and listing states
const startListingFlow = async () => {
if (!signer) return
sendAnalyticsEvent(NFTEventName.NFT_SELL_START_LISTING, { ...startListingEventProperties })
setListingStatus(ListingStatus.SIGNING)
const signerAddress = await signer.getAddress()
const nonce = await looksRareNonceFetcher(signerAddress)
setLooksRareNonce(nonce ?? 0)
if (!collectionsRequiringApproval?.some((collection) => collection.status === ListingStatus.PAUSED)) {
setListingStatus(ListingStatus.SIGNING)
setOpenIndex(1)
}
// for all unique collection, marketplace combos -> approve collections
for (const collectionRow of collectionsRequiringApproval) {
verifyStatus(collectionRow.status) &&
(isMobile
? await approveCollectionRow(collectionRow, signer, setCollectionStatusAndCallback, pauseAllRows)
: approveCollectionRow(collectionRow, signer, setCollectionStatusAndCallback, pauseAllRows))
}
}
const signListings = async () => {
if (!signer || !provider) return
setListingStatus(ListingStatus.SIGNING)
setOpenIndex(2)
// sign listings
for (const listing of listings) {
verifyStatus(listing.status) &&
(await signListingRow(
listing,
signer,
provider,
getLooksRareNonce,
setLooksRareNonce,
setListingStatusAndCallback,
pauseAllRows
))
}
const allListingsSigned = listings.every((listing: ListingRow) => listing.status === ListingStatus.APPROVED)
const paused = listings.some((listing: ListingRow) => listing.status === ListingStatus.PAUSED)
if (allListingsSigned) {
setOpenIndex(0)
setListingStatus(ListingStatus.APPROVED)
} else if (!paused) {
setListingStatus(ListingStatus.FAILED)
}
sendAnalyticsEvent(NFTEventName.NFT_LISTING_COMPLETED, {
signatures_approved: listings.filter((asset) => asset.status === ListingStatus.APPROVED),
list_quantity: listings.length,
usd_value: ethPriceInUSD * totalEthListingValue,
...trace,
})
await logListing(listings, (await signer?.getAddress()) ?? '')
}
const pauseAllRows = () => {
for (const collection of collectionsRequiringApproval) {
pauseRow(collection, collectionsRequiringApproval, setCollectionsRequiringApproval as Dispatch<AssetRow[]>)
}
for (const listing of listings) {
pauseRow(listing, listings, setListings as Dispatch<AssetRow[]>)
}
}
const resetAllRows = () => {
for (const collection of collectionsRequiringApproval) {
resetRow(collection, collectionsRequiringApproval, setCollectionsRequiringApproval as Dispatch<AssetRow[]>)
}
for (const listing of listings) {
resetRow(listing, listings, setListings as Dispatch<AssetRow[]>)
}
}
const clickStopListing = () => {
pauseAllRows()
}
const clickStartListingFlow = () => {
resetAllRows()
allCollectionsApproved ? signListings() : startListingFlow()
}
const showSuccessScreen = useMemo(() => listingStatus === ListingStatus.APPROVED, [listingStatus])
return (
<Trace modal={InterfaceModalName.NFT_LISTING}>
<Column paddingTop="20" paddingBottom="20" paddingLeft="12" paddingRight="12">
<Row className={headlineSmall} marginBottom="10">
{isMobile && !showSuccessScreen && (
<Box paddingTop="4" marginRight="4" onClick={toggleCart}>
<ChevronLeftIcon height={28} width={28} />
</Box>
)}
{showSuccessScreen ? 'Success!' : `Listing ${sellAssets.length} NFTs`}
<Box
as="button"
border="none"
color="textSecondary"
backgroundColor="backgroundSurface"
marginLeft="auto"
marginRight="0"
paddingRight="0"
display={{ sm: 'flex', md: 'none' }}
cursor="pointer"
onClick={toggleCart}
>
<XMarkIcon height={28} width={28} fill={themeVars.colors.textPrimary} />
</Box>
</Row>
<Column overflowX="hidden" overflowY="auto" style={{ maxHeight: '60vh' }}>
{showSuccessScreen ? (
<Trace
name={NFTEventName.NFT_LISTING_COMPLETED}
properties={{ list_quantity: listings.length, usd_value: ethPriceInUSD * totalEthListingValue, ...trace }}
shouldLogImpression
>
<ListingSection
sectionTitle={`Listed ${listings.length} item${pluralize(listings.length)} for sale`}
rows={listings}
index={0}
openIndex={openIndex}
isSuccessScreen={true}
/>
</Trace>
) : (
<>
<ListingSection
sectionTitle={`Approve ${collectionsRequiringApproval.length} collection${pluralize(
collectionsRequiringApproval.length
)}`}
title="COLLECTIONS"
rows={collectionsRequiringApproval}
index={1}
openIndex={openIndex}
/>
<ListingSection
sectionTitle={`Confirm ${listings.length} listing${pluralize(listings.length)}`}
caption="Now you can sign to list each item"
title="NFTS"
rows={listings}
index={2}
openIndex={openIndex}
/>
</>
)}
</Column>
<hr className={styles.sectionDivider} />
<Row className={subhead} marginTop="12" marginBottom={showSuccessScreen ? '8' : '20'}>
Return if sold
<Row className={subheadSmall} marginLeft="auto" marginRight="0">
{totalEthListingValue}
&nbsp;ETH
</Row>
</Row>
{showSuccessScreen ? (
<Box as="span" className={caption} color="textSecondary">
Status:{' '}
<Box as="span" color="accentSuccess">
Confirmed
</Box>
</Box>
) : (
<ListingButton onClick={clickStartListingFlow} buttonText="Start listing" showWarningOverride={isMobile} />
)}
{(listingStatus === ListingStatus.PENDING || listingStatus === ListingStatus.SIGNING) && (
<Box
as="button"
border="none"
backgroundColor="backgroundSurface"
cursor="pointer"
color="orange"
className={styles.button}
onClick={clickStopListing}
type="button"
>
Stop listing
</Box>
)}
</Column>
</Trace>
)
}
export default ListingModal

View File

@@ -1,187 +0,0 @@
import clsx from 'clsx'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { ApprovedCheckmarkIcon, ChevronUpIcon, FailedListingIcon, LoadingIcon } from 'nft/components/icons'
import { badge, bodySmall, buttonTextSmall, subhead } from 'nft/css/common.css'
import { useSellAsset } from 'nft/hooks'
import { AssetRow, CollectionRow, ListingRow, ListingStatus } from 'nft/types'
import { formatEthPrice, numberToWei } from 'nft/utils/currency'
import { useEffect, useState } from 'react'
import * as styles from './ListingModal.css'
export const ListingSection = ({
sectionTitle,
caption = undefined,
title = undefined,
rows,
index,
openIndex,
isSuccessScreen = false,
}: {
sectionTitle: string
caption?: string
title?: string
rows: AssetRow[]
index: number
openIndex: number
isSuccessScreen?: boolean
}) => {
const [isOpen, setIsOpen] = useState(false)
const notAllApproved = rows.some((row: AssetRow) => row.status !== ListingStatus.APPROVED)
const sellAssets = useSellAsset((state) => state.sellAssets)
const removeAssetMarketplace = useSellAsset((state) => state.removeAssetMarketplace)
const removeRow = (row: any) => {
// collections
if (index === 1) {
for (const asset of sellAssets)
if (asset.asset_contract.address === row.collectionAddress) removeAssetMarketplace(asset, row.marketplace)
}
// listings
else removeAssetMarketplace(row.asset, row.marketplace)
}
useEffect(() => {
setIsOpen(index === openIndex)
}, [index, openIndex])
function getListingRowPrice(row: AssetRow): number | undefined {
const listingRow = row as ListingRow
const newListings = listingRow.asset.newListings
return newListings?.find((listing) => listing.marketplace.name === listingRow.marketplace.name)?.price ?? 0
}
const allApproved = !notAllApproved && rows.length > 0 && !isSuccessScreen
return (
<Row
flexWrap="wrap"
className={subhead}
marginTop="10"
marginBottom="10"
onClick={() => rows.length > 0 && setIsOpen(!isOpen)}
color={allApproved ? 'accentSuccess' : 'textPrimary'}
>
{allApproved && <ApprovedCheckmarkIcon style={{ marginRight: '8px' }} />}
{sectionTitle}
{!isSuccessScreen && <ChevronUpIcon className={clsx(`${isOpen ? '' : styles.chevronDown} ${styles.chevron}`)} />}
{(isOpen || isSuccessScreen) && (
<Column
gap="12"
width="full"
paddingTop={isSuccessScreen ? '28' : 'auto'}
className={clsx(!isSuccessScreen && styles.listingSectionBorder)}
>
{caption && (
<Box color="textPrimary" fontWeight="normal" className={caption}>
{caption}
</Box>
)}
{title && (
<Box color="textSecondary" className={badge}>
{title}
</Box>
)}
<Column gap="8">
{rows.map((row: AssetRow, index) => {
return (
<Column key={index} gap="8">
<Row>
{row.images?.map((image, index) => {
return (
<Box
as="img"
height="20"
width="20"
borderRadius={index === 0 && (row as CollectionRow).collectionAddress ? 'round' : '4'}
style={{ zIndex: 2 - index }}
className={styles.listingModalIcon}
src={image}
alt={row.name}
key={index}
/>
)
})}
<Box
marginLeft="8"
marginRight="auto"
fontWeight="normal"
color="textPrimary"
textOverflow="ellipsis"
overflow="hidden"
whiteSpace="nowrap"
maxWidth={{
sm: 'max',
md:
row.status === ListingStatus.REJECTED || row.status === ListingStatus.FAILED ? '120' : 'full',
}}
className={bodySmall}
>
{row.name}
</Box>
{isSuccessScreen ? (
getListingRowPrice(row) &&
`${formatEthPrice(numberToWei(getListingRowPrice(row) ?? 0).toString())} ETH`
) : row.status === ListingStatus.APPROVED ? (
<ApprovedCheckmarkIcon height="20" width="20" />
) : row.status === ListingStatus.FAILED || row.status === ListingStatus.REJECTED ? (
<Row gap="4">
<Box fontWeight="normal" fontSize="14" color="textSecondary">
{row.status}
</Box>
<FailedListingIcon />
</Row>
) : (
row.status === ListingStatus.SIGNING && <LoadingIcon height="20" width="20" stroke="#4673FA" />
)}
</Row>
{(row.status === ListingStatus.FAILED || row.status === ListingStatus.REJECTED) && (
<Row gap="8" justifyContent="center">
<Box
width="120"
as="button"
className={buttonTextSmall}
borderRadius="12"
border="none"
color="red400"
height="32"
cursor="pointer"
style={{ backgroundColor: '#FA2B391A' }}
onClick={async (e) => {
e.stopPropagation()
removeRow(row)
}}
>
Remove
</Box>
<Box
width="120"
as="button"
className={buttonTextSmall}
borderRadius="12"
border="none"
color="accentAction"
height="32"
cursor="pointer"
style={{ backgroundColor: '#4C82FB29' }}
onClick={async (e) => {
e.stopPropagation()
if (row.callback) {
await row.callback()
}
}}
>
Try again
</Box>
</Row>
)}
</Column>
)
})}
</Column>
</Column>
)}
</Row>
)
}

View File

@@ -2,30 +2,8 @@ import type { JsonRpcSigner, Web3Provider } from '@ethersproject/providers'
import { addressesByNetwork, SupportedChainId } from '@looksrare/sdk'
import { LOOKSRARE_MARKETPLACE_CONTRACT, X2Y2_TRANSFER_CONTRACT } from 'nft/queries'
import { OPENSEA_CROSS_CHAIN_CONDUIT } from 'nft/queries/openSea'
import { AssetRow, CollectionRow, ListingMarket, ListingRow, ListingStatus, WalletAsset } from 'nft/types'
import { CollectionRow, ListingMarket, ListingRow, ListingStatus, WalletAsset } from 'nft/types'
import { approveCollection, LOOKS_RARE_CREATOR_BASIS_POINTS, signListing } from 'nft/utils/listNfts'
import { Dispatch } from 'react'
const updateStatus = ({
listing,
newStatus,
rows,
setRows,
callback,
}: {
listing: AssetRow
newStatus: ListingStatus
rows: AssetRow[]
setRows: Dispatch<AssetRow[]>
callback?: () => Promise<void>
}) => {
const rowsCopy = [...rows]
const index = rows.findIndex((n) => n === listing)
listing.status = newStatus
if (callback) listing.callback = callback
rowsCopy[index] = listing
setRows(rowsCopy)
}
export async function approveCollectionRow(
collectionRow: CollectionRow,
@@ -211,27 +189,3 @@ export const getListingState = (
export const verifyStatus = (status: ListingStatus) => {
return status !== ListingStatus.PAUSED && status !== ListingStatus.APPROVED
}
export const pauseRow = (row: AssetRow, rows: AssetRow[], setRows: Dispatch<AssetRow[]>) => {
if (row.status === ListingStatus.PENDING || row.status === ListingStatus.DEFINED)
updateStatus({
listing: row,
newStatus: ListingStatus.PAUSED,
rows,
setRows,
})
}
export const resetRow = (row: AssetRow, rows: AssetRow[], setRows: Dispatch<AssetRow[]>) => {
if (
row.status === ListingStatus.PAUSED ||
row.status === ListingStatus.FAILED ||
row.status === ListingStatus.REJECTED
)
updateStatus({
listing: row,
newStatus: ListingStatus.DEFINED,
rows,
setRows,
})
}

View File

@@ -240,21 +240,36 @@ export const RarityVerifiedIcon = () => (
</svg>
)
export const EditPriceIcon = (props: SVGProps) => (
<svg {...props} width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12.4713 5.06496L13.2161 4.28041C13.5935 3.88317 13.6233 3.33696 13.2459 2.95958L12.9877 2.69144C12.65 2.35378 12.084 2.40344 11.7165 2.77089L10.9419 3.52565L12.4713 5.06496ZM3.10986 13.2347L5.14573 12.3806L11.7463 5.78L10.2169 4.26055L3.61635 10.8711L2.72255 12.8374C2.62324 13.0658 2.88145 13.324 3.10986 13.2347Z"
fill="#70757A"
/>
</svg>
)
export const AttachPriceIcon = (props: SVGProps) => (
<svg {...props} width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M7.76353 9.88671L8.53195 9.10825C7.93931 9.05803 7.51242 8.86216 7.20605 8.5558C6.35728 7.70702 6.35728 6.50669 7.20103 5.66796L8.86844 3.99553C9.71722 3.15178 10.9125 3.14676 11.7613 3.99553C12.6151 4.84932 12.6051 6.04464 11.7663 6.88839L10.9125 7.73716C11.0732 8.10881 11.1285 8.56082 11.0381 8.95256L12.4745 7.5212C13.71 6.29073 13.715 4.53292 12.4694 3.28738C11.2189 2.03682 9.47112 2.04687 8.23563 3.28236L6.48786 5.03013C5.25237 6.26562 5.24735 8.01841 6.49289 9.26394C6.78418 9.56026 7.18597 9.78124 7.76353 9.88671ZM8.23061 5.64285L7.46219 6.42131C8.05483 6.47655 8.48172 6.6674 8.78809 6.97376C9.64188 7.82254 9.63686 9.02287 8.79311 9.86662L7.1257 11.534C6.27693 12.3828 5.08161 12.3828 4.23786 11.534C3.38407 10.6802 3.38909 9.48995 4.23284 8.6462L5.08161 7.7924C4.9209 7.42577 4.87068 6.97376 4.95605 6.577L3.51967 8.01339C2.28418 9.24385 2.27916 10.9966 3.52469 12.2422C4.77525 13.4927 6.52302 13.4827 7.75851 12.2522L9.50628 10.4994C10.7418 9.26394 10.7468 7.51115 9.50126 6.26562C9.20996 5.97432 8.8132 5.75334 8.23061 5.64285Z"
fill="#4673FA"
/>
export const BrokenLinkIcon = (props: SVGProps) => (
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<g clipPath="url(#clip0_79_4612)">
<path
d="M14.4344 11.3181L16.9344 8.81813C17.6934 8.03229 18.1133 6.97978 18.1039 5.8873C18.0944 4.79481 17.6562 3.74976 16.8836 2.97722C16.1111 2.20469 15.066 1.76649 13.9735 1.75699C12.8811 1.7475 11.8286 2.16748 11.0427 2.92647L9.60938 4.35147"
stroke="#98A1C0"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5.20088 8.75098L2.70088 11.251C1.94189 12.0368 1.52191 13.0893 1.53141 14.1818C1.5409 15.2743 1.9791 16.3194 2.75164 17.0919C3.52417 17.8644 4.56922 18.3026 5.66171 18.3121C6.7542 18.3216 7.80671 17.9016 8.59255 17.1426L10.0175 15.7176"
stroke="#98A1C0"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5 3.24316L14.7368 16.6952"
stroke="#98A1C0"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>
<defs>
<clipPath id="clip0_79_4612">
<rect width="20" height="20" fill="white" transform="translate(0.128906 0.0341797)" />
</clipPath>
</defs>
</svg>
)
@@ -289,30 +304,6 @@ export const ClockIcon = () => (
</svg>
)
export const LoadingIcon = (props: SVGProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
width="100px"
height="100px"
viewBox="0 0 100 100"
preserveAspectRatio="xMidYMid"
{...props}
>
<circle cx="50" cy="50" fill="none" strokeWidth="10" r="35" strokeDasharray="164.93361431346415 56.97787143782138">
<animateTransform
attributeName="transform"
type="rotate"
repeatCount="indefinite"
dur="1s"
values="0 50 50;360 50 50"
keyTimes="0;1"
{...props}
></animateTransform>
</circle>
</svg>
)
export const ApprovedCheckmarkIcon = (props: SVGProps) => (
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
@@ -322,25 +313,6 @@ export const ApprovedCheckmarkIcon = (props: SVGProps) => (
</svg>
)
export const HazardIcon = () => (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="8.57227" y="6.66669" width="2.85714" height="8.57143" fill="white" />
<path
d="M5.14158 15.7143H14.8489C15.8017 15.7143 16.4294 15.0318 16.4294 14.1977C16.4294 13.9377 16.3509 13.6885 16.2052 13.4502L11.3516 5.05483C11.0489 4.53486 10.5221 4.28571 9.99523 4.28571C9.46839 4.28571 8.93034 4.53486 8.6389 5.05483L3.78524 13.4502C3.63952 13.6885 3.57227 13.9377 3.57227 14.1977C3.57227 15.0318 4.18878 15.7143 5.14158 15.7143ZM9.99523 11.4245C9.56928 11.4245 9.25542 11.1428 9.24421 10.7312L9.15453 7.71969C9.14332 7.24305 9.49081 6.90724 9.99523 6.90724C10.5109 6.90724 10.8584 7.23222 10.8471 7.71969L10.7575 10.7312C10.7351 11.1428 10.4324 11.4245 9.99523 11.4245ZM9.99523 14.0677C9.43477 14.0677 8.98639 13.6452 8.98639 13.1036C8.98639 12.5728 9.43477 12.1503 9.99523 12.1503C10.5669 12.1503 11.0153 12.5836 11.0153 13.1036C11.0041 13.6452 10.5669 14.0677 9.99523 14.0677Z"
fill="#F95E14"
/>
</svg>
)
export const FailedListingIcon = (props: SVGProps) => (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
d="M9.9933 16.2444C13.5529 16.2444 16.4909 13.3064 16.4909 9.75307C16.4909 6.19978 13.5466 3.26172 9.98703 3.26172C6.43373 3.26172 3.50195 6.19978 3.50195 9.75307C3.50195 13.3064 6.44001 16.2444 9.9933 16.2444ZM8.12877 12.3207C7.78976 12.3207 7.62653 12.1324 7.62653 11.8624V7.63742C7.62653 7.36747 7.78976 7.17913 8.12877 7.17913H8.80678C9.14579 7.17913 9.30901 7.36747 9.30901 7.63742V11.8624C9.30901 12.1324 9.14579 12.3207 8.80678 12.3207H8.12877ZM11.1798 12.3207C10.8471 12.3207 10.6776 12.1324 10.6776 11.8624V7.63742C10.6776 7.36747 10.8471 7.17913 11.1798 7.17913H11.8641C12.1906 7.17913 12.3538 7.36747 12.3538 7.63742V11.8624C12.3538 12.1324 12.1906 12.3207 11.8641 12.3207H11.1798Z"
fill={themeVars.colors.textSecondary}
/>
</svg>
)
export const FilterIcon = (props: SVGProps) => (
<svg width="20" height="20" viewBox="1 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path

View File

@@ -16,6 +16,6 @@ export const overlay = style([
{
opacity: 0.72,
overflow: 'hidden',
zIndex: Z_INDEX.modalBackdrop + 1,
zIndex: Z_INDEX.modalBackdrop - 2,
},
])

View File

@@ -1,14 +1,12 @@
import { t, Trans } from '@lingui/macro'
import { Trans } from '@lingui/macro'
import { sendAnalyticsEvent, useTrace } from '@uniswap/analytics'
import { InterfaceModalName, NFTEventName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import Column from 'components/Column'
import Row from 'components/Row'
import { SMALL_MEDIA_BREAKPOINT } from 'components/Tokens/constants'
import { NftListV2Variant, useNftListV2Flag } from 'featureFlags/flags/nftListV2'
import { ListingButton } from 'nft/components/bag/profile/ListingButton'
import { approveCollectionRow, getListingState, getTotalEthValue, verifyStatus } from 'nft/components/bag/profile/utils'
import { useBag, useIsMobile, useNFTList, useProfilePageState, useSellAsset } from 'nft/hooks'
import { ListingButton } from 'nft/components/profile/list/ListingButton'
import { useIsMobile, useNFTList, useProfilePageState, useSellAsset } from 'nft/hooks'
import { LIST_PAGE_MARGIN, LIST_PAGE_MARGIN_MOBILE } from 'nft/pages/profile/shared'
import { looksRareNonceFetcher } from 'nft/queries'
import { ListingStatus, ProfilePageStateType } from 'nft/types'
@@ -16,7 +14,7 @@ import { fetchPrice, formatEth, formatUsdPrice } from 'nft/utils'
import { ListingMarkets } from 'nft/utils/listNfts'
import { useEffect, useMemo, useReducer, useState } from 'react'
import { ArrowLeft } from 'react-feather'
import styled, { css } from 'styled-components/macro'
import styled from 'styled-components/macro'
import { BREAKPOINTS, ThemedText } from 'theme'
import { Z_INDEX } from 'theme/zIndex'
import shallow from 'zustand/shallow'
@@ -81,20 +79,11 @@ const ButtonsWrapper = styled(Row)`
width: min-content;
`
const MarketWrap = styled.section<{ isNftListV2: boolean }>`
const MarketWrap = styled.section`
gap: 48px;
margin: 0px auto;
width: 100%;
max-width: 1200px;
${({ isNftListV2 }) => !isNftListV2 && v1Padding}
`
const v1Padding = css`
padding: 0px 16px;
@media screen and (min-width: ${SMALL_MEDIA_BREAKPOINT}) {
padding: 0px 44px;
}
`
const ListingHeaderRow = styled(Row)`
@@ -112,15 +101,6 @@ const GridWrapper = styled.div`
margin-bottom: 48px;
`
const MobileListButtonWrapper = styled.div`
display: flex;
margin: 14px 16px 32px 16px;
@media screen and (min-width: ${SMALL_MEDIA_BREAKPOINT}) {
display: none;
}
`
const FloatingConfirmationBar = styled(Row)<{ issues: boolean }>`
padding: 12px 12px 12px 32px;
border: 1px solid;
@@ -195,9 +175,7 @@ const EthValueWrapper = styled.span<{ totalEthListingValue: boolean }>`
export const ListPage = () => {
const { setProfilePageState: setSellPageState } = useProfilePageState()
const { provider } = useWeb3React()
const toggleBag = useBag((s) => s.toggleBag)
const isMobile = useIsMobile()
const isNftListV2 = useNftListV2Flag() === NftListV2Variant.Enabled
const trace = useTrace({ modal: InterfaceModalName.NFT_LISTING })
const { setGlobalMarketplaces, sellAssets, issues } = useSellAsset(
({ setGlobalMarketplaces, sellAssets, issues }) => ({
@@ -234,7 +212,6 @@ export const ListPage = () => {
)
const totalEthListingValue = useMemo(() => getTotalEthValue(sellAssets), [sellAssets])
const anyListingsMissingPrice = useMemo(() => !!listings.find((listing) => !listing.price), [listings])
const [showListModal, toggleShowListModal] = useReducer((s) => !s, false)
const [selectedMarkets, setSelectedMarkets] = useState([ListingMarkets[0]]) // default marketplace: x2y2
const [ethPriceInUSD, setEthPriceInUSD] = useState(0)
@@ -291,7 +268,7 @@ export const ListPage = () => {
}
}
const handleV2Click = () => {
const showModalAndStartListing = () => {
toggleShowListModal()
startListingFlow()
}
@@ -308,7 +285,7 @@ export const ListPage = () => {
return (
<Column>
<MarketWrap isNftListV2={isNftListV2}>
<MarketWrap>
<ListingHeader>
<Row>
<ArrowContainer>
@@ -332,39 +309,24 @@ export const ListPage = () => {
<NFTListingsGrid selectedMarkets={selectedMarkets} />
</GridWrapper>
</MarketWrap>
{isNftListV2 && (
<>
<FloatingConfirmationBar issues={!!issues}>
{BannerText}
<ProceedsAndButtonWrapper>
<ProceedsWrapper>
<EthValueWrapper totalEthListingValue={!!totalEthListingValue}>
{totalEthListingValue > 0 ? formatEth(totalEthListingValue) : '-'} ETH
</EthValueWrapper>
{!!totalEthListingValue && !!ethPriceInUSD && (
<UsdValue>{formatUsdPrice(totalEthListingValue * ethPriceInUSD)}</UsdValue>
)}
</ProceedsWrapper>
<ListingButton
onClick={handleV2Click}
buttonText={anyListingsMissingPrice && !isMobile ? t`Set prices to continue` : t`Start listing`}
showWarningOverride={true}
/>
</ProceedsAndButtonWrapper>
</FloatingConfirmationBar>
<Overlay />
</>
)}
{!isNftListV2 && (
<MobileListButtonWrapper>
<ListingButton onClick={toggleBag} buttonText="Continue listing" />
</MobileListButtonWrapper>
)}
{isNftListV2 && showListModal && (
<>
<ListModal overlayClick={toggleShowListModal} />
</>
)}
<FloatingConfirmationBar issues={!!issues}>
{BannerText}
<ProceedsAndButtonWrapper>
<ProceedsWrapper>
<EthValueWrapper totalEthListingValue={!!totalEthListingValue}>
{totalEthListingValue > 0 ? formatEth(totalEthListingValue) : '-'} ETH
</EthValueWrapper>
{!!totalEthListingValue && !!ethPriceInUSD && (
<UsdValue>{formatUsdPrice(totalEthListingValue * ethPriceInUSD)}</UsdValue>
)}
</ProceedsWrapper>
<ListingButton onClick={showModalAndStartListing} />
</ProceedsAndButtonWrapper>
</FloatingConfirmationBar>
<Overlay />
{showListModal && <ListModal overlayClick={toggleShowListModal} />}
</Column>
)
}

View File

@@ -0,0 +1,144 @@
import { Plural, t, Trans } from '@lingui/macro'
import { BaseButton } from 'components/Button'
import ms from 'ms.macro'
import { BelowFloorWarningModal } from 'nft/components/profile/list/Modal/BelowFloorWarningModal'
import { useIsMobile, useNFTList, useSellAsset } from 'nft/hooks'
import { Listing, ListingStatus, WalletAsset } from 'nft/types'
import { useEffect, useMemo, useState } from 'react'
import styled from 'styled-components/macro'
import { BREAKPOINTS } from 'theme'
import shallow from 'zustand/shallow'
import { getListings } from '../../bag/profile/utils'
const BELOW_FLOOR_PRICE_THRESHOLD = 0.8
const StyledListingButton = styled(BaseButton)<{ showResolveIssues: boolean; missingPrices: boolean }>`
background: ${({ showResolveIssues, theme }) => (showResolveIssues ? theme.accentFailure : theme.accentAction)};
color: ${({ theme }) => theme.accentTextLightPrimary};
font-weight: 600;
font-size: 20px;
line-height: 24px;
padding: 16px;
border-radius: 12px;
width: min-content;
border: none;
cursor: ${({ missingPrices }) => (missingPrices ? 'auto' : 'pointer')};
opacity: ${({ showResolveIssues, missingPrices }) => !showResolveIssues && missingPrices && '0.3'};
@media screen and (max-width: ${BREAKPOINTS.sm}px) {
font-size: 16px;
line-height: 20px;
padding: 10px 12px;
}
`
export const ListingButton = ({ onClick }: { onClick: () => void }) => {
const { sellAssets, showResolveIssues, toggleShowResolveIssues, issues, setIssues } = useSellAsset(
({ sellAssets, showResolveIssues, toggleShowResolveIssues, issues, setIssues }) => ({
sellAssets,
showResolveIssues,
toggleShowResolveIssues,
issues,
setIssues,
}),
shallow
)
const { setListingStatus, setListings, setCollectionsRequiringApproval } = useNFTList(
({ setListingStatus, setListings, setCollectionsRequiringApproval }) => ({
setListingStatus,
setListings,
setCollectionsRequiringApproval,
}),
shallow
)
const [showWarning, setShowWarning] = useState(false)
const isMobile = useIsMobile()
// instantiate listings and collections to approve when user's modify input data
useEffect(() => {
const [newCollectionsToApprove, newListings] = getListings(sellAssets)
setListings(newListings)
setCollectionsRequiringApproval(newCollectionsToApprove)
setListingStatus(ListingStatus.DEFINED)
}, [sellAssets, setCollectionsRequiringApproval, setListingStatus, setListings])
// Find issues with item listing data
const [listingsMissingPrice, listingsBelowFloor] = useMemo(() => {
const missingExpiration = sellAssets.some((asset) => {
return (
asset.expirationTime != null &&
(isNaN(asset.expirationTime) || asset.expirationTime * 1000 - Date.now() < ms`60 seconds`)
)
})
const overMaxExpiration = sellAssets.some((asset) => {
return asset.expirationTime != null && asset.expirationTime * 1000 - Date.now() > ms`180 days`
})
const listingsMissingPrice: [WalletAsset, Listing][] = []
const listingsBelowFloor: [WalletAsset, Listing][] = []
const listingsAboveSellOrderFloor: [WalletAsset, Listing][] = []
const invalidPrices: [WalletAsset, Listing][] = []
for (const asset of sellAssets) {
if (asset.newListings) {
for (const listing of asset.newListings) {
if (!listing.price) listingsMissingPrice.push([asset, listing])
else if (isNaN(listing.price) || listing.price < 0) invalidPrices.push([asset, listing])
else if (
listing.price < (asset?.floorPrice ?? 0) * BELOW_FLOOR_PRICE_THRESHOLD &&
!listing.overrideFloorPrice
)
listingsBelowFloor.push([asset, listing])
else if (asset.floor_sell_order_price && listing.price >= asset.floor_sell_order_price)
listingsAboveSellOrderFloor.push([asset, listing])
}
}
}
// set number of issues
const foundIssues =
Number(missingExpiration) +
Number(overMaxExpiration) +
listingsMissingPrice.length +
listingsAboveSellOrderFloor.length
setIssues(foundIssues)
!foundIssues && showResolveIssues && toggleShowResolveIssues()
// Only show Resolve Issue text if there was a user submitted error (ie not when page loads with no prices set)
if ((missingExpiration || overMaxExpiration || listingsAboveSellOrderFloor.length) && !showResolveIssues)
toggleShowResolveIssues()
return [listingsMissingPrice, listingsBelowFloor]
}, [sellAssets, setIssues, showResolveIssues, toggleShowResolveIssues])
const warningWrappedClick = () => {
if (issues) !showResolveIssues && toggleShowResolveIssues()
else if (listingsBelowFloor.length) setShowWarning(true)
else onClick()
}
return (
<>
<StyledListingButton
onClick={warningWrappedClick}
missingPrices={!!listingsMissingPrice.length}
showResolveIssues={showResolveIssues}
>
{showResolveIssues ? (
<Plural value={issues !== 1 ? 2 : 1} _1="Resolve issue" other={t`Resolve ${issues} issues`} />
) : listingsMissingPrice.length && !isMobile ? (
<Trans>Set prices to continue</Trans>
) : (
<Trans>Start listing</Trans>
)}
</StyledListingButton>
{showWarning && (
<BelowFloorWarningModal
listingsBelowFloor={listingsBelowFloor}
closeModal={() => setShowWarning(false)}
startListing={onClick}
/>
)}
</>
)
}

View File

@@ -136,13 +136,18 @@ export const MarketplaceRow = ({
toggleExpandMarketplaceRows,
rowHovered,
}: MarketplaceRowProps) => {
const [listPrice, setListPrice] = useState<number>()
const [globalOverride, setGlobalOverride] = useState(false)
const showGlobalPrice = globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride && globalPrice
const setAssetListPrice = useSellAsset((state) => state.setAssetListPrice)
const removeAssetMarketplace = useSellAsset((state) => state.removeAssetMarketplace)
const [marketIconHovered, toggleMarketIconHovered] = useReducer((s) => !s, false)
const [marketRowHovered, toggleMarketRowHovered] = useReducer((s) => !s, false)
const [listPrice, setListPrice] = useState<number | undefined>(
() =>
asset.newListings?.find((listing) =>
expandMarketplaceRows ? listing.marketplace.name === selectedMarkets?.[0].name : !!listing.price
)?.price
)
const [globalOverride, setGlobalOverride] = useState(false)
const showGlobalPrice = globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride && globalPrice
const price = showGlobalPrice ? globalPrice : listPrice
@@ -198,7 +203,7 @@ export const MarketplaceRow = ({
if (!listPrice) setListPrice(globalPrice)
price = listPrice ? listPrice : globalPrice
} else {
price = globalPrice
price = listPrice
}
if (selectedMarkets.length) for (const marketplace of selectedMarkets) setAssetListPrice(asset, price, marketplace)
else setAssetListPrice(asset, price)
@@ -228,14 +233,14 @@ export const MarketplaceRow = ({
return (
<Row onMouseEnter={toggleMarketRowHovered} onMouseLeave={toggleMarketRowHovered}>
<FloorPriceInfo>
<ThemedText.BodySmall color="textSecondary" lineHeight="20px">
<ThemedText.BodyPrimary color="textSecondary" lineHeight="24px">
{asset.floorPrice ? `${asset.floorPrice.toFixed(3)} ETH` : '-'}
</ThemedText.BodySmall>
</ThemedText.BodyPrimary>
</FloorPriceInfo>
<LastPriceInfo>
<ThemedText.BodySmall color="textSecondary" lineHeight="20px">
<ThemedText.BodyPrimary color="textSecondary" lineHeight="24px">
{asset.lastPrice ? `${asset.lastPrice.toFixed(3)} ETH` : '-'}
</ThemedText.BodySmall>
</ThemedText.BodyPrimary>
</LastPriceInfo>
<Row flex="2">

View File

@@ -8,7 +8,7 @@ import { Overlay } from 'nft/components/modals/Overlay'
import { useNFTList, useSellAsset } from 'nft/hooks'
import { ListingStatus } from 'nft/types'
import { fetchPrice } from 'nft/utils'
import { useEffect, useMemo, useReducer, useState } from 'react'
import { useCallback, useEffect, useMemo, useReducer, useState } from 'react'
import { X } from 'react-feather'
import styled from 'styled-components/macro'
import { BREAKPOINTS, ThemedText } from 'theme'
@@ -112,24 +112,28 @@ export const ListModal = ({ overlayClick }: { overlayClick: () => void }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [allCollectionsApproved])
const closeModalOnClick = useCallback(() => {
listingStatus === ListingStatus.APPROVED ? window.location.reload() : overlayClick()
}, [listingStatus, overlayClick])
// In the case that a user removes all listings via retry logic, close modal
useEffect(() => {
!listings.length && overlayClick()
}, [listings, overlayClick])
!listings.length && closeModalOnClick()
}, [listings, closeModalOnClick])
return (
<Portal>
<Trace modal={InterfaceModalName.NFT_LISTING}>
<ListModalWrapper>
{listingStatus === ListingStatus.APPROVED ? (
<SuccessScreen overlayClick={overlayClick} />
<SuccessScreen overlayClick={closeModalOnClick} />
) : (
<>
<TitleRow>
<ThemedText.HeadlineSmall lineHeight="28px">
<Trans>List NFTs</Trans>
</ThemedText.HeadlineSmall>
<X size={24} cursor="pointer" onClick={overlayClick} />
<X size={24} cursor="pointer" onClick={closeModalOnClick} />
</TitleRow>
<ListModalSection
sectionType={Section.APPROVE}
@@ -147,7 +151,7 @@ export const ListModal = ({ overlayClick }: { overlayClick: () => void }) => {
)}
</ListModalWrapper>
</Trace>
<Overlay onClick={overlayClick} />
<Overlay onClick={closeModalOnClick} />
</Portal>
)
}

View File

@@ -12,6 +12,8 @@ import { opacify } from 'theme/utils'
import { MarketplaceRow } from './MarketplaceRow'
import { SetPriceMethod } from './NFTListingsGrid'
const IMAGE_THUMBNAIL_SIZE = 60
const NFTListRowWrapper = styled(Row)`
padding: 24px 0px;
align-items: center;
@@ -23,8 +25,8 @@ const NFTListRowWrapper = styled(Row)`
`
const RemoveIconContainer = styled.div`
width: 44px;
height: 44px;
width: ${IMAGE_THUMBNAIL_SIZE}px;
height: ${IMAGE_THUMBNAIL_SIZE}px;
padding-left: 12px;
align-self: flex-start;
align-items: center;
@@ -51,8 +53,8 @@ const NFTInfoWrapper = styled(Row)`
`
const NFTImage = styled.img`
width: 44px;
height: 44px;
width: ${IMAGE_THUMBNAIL_SIZE}px;
height: ${IMAGE_THUMBNAIL_SIZE}px;
border-radius: 8px;
margin-right: 8px;
`
@@ -85,6 +87,7 @@ const MarketPlaceRowWrapper = styled(Column)`
gap: 24px;
flex: 1.5;
margin-right: 12px;
padding: 6px 0px;
@media screen and (min-width: ${BREAKPOINTS.md}px) {
flex: 2;

View File

@@ -28,7 +28,7 @@ const TableHeader = styled.div`
line-height: 20px;
@media screen and (min-width: ${BREAKPOINTS.sm}px) {
margin-left: 48px;
padding-left: 48px;
}
`

View File

@@ -1,14 +1,14 @@
import { Trans } from '@lingui/macro'
import Column from 'components/Column'
import Row from 'components/Row'
import { AttachPriceIcon, EditPriceIcon } from 'nft/components/icons'
import { BrokenLinkIcon } from 'nft/components/icons'
import { NumericInput } from 'nft/components/layout/Input'
import { body } from 'nft/css/common.css'
import { useSellAsset } from 'nft/hooks'
import { ListingWarning, WalletAsset } from 'nft/types'
import { formatEth } from 'nft/utils/currency'
import { Dispatch, FormEvent, useEffect, useRef, useState } from 'react'
import { AlertTriangle } from 'react-feather'
import { AlertTriangle, Link } from 'react-feather'
import styled, { useTheme } from 'styled-components/macro'
import { BREAKPOINTS } from 'theme'
import { colors } from 'theme/colors'
@@ -19,7 +19,7 @@ const PriceTextInputWrapper = styled(Column)`
`
const InputWrapper = styled(Row)<{ borderColor: string }>`
height: 44px;
height: 48px;
color: ${({ theme }) => theme.textTertiary};
padding: 12px;
border: 2px solid;
@@ -34,12 +34,17 @@ const CurrencyWrapper = styled.div<{ listPrice: number | undefined }>`
`
const GlobalPriceIcon = styled.div`
display: block;
display: flex;
cursor: pointer;
position: absolute;
top: -6px;
right: -4px;
bottom: 32px;
right: -10px;
background-color: ${({ theme }) => theme.backgroundSurface};
border-radius: 50%;
height: 28px;
width: 28px;
align-items: center;
justify-content: center;
`
const WarningRow = styled(Row)`
@@ -104,7 +109,6 @@ export const PriceTextInput = ({
warning,
asset,
}: PriceTextInputProps) => {
const [focused, setFocused] = useState(false)
const [warningType, setWarningType] = useState(WarningType.NONE)
const removeMarketplaceWarning = useSellAsset((state) => state.removeMarketplaceWarning)
const removeSellAsset = useSellAsset((state) => state.removeSellAsset)
@@ -128,7 +132,7 @@ export const PriceTextInput = ({
const warningColor =
showResolveIssues && !listPrice
? colors.red400
: warningType !== WarningType.NONE && !focused
: warningType !== WarningType.NONE
? (warningType === WarningType.BELOW_FLOOR && percentBelowFloor >= 20) ||
warningType === WarningType.ALREADY_LISTED
? colors.red400
@@ -151,10 +155,6 @@ export const PriceTextInput = ({
placeholder="0"
backgroundColor="none"
width={{ sm: '54', md: '68' }}
onFocus={() => setFocused(true)}
onBlur={() => {
setFocused(false)
}}
ref={inputRef}
onChange={(v: FormEvent<HTMLInputElement>) => {
if (!listPrice && v.currentTarget.value.includes('.') && parseFloat(v.currentTarget.value) === 0) {
@@ -167,7 +167,7 @@ export const PriceTextInput = ({
<CurrencyWrapper listPrice={listPrice}>&nbsp;ETH</CurrencyWrapper>
{(isGlobalPrice || globalOverride) && (
<GlobalPriceIcon onClick={() => setGlobalOverride(!globalOverride)}>
{globalOverride ? <AttachPriceIcon /> : <EditPriceIcon />}
{globalOverride ? <BrokenLinkIcon /> : <Link size={20} color={warningColor} />}
</GlobalPriceIcon>
)}
</InputWrapper>

View File

@@ -138,7 +138,7 @@ export const ProfilePage = () => {
borderRadius="12"
paddingX="16"
paddingY="12"
background="backgroundModule"
background="backgroundSurface"
borderStyle="solid"
borderColor="backgroundOutline"
borderWidth="1px"

View File

@@ -18,27 +18,10 @@ export const subheadSmall = sprinkles({ fontWeight: 'medium', fontSize: '14', li
export const body = sprinkles({ fontWeight: 'normal', fontSize: '16', lineHeight: '24' })
export const bodySmall = sprinkles({ fontWeight: 'normal', fontSize: '14', lineHeight: '20' })
export const caption = sprinkles({ fontWeight: 'normal', fontSize: '12', lineHeight: '16' })
export const badge = sprinkles({ fontWeight: 'semibold', fontSize: '10', lineHeight: '12' })
export const buttonTextMedium = sprinkles({ fontWeight: 'semibold', fontSize: '16', lineHeight: '20' })
export const buttonTextSmall = sprinkles({ fontWeight: 'semibold', fontSize: '14', lineHeight: '16' })
export const commonButtonStyles = style([
sprinkles({
borderRadius: '12',
transition: '250',
}),
{
border: 'none',
':hover': {
cursor: 'pointer',
},
':disabled': {
cursor: 'auto',
},
},
])
const magicalGradient = style({
selectors: {
'&::before': {

View File

@@ -0,0 +1,50 @@
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { useMemo } from 'react'
import { InterfaceTrade } from 'state/routing/types'
import { useTheme } from 'styled-components/macro'
import { computeRealizedPriceImpact, getPriceImpactWarning } from 'utils/prices'
export interface PriceImpact {
priceImpactSeverity: PriceImpactSeverity
displayPercentage(): string
}
interface PriceImpactSeverity {
type: 'warning' | 'error'
color: string
}
export function usePriceImpact(trade?: InterfaceTrade<Currency, Currency, TradeType>): PriceImpact | undefined {
const theme = useTheme()
return useMemo(() => {
const marketPriceImpact = trade ? computeRealizedPriceImpact(trade) : undefined
const priceImpactWarning = marketPriceImpact ? getPriceImpactWarning(marketPriceImpact) : undefined
const warningColor =
priceImpactWarning === 'error'
? theme.accentCritical
: priceImpactWarning === 'warning'
? theme.accentWarning
: undefined
return marketPriceImpact && priceImpactWarning && warningColor
? {
priceImpactSeverity: {
type: priceImpactWarning,
color: warningColor,
},
displayPercentage: () => toHumanReadablePercent(marketPriceImpact),
}
: undefined
}, [theme.accentCritical, theme.accentWarning, trade])
}
function toHumanReadablePercent(priceImpact: Percent): string {
const sign = priceImpact.lessThan(0) ? '+' : ''
const exactFloat = (Number(priceImpact.numerator) / Number(priceImpact.denominator)) * 100
if (exactFloat < 0.005) {
return '0.00%'
}
const number = parseFloat(priceImpact.multiply(-1)?.toFixed(2))
return `${sign}${number}%`
}

View File

@@ -15,6 +15,7 @@ import { CollectionPageSkeleton } from 'nft/components/collection/CollectionPage
import { BagCloseIcon } from 'nft/components/icons'
import { useBag, useCollectionFilters, useFiltersExpanded, useIsMobile } from 'nft/hooks'
import * as styles from 'nft/pages/collection/index.css'
import { blocklistedCollections } from 'nft/utils'
import { Suspense, useEffect } from 'react'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { animated, easings, useSpring } from 'react-spring'
@@ -85,7 +86,7 @@ const FiltersContainer = styled.div<{ isMobile: boolean; isFiltersExpanded: bool
width: ${({ isMobile }) => (isMobile ? '100%' : '0px')};
height: ${({ isMobile, isFiltersExpanded }) => (isMobile && isFiltersExpanded ? '100%' : undefined)};
background: ${({ theme, isMobile }) => (isMobile ? theme.backgroundBackdrop : undefined)};
z-index: ${Z_INDEX.modalBackdrop};
z-index: ${Z_INDEX.modalBackdrop - 3};
overflow-y: ${({ isMobile }) => (isMobile ? 'scroll' : undefined)};
@media screen and (min-width: ${({ theme }) => theme.breakpoint.sm}px) {
@@ -188,7 +189,7 @@ const Collection = () => {
width: CollectionContainerWidthChange.to((x) => `calc(100% - ${x as number}px)`),
}}
>
{contractAddress ? (
{contractAddress && !blocklistedCollections.includes(contractAddress) ? (
<>
<BannerWrapper>
<Banner

View File

@@ -1,8 +1,8 @@
import { Trans } from '@lingui/macro'
import { Trace } from '@uniswap/analytics'
import { InterfacePageName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import { ButtonPrimary } from 'components/Button'
import { NftListV2Variant, useNftListV2Flag } from 'featureFlags/flags/nftListV2'
import { XXXL_BAG_WIDTH } from 'nft/components/bag/Bag'
import { ListPage } from 'nft/components/profile/list/ListPage'
import { ProfilePage } from 'nft/components/profile/view/ProfilePage'
@@ -26,17 +26,17 @@ const ProfilePageWrapper = styled.div`
}
`
const LoadedAccountPage = styled.div<{ cartExpanded: boolean; isOnV2ListPage: boolean }>`
const LoadedAccountPage = styled.div<{ cartExpanded: boolean; isListingNfts: boolean }>`
width: calc(
100% -
${({ cartExpanded, isOnV2ListPage }) =>
isOnV2ListPage ? LIST_PAGE_MARGIN * 2 : cartExpanded ? XXXL_BAG_WIDTH : 0}px
${({ cartExpanded, isListingNfts }) =>
isListingNfts ? LIST_PAGE_MARGIN * 2 : cartExpanded ? XXXL_BAG_WIDTH : 0}px
);
margin: 0px ${({ isOnV2ListPage }) => (isOnV2ListPage ? LIST_PAGE_MARGIN : 0)}px;
margin: 0px ${({ isListingNfts }) => (isListingNfts ? LIST_PAGE_MARGIN : 0)}px;
@media screen and (max-width: ${BREAKPOINTS.sm}px) {
width: calc(100% - ${({ isOnV2ListPage }) => (isOnV2ListPage ? LIST_PAGE_MARGIN_MOBILE * 2 : 0)}px);
margin: 0px ${({ isOnV2ListPage }) => (isOnV2ListPage ? LIST_PAGE_MARGIN_MOBILE : 0)}px;
width: calc(100% - ${({ isListingNfts }) => (isListingNfts ? LIST_PAGE_MARGIN_MOBILE * 2 : 0)}px);
margin: 0px ${({ isListingNfts }) => (isListingNfts ? LIST_PAGE_MARGIN_MOBILE : 0)}px;
}
`
@@ -85,25 +85,23 @@ const ProfileContent = () => {
}
}, [account, resetSellAssets, setSellPageState, clearCollectionFilters])
const cartExpanded = useBag((state) => state.bagExpanded)
const isNftListV2 = useNftListV2Flag() === NftListV2Variant.Enabled
const isListingNfts = sellPageState === ProfilePageStateType.LISTING
const isOnV2ListPage = isNftListV2 && isListingNfts
return (
<Trace page={InterfacePageName.NFT_PROFILE_PAGE} shouldLogImpression>
<ProfilePageWrapper>
{account ? (
<LoadedAccountPage cartExpanded={cartExpanded} isOnV2ListPage={isOnV2ListPage}>
<LoadedAccountPage cartExpanded={cartExpanded} isListingNfts={isListingNfts}>
{!isListingNfts ? <ProfilePage /> : <ListPage />}
</LoadedAccountPage>
) : (
<Center>
<ThemedText.HeadlineMedium lineHeight="36px" color="textSecondary" fontWeight="600" marginBottom="24px">
No items to display
<Trans>No items to display</Trans>
</ThemedText.HeadlineMedium>
<ConnectWalletButton onClick={toggleWalletModal}>
<ThemedText.SubHeader color="white" lineHeight="20px">
Connect Wallet
<Trans>Connect Wallet</Trans>
</ThemedText.SubHeader>
</ConnectWalletButton>
</Center>

View File

@@ -1,4 +1,5 @@
import { isAddress } from '@ethersproject/address'
import { blocklistedCollections } from 'nft/utils'
import { GenieCollection } from '../../types'
@@ -45,15 +46,17 @@ export const fetchSearchCollections = async (addressOrName: string, recursive =
if (isName) {
const data = await r.json()
const formattedData = data?.data
? data.data.map((collection: { stats: Record<string, unknown>; floorPrice: string }) => {
return {
...collection,
stats: {
...collection.stats,
floor_price: collection.floorPrice,
},
}
})
? data.data
.filter((collection: { address: string }) => !blocklistedCollections.includes(collection.address))
.map((collection: { stats: Record<string, unknown>; floorPrice: string }) => {
return {
...collection,
stats: {
...collection.stats,
floor_price: collection.floorPrice,
},
}
})
: []
return formattedData.slice(0, MAX_SEARCH_RESULTS)
}

View File

@@ -1,3 +1,5 @@
import { blocklistedCollections } from 'nft/utils'
import { TimePeriod, TrendingCollection } from '../../types'
const NFT_API_URL = process.env.REACT_APP_TEMP_API_URL
@@ -18,5 +20,5 @@ export const fetchTrendingCollections = async (payload: {
const data = await r.json()
return data ?? []
return data.filter((collection: { address: string }) => !blocklistedCollections.includes(collection.address)) ?? []
}

View File

@@ -22,3 +22,8 @@ export const isInSameSudoSwapPool = (assetA: GenieAsset, assetB: GenieAsset): bo
export const isInSameMarketplaceCollection = (assetA: GenieAsset, assetB: GenieAsset): boolean => {
return assetA.address === assetB.address && assetA.marketplace === assetB.marketplace
}
export const blocklistedCollections = [
'0xd5eeac01b0d1d929d6cffaaf78020af137277293',
'0x85c08fffa9510f87019efdcf986301873cbb10d6',
]

View File

@@ -58,11 +58,13 @@ const Profile = lazy(() => import('nft/pages/profile/profile'))
const Asset = lazy(() => import('nft/pages/asset/Asset'))
// Placeholder API key. Actual API key used in the proxy server
const ANALYTICS_DUMMY_KEY = '00000000000000000000000000000000'
const ANALYTICS_PROXY_URL = process.env.REACT_APP_AMPLITUDE_PROXY_URL
const AMPLITUDE_DUMMY_KEY = '00000000000000000000000000000000'
const AMPLITUDE_PROXY_URL = process.env.REACT_APP_AMPLITUDE_PROXY_URL
const STATSIG_DUMMY_KEY = 'client-0000000000000000000000000000000000000000000'
const STATSIG_PROXY_URL = process.env.REACT_APP_STATSIG_PROXY_URL
const COMMIT_HASH = process.env.REACT_APP_GIT_COMMIT_HASH
initializeAnalytics(ANALYTICS_DUMMY_KEY, OriginApplication.INTERFACE, {
proxyUrl: ANALYTICS_PROXY_URL,
initializeAnalytics(AMPLITUDE_DUMMY_KEY, OriginApplication.INTERFACE, {
proxyUrl: AMPLITUDE_PROXY_URL,
defaultEventName: SharedEventName.PAGE_VIEWED,
commitHash: COMMIT_HASH,
isProductionEnv: isProductionEnv(),
@@ -210,10 +212,11 @@ export default function App() {
<StatsigProvider
user={statsigUser}
// TODO: replace with proxy and cycle key
sdkKey={process.env.REACT_APP_STATSIG_API_KEY ?? ''}
sdkKey={STATSIG_DUMMY_KEY}
waitForInitialization={false}
options={{
environment: { tier: getEnvName() },
api: STATSIG_PROXY_URL,
}}
>
<HeaderWrapper transparent={isHeaderTransparent}>

View File

@@ -116,7 +116,7 @@ export default function MigrateV2() {
<BodyWrapper style={{ padding: 24 }}>
<AutoColumn gap="16px">
<AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px">
<BackArrow to="/pool/v2" />
<BackArrow to="/pool" />
<ThemedText.DeprecatedMediumHeader>
<Trans>Migrate V2 Liquidity</Trans>
</ThemedText.DeprecatedMediumHeader>

View File

@@ -1,14 +1,18 @@
import { DEFAULT_LIST_OF_LISTS } from './../constants/lists'
const DEFAULT_LIST_PRIORITIES = DEFAULT_LIST_OF_LISTS.reduce<{ [listUrl: string]: number }>((memo, listUrl, index) => {
memo[listUrl] = index + 1
return memo
}, {})
const DEFAULT_LIST_PRIORITIES = DEFAULT_LIST_OF_LISTS.reduce(
(acc, listUrl, index) => ({
...acc,
[listUrl]: index + 1,
}),
{}
) as Record<string, number>
// use ordering of default list of lists to assign priority
export default function sortByListPriority(urlA: string, urlB: string) {
if (DEFAULT_LIST_PRIORITIES[urlA] && DEFAULT_LIST_PRIORITIES[urlB]) {
return DEFAULT_LIST_PRIORITIES[urlA] - DEFAULT_LIST_PRIORITIES[urlB]
}
return 0
const A = DEFAULT_LIST_PRIORITIES[urlA]
const B = DEFAULT_LIST_PRIORITIES[urlB]
if (!A) return 0
if (!B) return 0
return A - B
}

View File

@@ -4,6 +4,9 @@ import { CHAIN_NAME_TO_CHAIN_ID } from 'graphql/data/util'
export function getNativeTokenDBAddress(chain: Chain): string | undefined {
const pageChainId = CHAIN_NAME_TO_CHAIN_ID[chain]
if (pageChainId === undefined) {
return undefined
}
switch (chain) {
case Chain.Celo:
case Chain.Polygon:

View File

@@ -92,3 +92,9 @@ export function warningSeverity(priceImpact: Percent | undefined): WarningSeveri
}
return 0
}
export function getPriceImpactWarning(priceImpact: Percent): 'warning' | 'error' | undefined {
if (priceImpact.greaterThan(ALLOWED_PRICE_IMPACT_HIGH)) return 'error'
if (priceImpact.greaterThan(ALLOWED_PRICE_IMPACT_MEDIUM)) return 'warning'
return
}

Binary file not shown.

408
yarn.lock
View File

@@ -1156,12 +1156,12 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@>=7.17.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78"
integrity sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==
"@babel/runtime@>=7.17.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.21.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
dependencies:
regenerator-runtime "^0.13.4"
regenerator-runtime "^0.13.11"
"@babel/template@^7.10.4", "@babel/template@^7.12.13", "@babel/template@^7.15.4", "@babel/template@^7.16.7", "@babel/template@^7.18.10", "@babel/template@^7.3.3":
version "7.18.10"
@@ -1210,25 +1210,26 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@coinbase/wallet-sdk@^3.3.0":
version "3.3.0"
resolved "https://registry.yarnpkg.com/@coinbase/wallet-sdk/-/wallet-sdk-3.3.0.tgz#e77383bce1388e5367dc35f8309702e73be47503"
integrity sha512-Prmxs5eYRxe5i+kDSsny97oPG4Pa5PhLmNDx8f7UQrvlPowGy5Tg0gHOqCie6ck2shVMdW8sKJ+RCLIRZ9kIjA==
"@coinbase/wallet-sdk@^3.6.4":
version "3.6.4"
resolved "https://registry.yarnpkg.com/@coinbase/wallet-sdk/-/wallet-sdk-3.6.4.tgz#00b4325c501ec5cdd07ac1b365ab226cb3df3a22"
integrity sha512-2ecCT0/pmaMNVyMF7J1ZLFTfLnpnrHNQOGyIcbMBIepeqlE3jndjU023OdwwVLrLXyvfyelJ8K1iwAOvyEZxUw==
dependencies:
"@metamask/safe-event-emitter" "2.0.0"
"@solana/web3.js" "^1.70.1"
bind-decorator "^1.0.11"
bn.js "^5.1.1"
buffer "^6.0.3"
clsx "^1.1.0"
eth-block-tracker "4.4.3"
eth-json-rpc-filters "4.2.2"
eth-json-rpc-filters "5.1.0"
eth-rpc-errors "4.0.2"
js-sha256 "0.9.0"
json-rpc-engine "6.1.0"
keccak "^3.0.1"
preact "^10.5.9"
qs "^6.10.3"
rxjs "^6.6.3"
sha.js "^2.4.11"
stream-browserify "^3.0.0"
util "^0.12.4"
@@ -3251,6 +3252,21 @@
resolved "https://registry.npmjs.org/@multiformats/base-x/-/base-x-4.0.1.tgz"
integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==
"@noble/ed25519@^1.7.0":
version "1.7.3"
resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123"
integrity sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==
"@noble/hashes@^1.1.2":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12"
integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==
"@noble/secp256k1@^1.6.3":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c"
integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -3680,6 +3696,35 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
"@solana/buffer-layout@^4.0.0":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15"
integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==
dependencies:
buffer "~6.0.3"
"@solana/web3.js@^1.70.1":
version "1.73.3"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.73.3.tgz#60e6bd68f6f364d4be360b1e0a03a0a68468a029"
integrity sha512-vHRMo589XEIpoujpE2sZZ1aMZvfA1ImKfNxobzEFyMb+H5j6mRRUXfdgWD0qJ0sm11e5BcBC7HPeRXJB+7f3Lg==
dependencies:
"@babel/runtime" "^7.12.5"
"@noble/ed25519" "^1.7.0"
"@noble/hashes" "^1.1.2"
"@noble/secp256k1" "^1.6.3"
"@solana/buffer-layout" "^4.0.0"
agentkeepalive "^4.2.1"
bigint-buffer "^1.1.5"
bn.js "^5.0.0"
borsh "^0.7.0"
bs58 "^4.0.1"
buffer "6.0.1"
fast-stable-stringify "^1.0.0"
jayson "^3.4.4"
node-fetch "^2.6.7"
rpc-websockets "^7.5.1"
superstruct "^0.14.2"
"@styled-system/background@^5.1.2":
version "5.1.2"
resolved "https://registry.npmjs.org/@styled-system/background/-/background-5.1.2.tgz"
@@ -4025,13 +4070,6 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/bn.js@^4.11.3":
version "4.11.6"
resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz"
integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==
dependencies:
"@types/node" "*"
"@types/bn.js@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68"
@@ -4039,6 +4077,13 @@
dependencies:
"@types/node" "*"
"@types/connect@^3.4.33":
version "3.4.35"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
dependencies:
"@types/node" "*"
"@types/d3-array@^2":
version "2.12.3"
resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-2.12.3.tgz#8d16d51fb04ad5a5a8ebe14eb8263a579f1efdd1"
@@ -4444,6 +4489,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b"
integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==
"@types/node@^12.12.54":
version "12.20.55"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==
"@types/node@^13.13.5":
version "13.13.52"
resolved "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz"
@@ -4708,6 +4758,13 @@
anymatch "^3.0.0"
source-map "^0.6.0"
"@types/ws@^7.4.4":
version "7.4.7"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702"
integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==
dependencies:
"@types/node" "*"
"@types/ws@^8.0.0":
version "8.5.3"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
@@ -4931,15 +4988,15 @@
"@typescript-eslint/types" "5.47.0"
eslint-visitor-keys "^3.3.0"
"@uniswap/analytics-events@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@uniswap/analytics-events/-/analytics-events-2.3.0.tgz#e4d6b29633c09872be3b9b760b1a192b96368887"
integrity sha512-oShunkYEfa45RQAtl2aQfF91gfX4QirLa/fR+FyL5jfl+Ei4AZ1ihtyVjJ1VLOJlObX1p08JjlpA0yxqDwPYHw==
"@uniswap/analytics-events@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@uniswap/analytics-events/-/analytics-events-2.4.0.tgz#910f727bf4c72f5d1890f17daec4687dc44ee6c0"
integrity sha512-oZl2KRCSTAO8C3sxEFX6BEdGujdPs8tk/Zyx7UwOZOUDcim3mxx6rsdRvnwsAgkZAJjV9I1HSUR7JEeJmQkRmQ==
"@uniswap/analytics@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@uniswap/analytics/-/analytics-1.3.0.tgz#42de08949a7d529ebba647d76801f143792c132a"
integrity sha512-cwx3HDxcqehr5uUnnAJ20lak9jA68e+l8ww/s4XxoJzedkdHz0TWXc4+ZZ2iukKEun4oU/d3clrU6u3Cu6xDpg==
"@uniswap/analytics@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@uniswap/analytics/-/analytics-1.3.1.tgz#086a681fc483c08e1b3d896cce287bc162ad0c1d"
integrity sha512-wB8J+e9oeu+VSx3tgfA304g7MKf6zuAesFeEGKdD1pI2LNOoMQHczbnf8I8lPjPYnXxLvdbGVKRQZXWaI+jTGw==
dependencies:
"@amplitude/analytics-browser" "^1.5.8"
react "^18.2.0"
@@ -5194,10 +5251,10 @@
"@uniswap/v3-core" "1.0.0"
"@uniswap/v3-periphery" "^1.0.1"
"@uniswap/widgets@^2.29.3":
version "2.29.3"
resolved "https://registry.yarnpkg.com/@uniswap/widgets/-/widgets-2.29.3.tgz#1b2f0d4f7f4db16d6d12f40204bcee6a67e46c1c"
integrity sha512-gsyiNxeqcj5Wn5IsaAKIz9M1v9lkmYKGxCrJHWADw8UyuTrPnL7qXg6GkBRR5kYkd+eeK6lK28qc1BEzl7dvmQ==
"@uniswap/widgets@^2.40.0":
version "2.40.0"
resolved "https://registry.yarnpkg.com/@uniswap/widgets/-/widgets-2.40.0.tgz#98539f31cc21a8e70ced86da65db642f3380db14"
integrity sha512-55I6r8a3XfWAEFfU6eAj4BUbaYRBPyrbfzaedAy0+9SAntnxxT7wyh3DMC9MoMLEwd4PPLemWyLfVQTVIoYn0g==
dependencies:
"@babel/runtime" ">=7.17.0"
"@fontsource/ibm-plex-mono" "^4.5.1"
@@ -6047,6 +6104,14 @@
resolved "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz"
integrity sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==
JSONStream@^1.3.5:
version "1.3.5"
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==
dependencies:
jsonparse "^1.2.0"
through ">=2.2.7 <3"
abab@^2.0.3, abab@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
@@ -6135,6 +6200,15 @@ agent-base@6:
dependencies:
debug "4"
agentkeepalive@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717"
integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==
dependencies:
debug "^4.1.0"
depd "^1.1.2"
humanize-ms "^1.2.1"
aggregate-error@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
@@ -6940,6 +7014,13 @@ big.js@^5.2.2:
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
bigint-buffer@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442"
integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==
dependencies:
bindings "^1.3.0"
bignumber.js@^8.1.1:
version "8.1.1"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.1.1.tgz#4b072ae5aea9c20f6730e4e5d529df1271c4d885"
@@ -6965,9 +7046,9 @@ bind-decorator@^1.0.11:
resolved "https://registry.npmjs.org/bind-decorator/-/bind-decorator-1.0.11.tgz"
integrity sha1-5BvAah9l3ZzsR2yRxdrzl4SIJS8=
bindings@^1.5.0:
bindings@^1.3.0, bindings@^1.5.0:
version "1.5.0"
resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
dependencies:
file-uri-to-path "1.0.0"
@@ -7011,7 +7092,7 @@ bn.js@4.11.8:
resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz"
integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9:
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9:
version "4.12.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
@@ -7054,6 +7135,15 @@ boolbase@^1.0.0, boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
borsh@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a"
integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==
dependencies:
bn.js "^5.2.0"
bs58 "^4.0.0"
text-encoding-utf-8 "^1.0.2"
boxen@1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz"
@@ -7203,10 +7293,10 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.19.1, browserslist@^4
node-releases "^2.0.6"
update-browserslist-db "^1.0.9"
bs58@^4.0.0:
bs58@^4.0.0, bs58@^4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz"
integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo=
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==
dependencies:
base-x "^3.0.2"
@@ -7226,11 +7316,6 @@ bser@2.1.1:
dependencies:
node-int64 "^0.4.0"
btoa@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz"
integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz"
@@ -7284,6 +7369,14 @@ buffer-xor@^1.0.3:
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
buffer@6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.1.tgz#3cbea8c1463e5a0779e30b66d4c88c6ffa182ac2"
integrity sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.2.1"
buffer@^4.3.0:
version "4.9.2"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
@@ -7301,7 +7394,7 @@ buffer@^5.2.0, buffer@^5.4.3, buffer@^5.5.0, buffer@^5.6.0:
base64-js "^1.3.1"
ieee754 "^1.1.13"
buffer@^6.0.3:
buffer@^6.0.3, buffer@~6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
@@ -7309,10 +7402,10 @@ buffer@^6.0.3:
base64-js "^1.3.1"
ieee754 "^1.2.1"
bufferutil@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.6.tgz#ebd6c67c7922a0e902f053e5d8be5ec850e48433"
integrity sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==
bufferutil@^4.0.1, bufferutil@^4.0.6:
version "4.0.7"
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad"
integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==
dependencies:
node-gyp-build "^4.3.0"
@@ -7842,7 +7935,7 @@ cliui@^8.0.1:
strip-ansi "^6.0.1"
wrap-ansi "^7.0.0"
clone@2.x, clone@^2.1.1:
clone@2.x:
version "2.1.2"
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
@@ -7986,7 +8079,7 @@ commander@7:
resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
commander@^2.20.0:
commander@^2.20.0, commander@^2.20.3:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -9184,15 +9277,20 @@ delaunator@5:
dependencies:
robust-predicates "^3.0.0"
delay@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d"
integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
depd@~1.1.2:
depd@^1.1.2, depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
dependency-graph@^0.11.0:
version "0.11.0"
@@ -9690,6 +9788,18 @@ es6-iterator@2.0.3, es6-iterator@~2.0.3:
es5-ext "^0.10.35"
es6-symbol "^3.1.1"
es6-promise@^4.0.3:
version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
es6-promisify@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==
dependencies:
es6-promise "^4.0.3"
es6-symbol@^3.1.1, es6-symbol@~3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
@@ -10080,35 +10190,17 @@ eth-block-tracker@4.4.3:
pify "^3.0.0"
safe-event-emitter "^1.0.1"
eth-json-rpc-filters@4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/eth-json-rpc-filters/-/eth-json-rpc-filters-4.2.2.tgz#eb35e1dfe9357ace8a8908e7daee80b2cd60a10d"
integrity sha512-DGtqpLU7bBg63wPMWg1sCpkKCf57dJ+hj/k3zF26anXMzkmtSBDExL8IhUu7LUd34f0Zsce3PYNO2vV2GaTzaw==
eth-json-rpc-filters@5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/eth-json-rpc-filters/-/eth-json-rpc-filters-5.1.0.tgz#f0c2aeaec2a45e2dc6ca1b9843d8e85447821427"
integrity sha512-fos+9xmoa1A2Ytsc9eYof17r81BjdJOUcGcgZn4K/tKdCCTb+a8ytEtwlu1op5qsXFDlgGmstTELFrDEc89qEQ==
dependencies:
"@metamask/safe-event-emitter" "^2.0.0"
async-mutex "^0.2.6"
eth-json-rpc-middleware "^6.0.0"
eth-query "^2.1.2"
json-rpc-engine "^6.1.0"
pify "^5.0.0"
eth-json-rpc-middleware@^6.0.0:
version "6.0.0"
resolved "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-6.0.0.tgz"
integrity sha512-qqBfLU2Uq1Ou15Wox1s+NX05S9OcAEL4JZ04VZox2NS0U+RtCMjSxzXhLFWekdShUPZ+P8ax3zCO2xcPrp6XJQ==
dependencies:
btoa "^1.2.1"
clone "^2.1.1"
eth-query "^2.1.2"
eth-rpc-errors "^3.0.0"
eth-sig-util "^1.4.2"
ethereumjs-util "^5.1.2"
json-rpc-engine "^5.3.0"
json-stable-stringify "^1.0.1"
node-fetch "^2.6.1"
pify "^3.0.0"
safe-event-emitter "^1.0.1"
eth-query@^2.1.0, eth-query@^2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/eth-query/-/eth-query-2.1.2.tgz"
@@ -10124,21 +10216,6 @@ eth-rpc-errors@4.0.2, eth-rpc-errors@^4.0.2:
dependencies:
fast-safe-stringify "^2.0.6"
eth-rpc-errors@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz"
integrity sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg==
dependencies:
fast-safe-stringify "^2.0.6"
eth-sig-util@^1.4.2:
version "1.4.2"
resolved "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz"
integrity sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=
dependencies:
ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git"
ethereumjs-util "^5.1.1"
ethereum-bloom-filters@^1.0.6:
version "1.0.10"
resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a"
@@ -10167,39 +10244,6 @@ ethereum-cryptography@^0.1.3:
secp256k1 "^4.0.1"
setimmediate "^1.0.5"
"ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git":
version "0.6.8"
resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#ee3994657fa7a427238e6ba92a84d0b529bbcde0"
dependencies:
bn.js "^4.11.8"
ethereumjs-util "^6.0.0"
ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2:
version "5.2.1"
resolved "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz"
integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==
dependencies:
bn.js "^4.11.0"
create-hash "^1.1.2"
elliptic "^6.5.2"
ethereum-cryptography "^0.1.3"
ethjs-util "^0.1.3"
rlp "^2.0.0"
safe-buffer "^5.1.1"
ethereumjs-util@^6.0.0:
version "6.2.1"
resolved "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz"
integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==
dependencies:
"@types/bn.js" "^4.11.3"
bn.js "^4.11.0"
create-hash "^1.1.2"
elliptic "^6.5.2"
ethereum-cryptography "^0.1.3"
ethjs-util "0.1.6"
rlp "^2.2.3"
ethereumjs-util@^7.1.0:
version "7.1.5"
resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181"
@@ -10255,14 +10299,6 @@ ethjs-unit@0.1.6:
bn.js "4.11.6"
number-to-bn "1.7.0"
ethjs-util@0.1.6, ethjs-util@^0.1.3:
version "0.1.6"
resolved "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz"
integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==
dependencies:
is-hex-prefixed "1.0.0"
strip-hex-prefix "1.0.0"
eval@0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/eval/-/eval-0.1.6.tgz#9620d7d8c85515e97e6b47c5814f46ae381cb3cc"
@@ -10523,6 +10559,11 @@ extsprintf@1.3.0, extsprintf@^1.2.0:
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
eyes@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==
fast-deep-equal@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz"
@@ -10564,6 +10605,11 @@ fast-safe-stringify@^2.0.6:
resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz"
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
fast-stable-stringify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313"
integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==
fast-url-parser@1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz"
@@ -11676,6 +11722,13 @@ human-signals@^1.1.1:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
humanize-ms@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
dependencies:
ms "^2.0.0"
iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz"
@@ -12483,6 +12536,11 @@ isomorphic-ws@5.0.0:
resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf"
integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==
isomorphic-ws@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc"
integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@@ -12539,6 +12597,25 @@ javascript-stringify@^2.0.1:
resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79"
integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==
jayson@^3.4.4:
version "3.7.0"
resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.7.0.tgz#b735b12d06d348639ae8230d7a1e2916cb078f25"
integrity sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ==
dependencies:
"@types/connect" "^3.4.33"
"@types/node" "^12.12.54"
"@types/ws" "^7.4.4"
JSONStream "^1.3.5"
commander "^2.20.3"
delay "^5.0.0"
es6-promisify "^5.0.0"
eyes "^0.1.8"
isomorphic-ws "^4.0.1"
json-stringify-safe "^5.0.1"
lodash "^4.17.20"
uuid "^8.3.2"
ws "^7.4.5"
jest-changed-files@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0"
@@ -13019,7 +13096,7 @@ js-base64@^3.7.2:
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.2.tgz#816d11d81a8aff241603d19ce5761e13e41d7745"
integrity sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==
js-sha256@0.9.0, js-sha256@^0.9.0:
js-sha256@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==
@@ -13120,14 +13197,6 @@ json-rpc-engine@6.1.0, json-rpc-engine@^6.1.0:
"@metamask/safe-event-emitter" "^2.0.0"
eth-rpc-errors "^4.0.2"
json-rpc-engine@^5.3.0:
version "5.4.0"
resolved "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz"
integrity sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g==
dependencies:
eth-rpc-errors "^3.0.0"
safe-event-emitter "^1.0.1"
json-rpc-random-id@^1.0.0, json-rpc-random-id@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz"
@@ -13160,10 +13229,10 @@ json-stable-stringify@^1.0.1:
dependencies:
jsonify "~0.0.0"
json-stringify-safe@~5.0.1:
json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
json-to-pretty-yaml@^1.2.2:
version "1.2.2"
@@ -13211,6 +13280,11 @@ jsonify@~0.0.0:
resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz"
integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
jsonparse@^1.2.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
jsonwebtoken@^8.5.1:
version "8.5.1"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
@@ -16733,10 +16807,10 @@ regenerator-runtime@^0.11.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
version "0.13.11"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
regenerator-transform@^0.14.2:
version "0.14.5"
@@ -17106,7 +17180,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
rlp@^2.0.0, rlp@^2.2.3, rlp@^2.2.4:
rlp@^2.2.4:
version "2.2.7"
resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf"
integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==
@@ -17153,6 +17227,19 @@ rollup@^1.31.1:
"@types/node" "*"
acorn "^7.1.0"
rpc-websockets@^7.5.1:
version "7.5.1"
resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.1.tgz#e0a05d525a97e7efc31a0617f093a13a2e10c401"
integrity sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w==
dependencies:
"@babel/runtime" "^7.17.2"
eventemitter3 "^4.0.7"
uuid "^8.3.2"
ws "^8.5.0"
optionalDependencies:
bufferutil "^4.0.1"
utf-8-validate "^5.0.2"
rsvp@^4.8.4:
version "4.8.5"
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
@@ -17518,7 +17605,7 @@ setprototypeof@1.1.1:
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
sha.js@^2.4.0, sha.js@^2.4.8:
sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8:
version "2.4.11"
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
@@ -18213,6 +18300,11 @@ stylehacks@^4.0.0:
postcss "^7.0.0"
postcss-selector-parser "^3.0.0"
superstruct@^0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b"
integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==
supports-color@^5.3.0, supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@@ -18431,6 +18523,11 @@ test-value@^2.1.0:
array-back "^1.0.3"
typical "^2.6.0"
text-encoding-utf-8@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13"
integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==
text-table@0.2.0, text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
@@ -18459,10 +18556,10 @@ through2@^2.0.0:
readable-stream "~2.3.6"
xtend "~4.0.1"
through@^2.3.6, through@^2.3.8:
"through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8:
version "2.3.8"
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
thunky@^1.0.2:
version "1.1.0"
@@ -19164,10 +19261,10 @@ use@^3.1.0:
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
utf-8-validate@^5.0.8:
version "5.0.9"
resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.9.tgz#ba16a822fbeedff1a58918f2a6a6b36387493ea3"
integrity sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==
utf-8-validate@^5.0.2, utf-8-validate@^5.0.8:
version "5.0.10"
resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2"
integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==
dependencies:
node-gyp-build "^4.3.0"
@@ -19929,11 +20026,16 @@ ws@7.5.3:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
ws@8.11.0, ws@>=7.4.6:
ws@8.11.0:
version "8.11.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
ws@>=7.4.6, ws@^8.5.0:
version "8.12.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.1.tgz#c51e583d79140b5e42e39be48c934131942d4a8f"
integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==
ws@^6.2.1:
version "6.2.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e"