Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ea7b1de3f | ||
|
|
6efe8f3260 | ||
|
|
9ac28a4571 | ||
|
|
bde1421ffb | ||
|
|
3dceb45d9e | ||
|
|
7b589561bc |
@@ -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"
|
||||
|
||||
@@ -290,7 +290,7 @@ export default function CurrencyList({
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div style={{ paddingRight: '8px', paddingTop: '8px' }}>
|
||||
<div style={{ paddingRight: '4px' }}>
|
||||
{isLoading ? (
|
||||
<FixedSizeList
|
||||
className={styles.scrollbarStyle}
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 {
|
||||
|
||||
70
src/graphql/data/__generated__/types-and-hooks.ts
generated
70
src/graphql/data/__generated__/types-and-hooks.ts
generated
@@ -96,11 +96,6 @@ export enum Currency {
|
||||
Usd = 'USD'
|
||||
}
|
||||
|
||||
export enum DatasourceProvider {
|
||||
Alternate = 'ALTERNATE',
|
||||
Legacy = 'LEGACY'
|
||||
}
|
||||
|
||||
export type Dimensions = {
|
||||
__typename?: 'Dimensions';
|
||||
height?: Maybe<Scalars['Float']>;
|
||||
@@ -145,6 +140,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 +234,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 +348,6 @@ export type NftCollection = {
|
||||
|
||||
export type NftCollectionMarketsArgs = {
|
||||
currencies: Array<Currency>;
|
||||
datasource?: InputMaybe<DatasourceProvider>;
|
||||
};
|
||||
|
||||
export type NftCollectionConnection = {
|
||||
@@ -602,6 +638,7 @@ export type PortfolioTokensTotalDenominatedValueChangeArgs = {
|
||||
|
||||
export type Query = {
|
||||
__typename?: 'Query';
|
||||
nftActivity?: Maybe<NftActivityConnection>;
|
||||
nftAssets?: Maybe<NftAssetConnection>;
|
||||
nftBalances?: Maybe<NftBalanceConnection>;
|
||||
nftCollections?: Maybe<NftCollectionConnection>;
|
||||
@@ -617,13 +654,20 @@ export type Query = {
|
||||
};
|
||||
|
||||
|
||||
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 +680,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 +689,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 +711,6 @@ export type QueryNftRouteArgs = {
|
||||
|
||||
export type QueryPortfoliosArgs = {
|
||||
ownerAddresses: Array<Scalars['String']>;
|
||||
useAltDataSource?: InputMaybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -103,8 +103,9 @@ export const ListingButton = ({ onClick, buttonText, showWarningOverride = false
|
||||
return asset.expirationTime != null && isNaN(asset.expirationTime)
|
||||
})
|
||||
const overMaxExpiration = sellAssets.some((asset) => {
|
||||
return asset.expirationTime != null && asset.expirationTime - Date.now() > ms`180 days`
|
||||
return asset.expirationTime != null && asset.expirationTime * 1000 - Date.now() > ms`180 days`
|
||||
})
|
||||
|
||||
const listingsMissingPrice: [WalletAsset, Listing][] = []
|
||||
const listingsBelowFloor: [WalletAsset, Listing][] = []
|
||||
const listingsAboveSellOrderFloor: [WalletAsset, Listing][] = []
|
||||
@@ -285,7 +286,7 @@ export const ListingButton = ({ onClick, buttonText, showWarningOverride = false
|
||||
onClick={() => listingStatus !== ListingStatus.APPROVED && warningWrappedClick()}
|
||||
type="button"
|
||||
style={{
|
||||
color: showResolveIssues ? theme.accentTextDarkPrimary : theme.white,
|
||||
color: showResolveIssues ? theme.accentTextLightPrimary : theme.white,
|
||||
opacity:
|
||||
![ListingStatus.DEFINED, ListingStatus.FAILED, ListingStatus.CONTINUE].includes(listingStatus) ||
|
||||
(disableListButton && !showResolveIssues)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
@@ -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}> ETH</CurrencyWrapper>
|
||||
{(isGlobalPrice || globalOverride) && (
|
||||
<GlobalPriceIcon onClick={() => setGlobalOverride(!globalOverride)}>
|
||||
{globalOverride ? <AttachPriceIcon /> : <EditPriceIcon />}
|
||||
{globalOverride ? <BrokenLinkIcon /> : <Link size={20} color={warningColor} />}
|
||||
</GlobalPriceIcon>
|
||||
)}
|
||||
</InputWrapper>
|
||||
|
||||
@@ -138,7 +138,7 @@ export const ProfilePage = () => {
|
||||
borderRadius="12"
|
||||
paddingX="16"
|
||||
paddingY="12"
|
||||
background="backgroundModule"
|
||||
background="backgroundSurface"
|
||||
borderStyle="solid"
|
||||
borderColor="backgroundOutline"
|
||||
borderWidth="1px"
|
||||
|
||||
50
src/nft/hooks/usePriceImpact.ts
Normal file
50
src/nft/hooks/usePriceImpact.ts
Normal 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}%`
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user