Commit 8c372c61 authored by Jack Short's avatar Jack Short Committed by GitHub

fix: fixing pwat routing edge case (#5987)

* 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
parent 8c0998bd
......@@ -66,7 +66,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) : 3};
raiseZIndex ? (isProfilePage ? Z_INDEX.modalOverTooltip : Z_INDEX.modalBackdrop + 2) : 3};
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
right: 0px;
......@@ -232,7 +232,8 @@ const Bag = () => {
const { route, routeResponse } = buildRouteResponse(data.nftRoute, !!tokenTradeInput)
const updatedAssets = combineBuyItemsWithTxRoute(itemsToBuy, route)
const { hasPriceAdjustment, updatedAssets } = combineBuyItemsWithTxRoute(itemsToBuy, route)
const shouldRefetchCalldata = hasPriceAdjustment && !!tokenTradeInput
const fetchedPriceChangedAssets = updatedAssets
.filter((asset) => asset.updatedPriceInfo)
......@@ -266,9 +267,13 @@ const Bag = () => {
if (hasAssets) {
if (!shouldReview) {
purchaseAssets(routeResponse)
setBagStatus(BagStatus.CONFIRMING_IN_WALLET)
shouldLock = true
if (shouldRefetchCalldata) {
setBagStatus(BagStatus.CONFIRM_QUOTE)
} else {
purchaseAssets(routeResponse)
setBagStatus(BagStatus.CONFIRMING_IN_WALLET)
shouldLock = true
}
} else if (!hasAssetsInReview) setBagStatus(BagStatus.CONFIRM_REVIEW)
else {
setBagStatus(BagStatus.IN_REVIEW)
......@@ -289,7 +294,7 @@ const Bag = () => {
})
)
const updatedAssets = combineBuyItemsWithTxRoute(itemsToBuy, routeData.route)
const { updatedAssets } = combineBuyItemsWithTxRoute(itemsToBuy, routeData.route)
const fetchedPriceChangedAssets = updatedAssets
.filter((asset) => asset.updatedPriceInfo)
......@@ -405,12 +410,7 @@ const Bag = () => {
{isProfilePage ? <ProfileBagContent /> : <BagContent />}
</Column>
{hasAssetsToShow && !isProfilePage && (
<BagFooter
totalEthPrice={totalEthPrice}
bagStatus={bagStatus}
fetchAssets={fetchAssets}
eventProperties={eventProperties}
/>
<BagFooter totalEthPrice={totalEthPrice} fetchAssets={fetchAssets} eventProperties={eventProperties} />
)}
{isSellingAssets && isProfilePage && (
<Box
......
......@@ -77,11 +77,11 @@ const TotalColumn = styled(Column)`
const WarningIcon = styled(AlertTriangle)`
width: 14px;
margin-right: 4px;
color: ${({ theme }) => theme.accentWarning};
color: inherit;
`
const WarningText = styled(ThemedText.BodyPrimary)`
const WarningText = styled(ThemedText.BodyPrimary)<{ $color: string }>`
align-items: center;
color: ${({ theme }) => theme.accentWarning};
color: ${({ $color }) => $color};
display: flex;
justify-content: center;
margin-bottom: 10px !important;
......@@ -173,22 +173,22 @@ const ActionButton = ({
)
}
const Warning = ({ children }: PropsWithChildren<unknown>) => {
interface HelperTextProps {
color: string
}
const Warning = ({ color, children }: PropsWithChildren<HelperTextProps>) => {
if (!children) {
return null
}
return (
<WarningText fontSize="14px" lineHeight="20px">
<WarningText fontSize="14px" lineHeight="20px" $color={color}>
<WarningIcon />
{children}
</WarningText>
)
}
interface HelperTextProps {
color: string
}
const Helper = ({ children, color }: PropsWithChildren<HelperTextProps>) => {
if (!children) {
return null
......@@ -281,7 +281,6 @@ const FiatValue = ({
interface BagFooterProps {
totalEthPrice: BigNumber
bagStatus: BagStatus
fetchAssets: () => void
eventProperties: Record<string, unknown>
}
......@@ -293,7 +292,7 @@ const PENDING_BAG_STATUSES = [
BagStatus.PROCESSING_TRANSACTION,
]
export const BagFooter = ({ totalEthPrice, bagStatus, fetchAssets, eventProperties }: BagFooterProps) => {
export const BagFooter = ({ totalEthPrice, fetchAssets, eventProperties }: BagFooterProps) => {
const toggleWalletModal = useToggleWalletModal()
const theme = useTheme()
const { account, chainId, connector } = useWeb3React()
......@@ -309,11 +308,13 @@ export const BagFooter = ({ totalEthPrice, bagStatus, fetchAssets, eventProperti
const {
isLocked: bagIsLocked,
bagStatus,
setBagExpanded,
setBagStatus,
} = useBag(
({ isLocked, setBagExpanded, setBagStatus }) => ({
({ isLocked, bagStatus, setBagExpanded, setBagStatus }) => ({
isLocked,
bagStatus,
setBagExpanded,
setBagStatus,
}),
......@@ -389,116 +390,131 @@ export const BagFooter = ({ totalEthPrice, bagStatus, fetchAssets, eventProperti
setBagStatus(BagStatus.ADDING_TO_BAG)
}, [inputCurrency, setBagStatus])
const { buttonText, buttonTextColor, disabled, warningText, helperText, helperTextColor, handleClick, buttonColor } =
useMemo(() => {
let handleClick = fetchAssets
let buttonText = <Trans>Something went wrong</Trans>
let disabled = true
let warningText = undefined
let helperText = undefined
let helperTextColor = theme.textSecondary
let buttonColor = theme.accentAction
let buttonTextColor = theme.accentTextLightPrimary
if (connected && chainId !== SupportedChainId.MAINNET) {
handleClick = () => switchChain(connector, SupportedChainId.MAINNET)
buttonText = <Trans>Switch networks</Trans>
disabled = false
warningText = <Trans>Wrong network</Trans>
} else if (sufficientBalance === false) {
buttonText = <Trans>Pay</Trans>
disabled = true
warningText = <Trans>Insufficient funds</Trans>
} else if (bagStatus === BagStatus.WARNING) {
warningText = <Trans>Something went wrong. Please try again.</Trans>
} else if (!connected) {
handleClick = () => {
toggleWalletModal()
setBagExpanded({ bagExpanded: false })
}
disabled = false
buttonText = <Trans>Connect wallet</Trans>
} else if (usingPayWithAnyToken && tradeState !== TradeState.VALID) {
disabled = true
buttonText = <Trans>Fetching Route</Trans>
if (tradeState === TradeState.INVALID) {
buttonText = <Trans>Pay</Trans>
}
if (tradeState === TradeState.NO_ROUTE_FOUND) {
buttonText = <Trans>Insufficient liquidity</Trans>
buttonColor = theme.backgroundInteractive
buttonTextColor = theme.textPrimary
helperText = <Trans>Insufficient pool liquidity to complete transaction</Trans>
}
} else if (allowance.state === AllowanceState.REQUIRED || allowance.state === AllowanceState.LOADING) {
handleClick = () => updateAllowance()
disabled = isAllowancePending || isApprovalLoading || allowance.state === AllowanceState.LOADING
if (allowance.state === AllowanceState.LOADING) {
buttonText = <Trans>Loading Allowance</Trans>
} else if (isAllowancePending) {
buttonText = <Trans>Approve in your wallet</Trans>
} else if (isApprovalLoading) {
buttonText = <Trans>Approval pending</Trans>
} else {
helperText = <Trans>An approval is needed to use this token. </Trans>
buttonText = <Trans>Approve</Trans>
}
} else if (bagStatus === BagStatus.FETCHING_FINAL_ROUTE || bagStatus === BagStatus.CONFIRMING_IN_WALLET) {
disabled = true
buttonText = <Trans>Proceed in wallet</Trans>
} else if (bagStatus === BagStatus.PROCESSING_TRANSACTION) {
disabled = true
buttonText = <Trans>Transaction pending</Trans>
} else if (priceImpactWarning && priceImpactColor) {
disabled = false
buttonColor = priceImpactColor
helperText = <Trans>Price impact warning</Trans>
helperTextColor = priceImpactColor
buttonText = <Trans>Pay Anyway</Trans>
} else if (sufficientBalance === true) {
disabled = false
const {
buttonText,
buttonTextColor,
disabled,
warningText,
warningTextColor,
helperText,
helperTextColor,
handleClick,
buttonColor,
} = useMemo(() => {
let handleClick = fetchAssets
let buttonText = <Trans>Something went wrong</Trans>
let disabled = true
let warningText = undefined
let warningTextColor = theme.accentWarning
let helperText = undefined
let helperTextColor = theme.textSecondary
let buttonColor = theme.accentAction
let buttonTextColor = theme.accentTextLightPrimary
if (connected && chainId !== SupportedChainId.MAINNET) {
handleClick = () => switchChain(connector, SupportedChainId.MAINNET)
buttonText = <Trans>Switch networks</Trans>
disabled = false
warningText = <Trans>Wrong network</Trans>
} else if (sufficientBalance === false) {
buttonText = <Trans>Pay</Trans>
disabled = true
warningText = <Trans>Insufficient funds</Trans>
} else if (bagStatus === BagStatus.WARNING) {
warningText = <Trans>Something went wrong. Please try again.</Trans>
} else if (!connected) {
handleClick = () => {
toggleWalletModal()
setBagExpanded({ bagExpanded: false })
}
disabled = false
buttonText = <Trans>Connect wallet</Trans>
} else if (bagStatus === BagStatus.FETCHING_FINAL_ROUTE || bagStatus === BagStatus.CONFIRMING_IN_WALLET) {
disabled = true
buttonText = <Trans>Proceed in wallet</Trans>
} else if (bagStatus === BagStatus.PROCESSING_TRANSACTION) {
disabled = true
buttonText = <Trans>Transaction pending</Trans>
} else if (usingPayWithAnyToken && tradeState !== TradeState.VALID) {
disabled = true
buttonText = <Trans>Fetching Route</Trans>
if (tradeState === TradeState.INVALID) {
buttonText = <Trans>Pay</Trans>
helperText = usingPayWithAnyToken ? (
<Trans>Refunds for unavailable items will be given in ETH</Trans>
) : undefined
}
return {
buttonText,
buttonTextColor,
disabled,
warningText,
helperText,
helperTextColor,
handleClick,
buttonColor,
if (tradeState === TradeState.NO_ROUTE_FOUND) {
buttonText = <Trans>Insufficient liquidity</Trans>
buttonColor = theme.backgroundInteractive
buttonTextColor = theme.textPrimary
helperText = <Trans>Insufficient pool liquidity to complete transaction</Trans>
}
}, [
fetchAssets,
theme.textSecondary,
theme.accentAction,
theme.accentTextLightPrimary,
theme.backgroundInteractive,
theme.textPrimary,
connected,
chainId,
sufficientBalance,
bagStatus,
usingPayWithAnyToken,
tradeState,
allowance.state,
priceImpactWarning,
priceImpactColor,
connector,
toggleWalletModal,
setBagExpanded,
isAllowancePending,
isApprovalLoading,
updateAllowance,
])
} else if (allowance.state === AllowanceState.REQUIRED || allowance.state === AllowanceState.LOADING) {
handleClick = () => updateAllowance()
disabled = isAllowancePending || isApprovalLoading || allowance.state === AllowanceState.LOADING
if (allowance.state === AllowanceState.LOADING) {
buttonText = <Trans>Loading Allowance</Trans>
} else if (isAllowancePending) {
buttonText = <Trans>Approve in your wallet</Trans>
} else if (isApprovalLoading) {
buttonText = <Trans>Approval pending</Trans>
} else {
helperText = <Trans>An approval is needed to use this token. </Trans>
buttonText = <Trans>Approve</Trans>
}
} else if (bagStatus === BagStatus.CONFIRM_QUOTE) {
disabled = false
warningTextColor = theme.accentAction
warningText = <Trans>Price updated</Trans>
buttonText = <Trans>Pay</Trans>
} else if (priceImpactWarning && priceImpactColor) {
disabled = false
buttonColor = priceImpactColor
helperText = <Trans>Price impact warning</Trans>
helperTextColor = priceImpactColor
buttonText = <Trans>Pay Anyway</Trans>
} else if (sufficientBalance === true) {
disabled = false
buttonText = <Trans>Pay</Trans>
helperText = usingPayWithAnyToken ? <Trans>Refunds for unavailable items will be given in ETH</Trans> : undefined
}
return {
buttonText,
buttonTextColor,
disabled,
warningText,
warningTextColor,
helperText,
helperTextColor,
handleClick,
buttonColor,
}
}, [
fetchAssets,
theme.accentWarning,
theme.textSecondary,
theme.accentAction,
theme.accentTextLightPrimary,
theme.backgroundInteractive,
theme.textPrimary,
connected,
chainId,
sufficientBalance,
bagStatus,
usingPayWithAnyToken,
tradeState,
allowance.state,
priceImpactWarning,
priceImpactColor,
connector,
toggleWalletModal,
setBagExpanded,
isAllowancePending,
isApprovalLoading,
updateAllowance,
])
const traceEventProperties = {
usd_value: usdcValue?.toExact(),
......@@ -574,7 +590,7 @@ export const BagFooter = ({ totalEthPrice, bagStatus, fetchAssets, eventProperti
properties={{ ...traceEventProperties }}
shouldLogImpression={connected && !disabled}
>
<Warning>{warningText}</Warning>
<Warning color={warningTextColor}>{warningText}</Warning>
<Helper color={helperTextColor}>{helperText}</Helper>
<ActionButton
onClick={handleClick}
......
......@@ -16,6 +16,6 @@ export const overlay = style([
{
opacity: 0.72,
overflow: 'hidden',
zIndex: Z_INDEX.modalBackdrop - 1,
zIndex: Z_INDEX.modalBackdrop + 1,
},
])
......@@ -143,7 +143,7 @@ const findNFTsPurchased = (
)
})
return combineBuyItemsWithTxRoute(transferredItems, txRoute)
return combineBuyItemsWithTxRoute(transferredItems, txRoute).updatedAssets
}
const findNFTsNotPurchased = (toBuy: GenieAsset[], nftsPurchased: UpdatedGenieAsset[]) => {
......
......@@ -96,4 +96,5 @@ export enum BagStatus {
FETCHING_FINAL_ROUTE = 'Fetching final route',
CONFIRMING_IN_WALLET = 'Confirming in wallet',
PROCESSING_TRANSACTION = 'Processing',
CONFIRM_QUOTE = 'Confirm quote',
}
......@@ -19,8 +19,11 @@ const isTheSame = (item: GenieAsset, routeAsset: BuyItem | PriceInfo) => {
}
}
const isPriceDiff = (oldPrice: string, newPrice: string) => {
return formatWeiToDecimal(oldPrice) !== formatWeiToDecimal(newPrice)
const getPriceDiff = (oldPrice: string, newPrice: string): { hasPriceDiff: boolean; hasVisiblePriceDiff: boolean } => {
const hasPriceDiff = oldPrice !== newPrice
const hasVisiblePriceDiff = formatWeiToDecimal(oldPrice) !== formatWeiToDecimal(newPrice)
return { hasPriceDiff, hasVisiblePriceDiff }
}
const isAveragePriceOfPooledAssets = (
......@@ -28,7 +31,7 @@ const isAveragePriceOfPooledAssets = (
numberOfAssetsInPool: number,
expectedPrice: string
): boolean => {
return !isPriceDiff(calcAvgGroupPoolPrice(asset, numberOfAssetsInPool), expectedPrice)
return !getPriceDiff(calcAvgGroupPoolPrice(asset, numberOfAssetsInPool), expectedPrice).hasVisiblePriceDiff
}
const isAveragedPrice = (
......@@ -74,11 +77,11 @@ const itemInRouteAndSamePool = (
export const combineBuyItemsWithTxRoute = (
items: UpdatedGenieAsset[],
txRoute?: RoutingItem[]
): UpdatedGenieAsset[] => {
return items.map((item) => {
): { hasPriceAdjustment: boolean; updatedAssets: UpdatedGenieAsset[] } => {
let hasPriceAdjustment = false
const updatedAssets = items.map((item) => {
const route = getRouteForItem(item, txRoute)
// if the item is not found in txRoute, it means it's no longer for sale
if (txRoute && !route) {
return {
...item,
......@@ -86,15 +89,21 @@ export const combineBuyItemsWithTxRoute = (
}
}
const newPriceInfo = item.updatedPriceInfo ? item.updatedPriceInfo : item.priceInfo
let newPriceInfo = item.updatedPriceInfo ? item.updatedPriceInfo : item.priceInfo
// if the price changed
if (route && 'priceInfo' in route.assetOut) {
if (isPriceDiff(newPriceInfo.basePrice, route.assetOut.priceInfo.basePrice)) {
const { hasPriceDiff, hasVisiblePriceDiff } = getPriceDiff(
newPriceInfo.basePrice,
route.assetOut.priceInfo.basePrice
)
newPriceInfo = route.assetOut.priceInfo
hasPriceAdjustment = hasPriceDiff
if (hasVisiblePriceDiff) {
if (!isAveragedPrice(item, items, route, txRoute)) {
return {
...item,
updatedPriceInfo: route.assetOut.priceInfo,
updatedPriceInfo: newPriceInfo,
}
}
}
......@@ -107,4 +116,6 @@ export const combineBuyItemsWithTxRoute = (
orderSource: route && 'orderSource' in route.assetOut ? route.assetOut.orderSource : undefined,
}
})
return { hasPriceAdjustment, updatedAssets }
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment