Compare commits

...

12 Commits

Author SHA1 Message Date
shortcircuit
c1297b2aa3 fix: Use html for linking to tax services (#6124)
* Use html for linking to tax services

* Add target blank

* styling

* override default styling

---------

Co-authored-by: Lynn Yu <lynn.yu@uniswap.org>
2023-03-10 13:57:48 -05:00
lynn
6ab6f4daa5 fix: Revert "fix: use chain query param properly when activating" (#6122)
Revert "fix: use `chain` query param properly when activating (#6114)"

This reverts commit ae559d164a.
2023-03-10 11:32:31 -05:00
Vignesh Mohankumar
ae559d164a fix: use chain query param properly when activating (#6114)
* fix: use `chain` query param properly when activating

* move

* Update src/hooks/useSyncChainQuery.ts

Co-authored-by: Jordan Frankfurt <jordanwfrankfurt@gmail.com>

---------

Co-authored-by: Jordan Frankfurt <jordanwfrankfurt@gmail.com>
2023-03-09 22:24:14 -05:00
Zach Pomerantz
a90318cbe9 fix: update test-size (#6118)
* fix: update test-size

* fix: update main size
2023-03-09 14:49:39 -08:00
Zach Pomerantz
93cf4b358e build: ignore types and hooks and update test-size (#6117)
build: gitignore types and hooks
2023-03-09 14:27:46 -08:00
Vignesh Mohankumar
ad8aff0b90 chore: update graphql types (#6115) 2023-03-09 16:50:14 -05:00
lynn
8757e413bb feat: tax service toast + modal (#6101)
* working

* unused export

* remove fiat on ramp toast

* design feedback tweaks

* add test + final design tweaks from fred

* add back fiat on ramp modal

* temp

* remove temp

* remove fiat on ramp stuff

* add back modal

* remove fiat on ramp ack from reducer

* respond to jordan

* mobile full width

* add feature flag

* fixes

* TESTING FEATURE FLAG

* testing w blair

* add stat sig server to prod env

* testing stat sig, wait for initialization

* revert stat sig testing stuff

* fix
2023-03-09 14:19:45 -05:00
Vignesh Mohankumar
78ac7650ee fix: remove full range warning in AddLiquidity (#6113)
* fix: remove full range warning in AddLiquidity

* rm button yellow
2023-03-09 13:59:03 -05:00
Jordan Frankfurt
f3e9b513ba fix: dont fetch a route for wrap transactions (#6102)
* fix: dont fetch a route for wrap transactions

* refactor logic into useBestTrade

* add mock for useWeb3React

* remove unnecessary diff

* fix tests

* Update src/hooks/useBestTrade.ts

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

* Update src/hooks/useBestTrade.ts

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

* remove unnecessary wethContract reference

---------

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2023-03-09 11:16:33 -06:00
Annie Ke
412a10e0d9 fix: change optimism goerli addresses to actual deploy (#5542)
* fix: change optimism goerli addresses to actual deploy

* Update addresses.ts

---------

Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
2023-03-09 11:56:15 -05:00
Jesse
411b690ae4 chore: update Celo logo with new version (#6111)
Update Celo logo SVG with new version
2023-03-09 11:38:34 -05:00
Tina
b721824c7f feat: Add USD amounts to swap analytics (#6108) 2023-03-09 10:07:46 -05:00
37 changed files with 427 additions and 8086 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@
/src/locales/**/pseudo.po
# generated graphql types
/src/graphql/**/__generated__
schema.graphql
# dependencies

View File

@@ -5,9 +5,7 @@ const BONSAI_COLLECTION_ADDRESS = '0xec9c519d49856fd2f8133a0741b4dbe002ce211b'
describe('Testing nfts', () => {
beforeEach(() => {
cy.visit('/').then(() => {
cy.get(getTestSelector('FiatOnrampAnnouncement-close')).first().click()
})
cy.visit('/')
})
it('should load nft leaderboard', () => {

View File

@@ -1,5 +1,3 @@
import { getTestSelector } from '../utils'
describe('Pool', () => {
beforeEach(() => {
cy.visit('/pool').then(() => {
@@ -8,18 +6,12 @@ describe('Pool', () => {
})
it('add liquidity links to /add/ETH', () => {
cy.get('body')
.then((body) => {
if (body.find(getTestSelector('FiatOnrampAnnouncement-close')).length > 0) {
cy.get(getTestSelector('FiatOnrampAnnouncement-close')).click()
}
})
.then(() => {
cy.get('#join-pool-button')
.click()
.then(() => {
cy.url().should('contain', '/add/ETH')
})
})
cy.get('body').then(() => {
cy.get('#join-pool-button')
.click()
.then(() => {
cy.url().should('contain', '/add/ETH')
})
})
})
})

View File

@@ -135,7 +135,7 @@
"@sentry/react": "^7.29.0",
"@types/react-window-infinite-loader": "^1.0.6",
"@uniswap/analytics": "^1.3.1",
"@uniswap/analytics-events": "^2.5.1",
"@uniswap/analytics-events": "^2.6.0",
"@uniswap/conedison": "^1.4.0",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",

View File

@@ -16,10 +16,10 @@ try {
}
// The last recorded size for these assets, as reported by `yarn build`.
const MAX_SIZE_MAIN_KB = 361.36
const LAST_SIZE_MAIN_KB = 374
// This is the async-loaded js, called <number>.<hash>.js, with a matching css file.
const MAX_SIZE_ENTRY_MB = 1.38
const LAST_SIZE_ENTRY_KB = 1417
const SIZE_TOLERANCE_KB = 5
@@ -30,20 +30,22 @@ let fail = false
console.log('File sizes after gzip:\n')
jsEntrypoints.forEach((entrypoint) => {
const name = entrypoint.match(/\/([\w\d-]*)\./)[1]
const size = gzipSize(fs.readFileSync(path.join(buildDir, entrypoint)))
const size = gzipSize(fs.readFileSync(path.join(buildDir, entrypoint))) / 1024
let maxSize = MAX_SIZE_ENTRY_MB * 1024 * 1024
let maxSize = LAST_SIZE_ENTRY_KB + SIZE_TOLERANCE_KB
if (name === 'runtime-main') {
return
} else if (name === 'main') {
maxSize = MAX_SIZE_MAIN_KB * 1024
maxSize = LAST_SIZE_MAIN_KB + SIZE_TOLERANCE_KB
}
maxSize += SIZE_TOLERANCE_KB * 1024
const report = `\t${size.toFixed(2).padEnd(8)}kB\t${chalk.dim(
`max: ${maxSize.toFixed().padEnd(4)} kB`
)}\t${entrypoint}`
if (maxSize > size) {
console.log(chalk.green(`\t${toKb(maxSize)}\t${entrypoint}`))
console.log(chalk.green(report))
} else {
console.log(chalk.red(`\t${toKb(maxSize)}\t${entrypoint}`), '\tdid you import an unnecessary dependency?')
console.log(chalk.red(report), '\tdid you import an unnecessary dependency?')
fail = true
}
})
@@ -52,7 +54,3 @@ if (fail) {
console.log(chalk.yellow('Reduce the file size or update the size limit (in scripts/test-size.js)'))
process.exit(1)
}
function toKb(bytes) {
return ((bytes / 1024).toFixed(2) + ' kB').padEnd(8)
}

View File

@@ -1 +1,11 @@
<svg id="Celo_Rings" data-name="Celo Rings" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 950 950"><defs><style>.cls-1{fill:#fbcc5c;}.cls-2{fill:#35d07f;}.cls-3{fill:#5ea33b;}</style></defs><title>Artboard 1</title><path id="Bottom_Ring" data-name="Bottom Ring" class="cls-1" d="M375,850c151.88,0,275-123.12,275-275S526.88,300,375,300,100,423.12,100,575,223.12,850,375,850Zm0,100C167.9,950,0,782.1,0,575S167.9,200,375,200,750,367.9,750,575,582.1,950,375,950Z"/><path id="Top_Ring" data-name="Top Ring" class="cls-2" d="M575,650c151.88,0,275-123.12,275-275S726.88,100,575,100,300,223.12,300,375,423.12,650,575,650Zm0,100c-207.1,0-375-167.9-375-375S367.9,0,575,0,950,167.9,950,375,782.1,750,575,750Z"/><path id="Rings_Overlap" data-name="Rings Overlap" class="cls-3" d="M587.39,750a274.38,274.38,0,0,0,54.55-108.06A274.36,274.36,0,0,0,750,587.4a373.63,373.63,0,0,1-29.16,133.45A373.62,373.62,0,0,1,587.39,750ZM308.06,308.06A274.36,274.36,0,0,0,200,362.6a373.63,373.63,0,0,1,29.16-133.45A373.62,373.62,0,0,1,362.61,200,274.38,274.38,0,0,0,308.06,308.06Z"/></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 250 250" style="enable-background:new 0 0 250 250;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FCFF52;}
</style>
<circle class="st0" cx="125" cy="125" r="125"/>
<path d="M188.9,60.7H60.7v128.2h128.2v-44.8h-21.3c-7.3,16.3-23.8,27.7-42.7,27.7c-26,0-47.1-21.3-47.1-47.1c0-25.9,21.1-47,47.1-47
c19.3,0,35.8,11.7,43.1,28.4h20.9V60.7z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 630 B

View File

@@ -205,26 +205,6 @@ export const ButtonOutlined = styled(BaseButton)`
}
`
export const ButtonYellow = styled(BaseButton)`
background-color: ${({ theme }) => theme.accentWarningSoft};
color: ${({ theme }) => theme.accentWarning};
&:focus {
background-color: ${({ theme }) => theme.accentWarningSoft};
}
&:hover {
background: ${({ theme }) => theme.stateOverlayHover};
mix-blend-mode: normal;
}
&:active {
background-color: ${({ theme }) => theme.accentWarningSoft};
}
&:disabled {
background-color: ${({ theme }) => theme.accentWarningSoft};
opacity: 60%;
cursor: auto;
}
`
export const ButtonEmpty = styled(BaseButton)`
background-color: transparent;
color: ${({ theme }) => theme.accentAction};
@@ -422,7 +402,7 @@ function pickThemeButtonBackgroundColor({ theme, emphasis }: { theme: DefaultThe
case ButtonEmphasis.high:
return theme.accentAction
case ButtonEmphasis.promotional:
return theme.accentTextLightPrimary
return theme.accentActionSoft
case ButtonEmphasis.highSoft:
return theme.accentActionSoft
case ButtonEmphasis.low:
@@ -476,7 +456,7 @@ function pickThemeButtonTextColor({ theme, emphasis }: { theme: DefaultTheme; em
switch (emphasis) {
case ButtonEmphasis.high:
case ButtonEmphasis.promotional:
return theme.accentTextLightPrimary
return theme.accentAction
case ButtonEmphasis.highSoft:
return theme.accentAction
case ButtonEmphasis.low:

View File

@@ -3,6 +3,7 @@ import { GqlRoutingVariant, useGqlRoutingFlag } from 'featureFlags/flags/gqlRout
import { NftGraphqlVariant, useNftGraphqlFlag } from 'featureFlags/flags/nftlGraphql'
import { PayWithAnyTokenVariant, usePayWithAnyTokenFlag } from 'featureFlags/flags/payWithAnyToken'
import { SwapWidgetVariant, useSwapWidgetFlag } from 'featureFlags/flags/swapWidget'
import { TaxServiceVariant, useTaxServiceBannerFlag } from 'featureFlags/flags/taxServiceBanner'
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
@@ -229,6 +230,12 @@ export default function FeatureFlagModal() {
featureFlag={FeatureFlag.nftGraphql}
label="Migrate NFT read endpoints to GQL"
/>
<FeatureFlagOption
variant={TaxServiceVariant}
value={useTaxServiceBannerFlag()}
featureFlag={FeatureFlag.taxService}
label="Tax Service Banner"
/>
<FeatureFlagGroup name="Debug">
<FeatureFlagOption
variant={TraceJsonRpcVariant}

View File

@@ -1,148 +0,0 @@
import { Trans } from '@lingui/macro'
import { sendAnalyticsEvent } from '@uniswap/analytics'
import { InterfaceEventName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import fiatMaskUrl from 'assets/svg/fiat_mask.svg'
import { useCallback, useEffect, useState } from 'react'
import { X } from 'react-feather'
import { useToggleWalletDropdown } from 'state/application/hooks'
import { useAppSelector } from 'state/hooks'
import { useFiatOnrampAck } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { isMobile } from 'utils/userAgent'
const Arrow = styled.div`
top: -4px;
height: 16px;
position: absolute;
right: 16px;
width: 16px;
::before {
background: hsl(315.75, 93%, 83%);
border-top: none;
border-left: none;
box-sizing: border-box;
content: '';
height: 16px;
position: absolute;
transform: rotate(45deg);
width: 16px;
}
`
const ArrowWrapper = styled.div`
position: absolute;
right: 16px;
top: 90%;
width: 100%;
max-width: 320px;
min-height: 92px;
@media screen and (min-width: ${({ theme }) => theme.breakpoint.lg}px) {
right: 36px;
}
`
const CloseIcon = styled(X)`
color: white;
cursor: pointer;
position: absolute;
top: 8px;
right: 8px;
z-index: 1;
`
const Wrapper = styled.button`
background: radial-gradient(105% 250% at 100% 5%, hsla(318, 95%, 85%) 1%, hsla(331, 80%, 75%, 0.1) 84%),
linear-gradient(180deg, hsla(296, 92%, 67%, 0.5) 0%, hsla(313, 96%, 60%, 0.5) 130%);
background-color: hsla(297, 93%, 68%, 1);
border-radius: 12px;
border: none;
cursor: pointer;
outline: none;
overflow: hidden;
position: relative;
text-align: start;
max-width: 320px;
min-height: 92px;
width: 100%;
:before {
background-image: url(${fiatMaskUrl});
background-repeat: no-repeat;
content: '';
height: 100%;
position: absolute;
right: -154px; // roughly width of fiat mask image
top: 0;
width: 100%;
}
`
const Header = styled(ThemedText.SubHeader)`
color: white;
margin: 0;
padding: 12px 12px 4px;
position: relative;
`
const Body = styled(ThemedText.BodySmall)`
color: white;
margin: 0 12px 12px 12px !important;
position: relative;
`
const ANNOUNCEMENT_RENDERED = 'FiatOnrampAnnouncement-rendered'
const ANNOUNCEMENT_DISMISSED = 'FiatOnrampAnnouncement-dismissed'
const MAX_RENDER_COUNT = 3
export function FiatOnrampAnnouncement() {
const { account } = useWeb3React()
const [acks, acknowledge] = useFiatOnrampAck()
const [localClose, setLocalClose] = useState(false)
useEffect(() => {
if (!sessionStorage.getItem(ANNOUNCEMENT_RENDERED)) {
acknowledge({ renderCount: acks?.renderCount + 1 })
sessionStorage.setItem(ANNOUNCEMENT_RENDERED, 'true')
}
}, [acknowledge, acks])
const handleClose = useCallback(() => {
setLocalClose(true)
localStorage.setItem(ANNOUNCEMENT_DISMISSED, 'true')
}, [])
const toggleWalletDropdown = useToggleWalletDropdown()
const handleClick = useCallback(() => {
sendAnalyticsEvent(InterfaceEventName.FIAT_ONRAMP_BANNER_CLICKED)
toggleWalletDropdown()
acknowledge({ user: true })
}, [acknowledge, toggleWalletDropdown])
const openModal = useAppSelector((state) => state.application.openModal)
if (
!account ||
acks?.user ||
localStorage.getItem(ANNOUNCEMENT_DISMISSED) ||
acks?.renderCount >= MAX_RENDER_COUNT ||
isMobile ||
openModal !== null ||
localClose
) {
return null
}
return (
<ArrowWrapper>
<Arrow />
<CloseIcon onClick={handleClose} data-testid="FiatOnrampAnnouncement-close" />
<Wrapper onClick={handleClick}>
<Header>
<Trans>Buy crypto</Trans>
</Header>
<Body>
<Trans>Get tokens at the best prices in web3 on Uniswap, powered by Moonpay.</Trans>
</Body>
</Wrapper>
</ArrowWrapper>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +1,160 @@
import { Trans } from '@lingui/macro'
import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button'
import { bodySmall, subhead } from 'nft/css/common.css'
import { useState } from 'react'
import { useCallback } from 'react'
import { X } from 'react-feather'
import { useIsDarkMode } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { opacify } from 'theme/utils'
import { Z_INDEX } from 'theme/zIndex'
import TaxServiceModal from '.'
import CointrackerLogo from './CointrackerLogo.png'
import TokenTaxLogo from './TokenTaxLogo.png'
const PopupContainer = styled.div<{ show: boolean; isDarkMode: boolean }>`
box-shadow: ${({ theme }) => theme.deepShadow};
border: 1px solid ${({ theme }) => theme.backgroundOutline};
background-color: ${({ theme }) => theme.backgroundSurface};
border-radius: 13px;
cursor: pointer;
color: ${({ theme }) => theme.textPrimary};
display: ${({ show }) => (show ? 'flex' : 'none')};
flex-direction: column;
position: fixed;
right: clamp(0px, 1vw, 16px);
z-index: ${Z_INDEX.sticky};
transition: ${({
theme: {
transition: { duration, timing },
},
}) => `${duration.slow} opacity ${timing.in}`};
width: 320px;
height: 156px;
bottom: 50px;
@media screen and (max-width: ${({ theme }) => theme.breakpoint.sm}px) {
width: 100%;
}
::before {
content: '';
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
background-image: url(${CointrackerLogo}), url(${TokenTaxLogo});
background-size: 15%, 20%;
background-repeat: no-repeat;
background-position: top right 75px, bottom 5px right 7px;
@media screen and (max-width: ${({ theme }) => theme.breakpoint.sm}px) {
background-size: 48px, 64px;
background-position: top right 75px, bottom 20px right 7px;
}
opacity: ${({ isDarkMode }) => (isDarkMode ? '0.9' : '0.25')};
}
`
const InnerContainer = styled.div<{ isDarkMode: boolean }>`
border-radius: 12px;
cursor: auto;
overflow: hidden;
display: flex;
flex-direction: column;
position: relative;
gap: 8px;
padding: 16px;
background-color: ${({ isDarkMode, theme }) =>
isDarkMode ? opacify(10, theme.accentAction) : opacify(4, theme.accentAction)};
`
const Button = styled(ThemeButton)`
margin-top: auto;
margin-right: auto;
padding: 8px 24px;
gap: 8px;
border-radius: 12px;
`
const TextContainer = styled.div`
user-select: none;
display: flex;
flex-direction: column;
width: 70%;
justify-content: center;
`
export const StyledXButton = styled(X)`
color: ${({ theme }) => theme.textPrimary};
cursor: pointer;
&:hover {
opacity: ${({ theme }) => theme.opacity.hover};
}
&:active {
opacity: ${({ theme }) => theme.opacity.click};
}
`
const TAX_SERVICE_DISMISSED = 'TaxServiceToast-dismissed'
export default function TaxServiceBanner() {
const isDarkMode = useIsDarkMode()
const [modalOpen, setModalOpen] = useState(false)
const sessionStorageTaxServiceDismissed = sessionStorage.getItem(TAX_SERVICE_DISMISSED)
if (!sessionStorageTaxServiceDismissed) {
sessionStorage.setItem(TAX_SERVICE_DISMISSED, 'false')
}
const [bannerOpen, setBannerOpen] = useState(sessionStorageTaxServiceDismissed !== 'true')
const onDismiss = useCallback(() => {
setModalOpen(false)
}, [])
const openTaxModal = useCallback(() => {
setModalOpen(true)
}, [])
const handleClose = useCallback(() => {
sessionStorage.setItem(TAX_SERVICE_DISMISSED, 'true')
setBannerOpen(false)
}, [])
return (
<PopupContainer show={bannerOpen} isDarkMode={isDarkMode}>
<InnerContainer isDarkMode={isDarkMode}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<TextContainer data-testid="tax-service-description">
<div className={subhead} style={{ paddingBottom: '12px' }}>
<Trans>Save on your crypto taxes</Trans>
</div>
<div className={bodySmall} style={{ paddingBottom: '12px' }}>
<Trans>Get up to a 20% discount on CoinTracker or TokenTax.</Trans>{' '}
</div>
</TextContainer>
<StyledXButton size={20} onClick={handleClose} />
</div>
<TraceEvent
events={[BrowserEvent.onClick]}
name={SharedEventName.ELEMENT_CLICKED}
element={InterfaceElementName.TAX_SERVICE_BANNER_CTA_BUTTON}
>
<Button
size={ButtonSize.small}
emphasis={ButtonEmphasis.promotional}
onClick={openTaxModal}
data-testid="learn-more-button"
>
<Trans>Learn more</Trans>
</Button>
</TraceEvent>
</InnerContainer>
<TaxServiceModal isOpen={modalOpen} onDismiss={onDismiss} />
</PopupContainer>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,17 @@
import { render, screen } from '../../test-utils'
import TaxServiceModal from './'
import TaxServiceBanner from './TaxServiceBanner'
it('renders Tax Service Modal content', async () => {
render(<TaxServiceModal isOpen={true} onDismiss={() => null} />)
expect(screen.getByText('Save 10% on all plans')).toBeInTheDocument()
expect(screen.getByText('New and existing users save up to 20%')).toBeInTheDocument()
expect(screen.getAllByTestId('tax-service-option-button')).toHaveLength(2)
})
it('renders Tax Service Banner', async () => {
render(<TaxServiceBanner />)
expect(screen.getByText('Save on your crypto taxes')).toBeInTheDocument()
expect(screen.getAllByTestId('learn-more-button')).toHaveLength(1)
expect(screen.getByText('Get up to a 20% discount on CoinTracker or TokenTax.')).toBeInTheDocument()
})

View File

@@ -0,0 +1,127 @@
import { Trans } from '@lingui/macro'
import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'
import { ButtonEmphasis } from 'components/Button'
import { ButtonSize, ThemeButton } from 'components/Button'
import { Box } from 'nft/components/Box'
import { bodySmall, subhead } from 'nft/css/common.css'
import { memo } from 'react'
import styled from 'styled-components/macro'
import Modal from '../Modal'
import CointrackerFullLogo from './CointrackerFullLogo.png'
import { StyledXButton } from './TaxServiceBanner'
import TokenTaxFullLogo from './TokenTaxFullLogo.png'
interface TaxServiceModalProps {
isOpen: boolean
onDismiss: () => void
}
interface TaxServiceOptionProps {
logo: any
description: string
url: string
}
const InnerContainer = styled.div`
background-color: ${({ theme }) => theme.backgroundSurface};
overflow: hidden;
display: flex;
width: 420px;
height: 268px;
flex-direction: column;
position: relative;
gap: 20px;
padding: 16px;
`
const TaxOptionContainer = styled.div`
display: flex;
flex: 1;
gap: 16px;
justify-content: center;
`
const TaxOptionDescription = styled.div`
display: flex;
height: 100%;
justify-content: center;
user-select: none;
text-align: center;
`
const TaxOption = styled.div`
align-items: center;
background-color: ${({ theme }) => theme.backgroundModule};
border-radius: 12px;
cursor: auto;
display: flex;
flex-direction: column;
flex: 1;
justify-content: space-between;
padding: 12px;
gap: 16px;
`
const StyledImageContainer = styled(Box)`
width: 75%;
height: 80%;
cursor: auto;
object-fit: contain;
`
const Button = styled(ThemeButton)`
cursor: pointer;
width: 100%;
margin-right: auto;
`
const TOKEN_TAX_URL = 'https://tokentax.co/uniswap?via=uniswap'
const COINTRACKER_URL = 'https://www.cointracker.io/partner/uniswap?utm_source=uniswap'
const TOKEN_TAX_DESCRIPTION = 'Save 10% on all plans'
const COINTRACKER_DESCRIPTION = 'New and existing users save up to 20%'
function TaxServiceOption({ description, logo, url }: TaxServiceOptionProps) {
return (
<TaxOption tabIndex={0}>
<StyledImageContainer as="img" src={logo} draggable={false} />
<TaxOptionDescription className={bodySmall}>{description}</TaxOptionDescription>
<TraceEvent
events={[BrowserEvent.onClick]}
name={SharedEventName.ELEMENT_CLICKED}
element={
url.includes('tokentax')
? InterfaceElementName.TAX_SERVICE_TOKENTAX_BUTTON
: InterfaceElementName.TAX_SERVICE_COINTRACKER_BUTTON
}
>
<a href={url} target="_blank" rel="noreferrer" style={{ textDecoration: 'none' }}>
<Button size={ButtonSize.medium} emphasis={ButtonEmphasis.medium} data-testid="tax-service-option-button">
Get started
</Button>
</a>
</TraceEvent>
</TaxOption>
)
}
export default memo(function TaxServiceModal({ isOpen, onDismiss }: TaxServiceModalProps) {
return (
<Modal isOpen={isOpen} onDismiss={onDismiss} maxHeight={90} minHeight={false}>
<InnerContainer>
<div style={{ display: 'flex', justifyContent: 'space-between', userSelect: 'none' }}>
<div className={subhead}>
<Trans>Save on your crypto taxes</Trans>
</div>
<StyledXButton size={20} onClick={onDismiss} />
</div>
<TaxOptionContainer>
<TaxServiceOption description={COINTRACKER_DESCRIPTION} logo={CointrackerFullLogo} url={COINTRACKER_URL} />
<TaxServiceOption description={TOKEN_TAX_DESCRIPTION} logo={TokenTaxFullLogo} url={TOKEN_TAX_URL} />
</TaxOptionContainer>
</InnerContainer>
</Modal>
)
})

View File

@@ -4,4 +4,3 @@ export const LARGE_MEDIA_BREAKPOINT = '840px'
export const MEDIUM_MEDIA_BREAKPOINT = '720px'
export const SMALL_MEDIA_BREAKPOINT = '540px'
export const MOBILE_MEDIA_BREAKPOINT = '420px'
// export const SMALL_MOBILE_MEDIA_BREAKPOINT = '390px'

View File

@@ -2,6 +2,8 @@ import { useWeb3React } from '@web3-react/core'
import AddressClaimModal from 'components/claim/AddressClaimModal'
import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'
import FiatOnrampModal from 'components/FiatOnrampModal'
import TaxServiceBanner from 'components/TaxServiceModal/TaxServiceBanner'
import { useTaxServiceBannerEnabled } from 'featureFlags/flags/taxServiceBanner'
import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
import { lazy } from 'react'
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
@@ -18,6 +20,7 @@ export default function TopLevelModals() {
const { account } = useWeb3React()
useAccountRiskCheck(account)
const accountBlocked = Boolean(blockedAccountModalOpen && account)
const taxServiceEnabled = useTaxServiceBannerEnabled()
return (
<>
@@ -27,6 +30,7 @@ export default function TopLevelModals() {
<TransactionCompleteModal />
<AirdropModal />
<FiatOnrampModal />
{taxServiceEnabled && <TaxServiceBanner />}
</>
)
}

View File

@@ -12,16 +12,14 @@ import { SupportedChainId } from 'constants/chains'
import useCopyClipboard from 'hooks/useCopyClipboard'
import useStablecoinPrice from 'hooks/useStablecoinPrice'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import ms from 'ms.macro'
import { useProfilePageState, useSellAsset, useWalletCollections } from 'nft/hooks'
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'
import { ProfilePageStateType } from 'nft/types'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useCallback, useMemo, useState } from 'react'
import { Copy, CreditCard, ExternalLink as ExternalLinkIcon, Info, Power } from 'react-feather'
import { useNavigate } from 'react-router-dom'
import { useCurrencyBalanceString } from 'state/connection/hooks'
import { useAppDispatch } from 'state/hooks'
import { useFiatOnrampAck } from 'state/user/hooks'
import { updateSelectedWallet } from 'state/user/reducer'
import styled, { css, keyframes } from 'styled-components/macro'
import { ExternalLink, ThemedText } from 'theme'
@@ -48,7 +46,7 @@ const BuyCryptoButtonBorderKeyframes = keyframes`
}
`
const BuyCryptoButton = styled(ThemeButton)<{ $animateBorder: boolean }>`
const BuyCryptoButton = styled(ThemeButton)`
border-color: transparent;
border-radius: 12px;
border-style: solid;
@@ -60,7 +58,6 @@ const BuyCryptoButton = styled(ThemeButton)<{ $animateBorder: boolean }>`
animation-fill-mode: none;
animation-iteration-count: 2;
animation-name: ${BuyCryptoButtonBorderKeyframes};
animation-play-state: ${({ $animateBorder }) => ($animateBorder ? 'running' : 'paused')};
animation-timing-function: ${({ theme }) => theme.transition.timing.inOut};
`
const WalletButton = styled(ThemeButton)`
@@ -223,26 +220,6 @@ const AuthenticatedHeader = () => {
closeModal()
}, [clearCollectionFilters, closeModal, navigate, resetSellAssets, setSellPageState])
// animate the border of the buy crypto button when a user navigates here from the feature announcement
// can be removed when components/FiatOnrampAnnouncment.tsx is no longer used
const [acknowledgements, acknowledge] = useFiatOnrampAck()
const animateBuyCryptoButtonBorder = acknowledgements?.user && !acknowledgements.system
useEffect(() => {
let stale = false
let timeoutId = 0
if (animateBuyCryptoButtonBorder) {
timeoutId = setTimeout(() => {
if (stale) return
acknowledge({ system: true })
}, ms`2 seconds`) as unknown as number
// as unknown as number is necessary so it's not incorrectly typed as a NodeJS.Timeout
}
return () => {
stale = true
clearTimeout(timeoutId)
}
}, [acknowledge, animateBuyCryptoButtonBorder])
const openFiatOnrampModal = useOpenModal(ApplicationModal.FIAT_ONRAMP)
const openFoRModalWithAnalytics = useCallback(() => {
sendAnalyticsEvent(InterfaceEventName.FIAT_ONRAMP_WIDGET_OPENED)
@@ -314,7 +291,6 @@ const AuthenticatedHeader = () => {
<Trans>View and sell NFTs</Trans>
</ProfileButton>
<BuyCryptoButton
$animateBorder={animateBuyCryptoButtonBorder}
size={ButtonSize.medium}
emphasis={ButtonEmphasis.medium}
onClick={handleBuyCryptoClick}

View File

@@ -2,7 +2,6 @@ import { t, Trans } from '@lingui/macro'
import { sendAnalyticsEvent, TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, InterfaceEventName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import { FiatOnrampAnnouncement } from 'components/FiatOnrampAnnouncement'
import { IconWrapper } from 'components/Identicon/StatusIcon'
import WalletDropdown from 'components/WalletDropdown'
import { getConnection } from 'connection/utils'
@@ -318,7 +317,6 @@ export default function Web3Status() {
return (
<span ref={ref}>
<Web3StatusInner />
<FiatOnrampAnnouncement />
<WalletModal ENSName={ENSName ?? undefined} pendingTransactions={pending} confirmedTransactions={confirmed} />
<Portal>
<span ref={walletRef}>

View File

@@ -107,6 +107,8 @@ export function useSyncWidgetTransactions() {
const eventProperties = {
...formatSwapSignedAnalyticsEventProperties({
trade,
// TODO: add once Widgets adds fiat values to callback
fiatValues: { amountIn: null, amountOut: null },
txHash: transaction.receipt?.transactionHash ?? '',
}),
...trace,

View File

@@ -21,6 +21,14 @@ const CELO_QUOTER_ADDRESSES = '0x82825d0554fA07f7FC52Ab63c961F330fdEFa8E8'
const CELO_NONFUNGIBLE_POSITION_MANAGER_ADDRESSES = '0x3d79EdAaBC0EaB6F08ED885C05Fc0B014290D95A'
const CELO_TICK_LENS_ADDRESSES = '0x5f115D9113F88e0a0Db1b5033D90D4a9690AcD3D'
// optimism goerli addresses
const OPTIMISM_GOERLI_V3_CORE_FACTORY_ADDRESSES = '0xB656dA17129e7EB733A557f4EBc57B76CFbB5d10'
const OPTIMISM_GOERLI_V3_MIGRATOR_ADDRESSES = '0xf6c55fBe84B1C8c3283533c53F51bC32F5C7Aba8'
const OPTIMISM_GOERLI_MULTICALL_ADDRESS = '0x07F2D8a2a02251B62af965f22fC4744A5f96BCCd'
const OPTIMISM_GOERLI_QUOTER_ADDRESSES = '0x9569CbA925c8ca2248772A9A4976A516743A246F'
const OPTIMISM_GOERLI_NONFUNGIBLE_POSITION_MANAGER_ADDRESSES = '0x39Ca85Af2F383190cBf7d7c41ED9202D27426EF6'
const OPTIMISM_GOERLI_TICK_LENS_ADDRESSES = '0xe6140Bd164b63E8BfCfc40D5dF952f83e171758e'
// arbitrum goerli v3 addresses
const ARBITRUM_GOERLI_V3_CORE_FACTORY_ADDRESSES = '0x4893376342d5D7b3e31d4184c08b265e5aB2A3f6'
const ARBITRUM_GOERLI_V3_MIGRATOR_ADDRESSES = '0xA815919D2584Ac3F76ea9CB62E6Fd40a43BCe0C3'
@@ -33,13 +41,13 @@ const ARBITRUM_GOERLI_TICK_LENS_ADDRESSES = '0xb52429333da969a0C79a60930a4Bf0020
export const V3_CORE_FACTORY_ADDRESSES: AddressMap = {
...constructSameAddressMap(V3_FACTORY_ADDRESS, [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISM_GOERLI,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.POLYGON_MUMBAI,
SupportedChainId.POLYGON,
]),
[SupportedChainId.CELO]: CELO_V3_CORE_FACTORY_ADDRESSES,
[SupportedChainId.CELO_ALFAJORES]: CELO_V3_CORE_FACTORY_ADDRESSES,
[SupportedChainId.OPTIMISM_GOERLI]: OPTIMISM_GOERLI_V3_CORE_FACTORY_ADDRESSES,
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_V3_CORE_FACTORY_ADDRESSES,
}
@@ -51,12 +59,12 @@ export const V3_MIGRATOR_ADDRESSES: AddressMap = {
]),
[SupportedChainId.CELO]: CELO_V3_MIGRATOR_ADDRESSES,
[SupportedChainId.CELO_ALFAJORES]: CELO_V3_MIGRATOR_ADDRESSES,
[SupportedChainId.OPTIMISM_GOERLI]: OPTIMISM_GOERLI_V3_MIGRATOR_ADDRESSES,
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_V3_MIGRATOR_ADDRESSES,
}
export const MULTICALL_ADDRESS: AddressMap = {
...constructSameAddressMap('0x1F98415757620B543A52E61c46B32eB19261F984', [
SupportedChainId.OPTIMISM_GOERLI,
SupportedChainId.OPTIMISM,
SupportedChainId.POLYGON_MUMBAI,
SupportedChainId.POLYGON,
@@ -64,6 +72,7 @@ export const MULTICALL_ADDRESS: AddressMap = {
[SupportedChainId.ARBITRUM_ONE]: '0xadF885960B47eA2CD9B55E6DAc6B42b7Cb2806dB',
[SupportedChainId.CELO]: CELO_MULTICALL_ADDRESS,
[SupportedChainId.CELO_ALFAJORES]: CELO_MULTICALL_ADDRESS,
[SupportedChainId.OPTIMISM_GOERLI]: OPTIMISM_GOERLI_MULTICALL_ADDRESS,
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_MULTICALL_ADDRESS,
}
@@ -99,26 +108,26 @@ export const ARGENT_WALLET_DETECTOR_ADDRESS: AddressMap = {
export const QUOTER_ADDRESSES: AddressMap = {
...constructSameAddressMap('0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6', [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISM_GOERLI,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.POLYGON_MUMBAI,
SupportedChainId.POLYGON,
]),
[SupportedChainId.CELO]: CELO_QUOTER_ADDRESSES,
[SupportedChainId.CELO_ALFAJORES]: CELO_QUOTER_ADDRESSES,
[SupportedChainId.OPTIMISM_GOERLI]: OPTIMISM_GOERLI_QUOTER_ADDRESSES,
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_QUOTER_ADDRESSES,
}
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: AddressMap = {
...constructSameAddressMap('0xC36442b4a4522E871399CD717aBDD847Ab11FE88', [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISM_GOERLI,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.POLYGON_MUMBAI,
SupportedChainId.POLYGON,
]),
[SupportedChainId.CELO]: CELO_NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
[SupportedChainId.CELO_ALFAJORES]: CELO_NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
[SupportedChainId.OPTIMISM_GOERLI]: OPTIMISM_GOERLI_NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
}
@@ -136,4 +145,5 @@ export const TICK_LENS_ADDRESSES: AddressMap = {
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_TICK_LENS_ADDRESSES,
[SupportedChainId.CELO]: CELO_TICK_LENS_ADDRESSES,
[SupportedChainId.CELO_ALFAJORES]: CELO_TICK_LENS_ADDRESSES,
[SupportedChainId.OPTIMISM_GOERLI]: OPTIMISM_GOERLI_TICK_LENS_ADDRESSES,
}

View File

@@ -9,4 +9,5 @@ export enum FeatureFlag {
gqlRouting = 'gqlRouting',
statsigDummy = 'web_dummy_gate_amplitude_id',
nftGraphql = 'nft_graphql_migration',
taxService = 'tax_service_banner',
}

View File

@@ -0,0 +1,11 @@
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
export function useTaxServiceBannerFlag(): BaseVariant {
return useBaseFlag(FeatureFlag.taxService, BaseVariant.Control)
}
export function useTaxServiceBannerEnabled(): boolean {
return useTaxServiceBannerFlag() === BaseVariant.Enabled
}
export { BaseVariant as TaxServiceVariant }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,13 @@ import useIsWindowVisible from './useIsWindowVisible'
const USDCAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '10000')
const DAIAmount = CurrencyAmount.fromRawAmount(DAI, '10000')
jest.mock('@web3-react/core', () => {
return {
useWeb3React: () => ({
chainId: 1,
}),
}
})
jest.mock('./useAutoRouterSupported')
jest.mock('./useClientSideV3Trade')
jest.mock('./useDebounce')
@@ -69,8 +76,7 @@ describe('#useBestV3Trade ExactIn', () => {
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, DAI, RouterPreference.CLIENT)
expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
expect(result.current).toEqual({ state: TradeState.NO_ROUTE_FOUND, trade: undefined })
})
describe('when routing api is in non-error state', () => {
@@ -155,8 +161,7 @@ describe('#useBestV3Trade ExactOut', () => {
USDC_MAINNET,
RouterPreference.CLIENT
)
expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
expect(result.current).toEqual({ state: TradeState.NO_ROUTE_FOUND, trade: undefined })
})
describe('when routing api is in non-error state', () => {
it('does not compute client side v3 trade if routing api is LOADING', () => {

View File

@@ -1,4 +1,6 @@
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
import { useMemo } from 'react'
import { RouterPreference } from 'state/routing/slice'
import { InterfaceTrade, TradeState } from 'state/routing/types'
@@ -24,6 +26,7 @@ export function useBestTrade(
state: TradeState
trade: InterfaceTrade<Currency, Currency, TradeType> | undefined
} {
const { chainId } = useWeb3React()
const autoRouterSupported = useAutoRouterSupported()
const isWindowVisible = useIsWindowVisible()
@@ -32,16 +35,27 @@ export function useBestTrade(
200
)
const isAWrapTransaction = useMemo(() => {
if (!chainId || !amountSpecified || !debouncedOtherCurrency) return false
const weth = WRAPPED_NATIVE_CURRENCY[chainId]
return (
(amountSpecified.currency.isNative && weth?.equals(debouncedOtherCurrency)) ||
(debouncedOtherCurrency.isNative && weth?.equals(amountSpecified.currency))
)
}, [amountSpecified, chainId, debouncedOtherCurrency])
const shouldGetTrade = !isAWrapTransaction && isWindowVisible
const [clientSideRouter] = useClientSideRouter()
const routingAPITrade = useRoutingAPITrade(
tradeType,
autoRouterSupported && isWindowVisible ? debouncedAmount : undefined,
autoRouterSupported && shouldGetTrade ? debouncedAmount : undefined,
debouncedOtherCurrency,
clientSideRouter ? RouterPreference.CLIENT : RouterPreference.API
)
const isLoading = routingAPITrade.state === TradeState.LOADING
const useFallback = !autoRouterSupported || routingAPITrade.state === TradeState.NO_ROUTE_FOUND
const useFallback = (!autoRouterSupported || routingAPITrade.state === TradeState.NO_ROUTE_FOUND) && shouldGetTrade
// only use client side router if routing api trade failed or is not supported
const bestV3Trade = useClientSideV3Trade(

View File

@@ -1,5 +1,5 @@
import { Trade } from '@uniswap/router-sdk'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { PermitSignature } from 'hooks/usePermitAllowance'
import { useMemo } from 'react'
@@ -13,6 +13,7 @@ import { useUniversalRouterSwapCallback } from './useUniversalRouter'
// and the user has approved the slippage adjusted input amount for the trade
export function useSwapCallback(
trade: Trade<Currency, Currency, TradeType> | undefined, // trade to execute, required
fiatValues: { amountIn: CurrencyAmount<Currency> | null; amountOut: CurrencyAmount<Currency> | null }, // usd values for amount in and out, logged for analytics
allowedSlippage: Percent, // in bips
permitSignature: PermitSignature | undefined
): { callback: null | (() => Promise<string>) } {
@@ -20,7 +21,7 @@ export function useSwapCallback(
const addTransaction = useTransactionAdder()
const universalRouterSwapCallback = useUniversalRouterSwapCallback(trade, {
const universalRouterSwapCallback = useUniversalRouterSwapCallback(trade, fiatValues, {
slippageTolerance: allowedSlippage,
deadline,
permit: permitSignature,

View File

@@ -4,7 +4,7 @@ import { t } from '@lingui/macro'
import { sendAnalyticsEvent } from '@uniswap/analytics'
import { SwapEventName } from '@uniswap/analytics-events'
import { Trade } from '@uniswap/router-sdk'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { SwapRouter, UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk'
import { FeeOptions, toHex } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core'
@@ -27,6 +27,7 @@ interface SwapOptions {
export function useUniversalRouterSwapCallback(
trade: Trade<Currency, Currency, TradeType> | undefined,
fiatValues: { amountIn: CurrencyAmount<Currency> | null; amountOut: CurrencyAmount<Currency> | null },
options: SwapOptions
) {
const { account, chainId, provider } = useWeb3React()
@@ -66,7 +67,7 @@ export function useUniversalRouterSwapCallback(
.then((response) => {
sendAnalyticsEvent(
SwapEventName.SWAP_SIGNED,
formatSwapSignedAnalyticsEventProperties({ trade, txHash: response.hash })
formatSwapSignedAnalyticsEventProperties({ trade, fiatValues, txHash: response.hash })
)
if (tx.data !== response.data) {
sendAnalyticsEvent(SwapEventName.SWAP_MODIFIED_IN_WALLET, { txHash: response.hash })
@@ -84,6 +85,7 @@ export function useUniversalRouterSwapCallback(
}, [
account,
chainId,
fiatValues,
options.deadline,
options.feeOptions,
options.permit,

View File

@@ -36,9 +36,11 @@ export const getPriceUpdateBasisPoints = (
export const formatSwapSignedAnalyticsEventProperties = ({
trade,
fiatValues,
txHash,
}: {
trade: InterfaceTrade<Currency, Currency, TradeType> | Trade<Currency, Currency, TradeType>
fiatValues: { amountIn: CurrencyAmount<Currency> | null; amountOut: CurrencyAmount<Currency> | null }
txHash: string
}) => ({
transaction_hash: txHash,
@@ -48,6 +50,8 @@ export const formatSwapSignedAnalyticsEventProperties = ({
token_out_symbol: trade.outputAmount.currency.symbol,
token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals),
token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals),
token_in_amount_usd: fiatValues.amountIn ? parseFloat(fiatValues.amountIn.toFixed(2)) : undefined,
token_out_amount_usd: fiatValues.amountOut ? parseFloat(fiatValues.amountOut.toFixed(2)) : undefined,
price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)),
chain_id:
trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId

View File

@@ -21,7 +21,7 @@ import {
} from 'state/mint/v3/hooks'
import { useTheme } from 'styled-components/macro'
import { ButtonError, ButtonLight, ButtonPrimary, ButtonText, ButtonYellow } from '../../components/Button'
import { ButtonError, ButtonLight, ButtonPrimary, ButtonText } from '../../components/Button'
import { BlueCard, OutlineCard, YellowCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column'
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
@@ -53,7 +53,7 @@ import { Bound, Field } from '../../state/mint/v3/actions'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { TransactionType } from '../../state/transactions/types'
import { useIsExpertMode, useUserSlippageToleranceWithDefault } from '../../state/user/hooks'
import { ExternalLink, ThemedText } from '../../theme'
import { ThemedText } from '../../theme'
import approveAmountCalldata from '../../utils/approveAmountCalldata'
import { calculateGasMargin } from '../../utils/calculateGasMargin'
import { currencyId } from '../../utils/currencyId'
@@ -153,11 +153,6 @@ export default function AddLiquidity() {
const [showConfirm, setShowConfirm] = useState<boolean>(false)
const [attemptingTxn, setAttemptingTxn] = useState<boolean>(false) // clicked confirm
// capital efficiency warning
const [showCapitalEfficiencyWarning, setShowCapitalEfficiencyWarning] = useState(false)
useEffect(() => setShowCapitalEfficiencyWarning(false), [baseCurrency, quoteCurrency, feeAmount])
useEffect(() => {
if (
parsedQs.minPrice &&
@@ -805,7 +800,7 @@ export default function AddLiquidity() {
disabled={!feeAmount || invalidPool || (noLiquidity && !startPriceTypedValue)}
>
<StackedContainer>
<StackedItem style={{ opacity: showCapitalEfficiencyWarning ? '0.05' : 1 }}>
<StackedItem>
<AutoColumn gap="md">
{noLiquidity && (
<RowBetween>
@@ -831,65 +826,12 @@ export default function AddLiquidity() {
{!noLiquidity && (
<PresetsButtons
setFullRange={() => {
setShowCapitalEfficiencyWarning(true)
getSetFullRange()
}}
/>
)}
</AutoColumn>
</StackedItem>
{showCapitalEfficiencyWarning && (
<StackedItem zIndex={1}>
<YellowCard
padding="15px"
$borderRadius="12px"
height="100%"
style={{
borderColor: theme.deprecated_yellow3,
border: '1px solid',
}}
>
<AutoColumn gap="sm" style={{ height: '100%' }}>
<RowFixed>
<AlertTriangle stroke={theme.deprecated_yellow3} size="16px" />
<ThemedText.DeprecatedYellow ml="12px" fontSize="15px">
<Trans>Efficiency Comparison</Trans>
</ThemedText.DeprecatedYellow>
</RowFixed>
<RowFixed>
<ThemedText.DeprecatedYellow ml="12px" fontSize="13px" margin={0} fontWeight={400}>
<Trans>
Full range positions may earn less fees than concentrated positions. Learn more{' '}
<ExternalLink
style={{ color: theme.deprecated_yellow3, textDecoration: 'underline' }}
href="https://help.uniswap.org/en/articles/5434296-can-i-provide-liquidity-over-the-full-range-in-v3"
>
here
</ExternalLink>
.
</Trans>
</ThemedText.DeprecatedYellow>
</RowFixed>
<Row>
<ButtonYellow
padding="8px"
marginRight="8px"
$borderRadius="8px"
width="auto"
onClick={() => {
setShowCapitalEfficiencyWarning(false)
getSetFullRange()
}}
>
<ThemedText.DeprecatedBlack fontSize={13} color="black">
<Trans>I understand</Trans>
</ThemedText.DeprecatedBlack>
</ButtonYellow>
</Row>
</AutoColumn>
</YellowCard>
</StackedItem>
)}
</StackedContainer>
{outOfRange ? (

View File

@@ -333,10 +333,14 @@ export default function Swap({ className }: { className?: string }) {
[currencyBalances]
)
const showMaxButton = Boolean(maxInputAmount?.greaterThan(0) && !parsedAmounts[Field.INPUT]?.equalTo(maxInputAmount))
const swapFiatValues = useMemo(() => {
return { amountIn: fiatValueTradeInput, amountOut: fiatValueTradeOutput }
}, [fiatValueTradeInput, fiatValueTradeOutput])
// the callback to execute the swap
const { callback: swapCallback } = useSwapCallback(
trade,
swapFiatValues,
allowedSlippage,
allowance.state === AllowanceState.ALLOWED ? allowance.permitSignature : undefined
)

View File

@@ -17,7 +17,6 @@ import { AppState } from '../types'
import {
addSerializedPair,
addSerializedToken,
updateFiatOnrampAcknowledgments,
updateHideClosedPositions,
updateUserClientSideRouter,
updateUserDarkMode,
@@ -104,26 +103,6 @@ export function useExpertModeManager(): [boolean, () => void] {
return [expertMode, toggleSetExpertMode]
}
interface FiatOnrampAcknowledgements {
renderCount: number
system: boolean
user: boolean
}
export function useFiatOnrampAck(): [
FiatOnrampAcknowledgements,
(acknowledgements: Partial<FiatOnrampAcknowledgements>) => void
] {
const dispatch = useAppDispatch()
const fiatOnrampAcknowledgments = useAppSelector((state) => state.user.fiatOnrampAcknowledgments)
const setAcknowledgements = useCallback(
(acks: Partial<FiatOnrampAcknowledgements>) => {
dispatch(updateFiatOnrampAcknowledgments(acks))
},
[dispatch]
)
return [fiatOnrampAcknowledgments, setAcknowledgements]
}
export function useClientSideRouter(): [boolean, (userClientSideRouter: boolean) => void] {
const dispatch = useAppDispatch()

View File

@@ -82,12 +82,6 @@ const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
updateFiatOnrampAcknowledgments(
state,
{ payload }: { payload: Partial<{ renderCount: number; user: boolean; system: boolean }> }
) {
state.fiatOnrampAcknowledgments = { ...state.fiatOnrampAcknowledgments, ...payload }
},
updateSelectedWallet(state, { payload: { wallet } }) {
state.selectedWallet = wallet
},
@@ -181,7 +175,6 @@ const userSlice = createSlice({
export const {
addSerializedPair,
addSerializedToken,
updateFiatOnrampAcknowledgments,
updateSelectedWallet,
updateHideClosedPositions,
updateMatchesDarkMode,

View File

@@ -198,7 +198,7 @@ export const lightTheme: Theme = {
accentFailure: colors.red400,
accentCritical: colors.red400,
accentActionSoft: opacify(24, colors.pink400),
accentActionSoft: opacify(12, colors.pink400),
accentActiveSoft: opacify(24, colors.blue400),
accentSuccessSoft: opacify(24, colors.green300),
accentWarningSoft: opacify(24, colors.gold200),

View File

@@ -4989,10 +4989,10 @@
"@typescript-eslint/types" "5.47.0"
eslint-visitor-keys "^3.3.0"
"@uniswap/analytics-events@^2.5.1":
version "2.5.1"
resolved "https://registry.yarnpkg.com/@uniswap/analytics-events/-/analytics-events-2.5.1.tgz#26095391518954dd3c1248703b3996f07f034579"
integrity sha512-CkatfcWlKZFIQat+/j53AuxhpVntwrgizBYiUgnxcjVQ0Qh87XrCFqgwfTq44QD5bYwgbYjjw263MXujdEqUYg==
"@uniswap/analytics-events@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@uniswap/analytics-events/-/analytics-events-2.6.0.tgz#c6d21830f46b450f7f633d2c8e832d3531c00f24"
integrity sha512-8+El5RKKKs9CXRwRaIZ5t4onlnaTOZThBeO+vSTdr1KtYLp/updtwTR59wE7Yjb5Hj5I6OVKJwiPqAHxja/H4Q==
"@uniswap/analytics@^1.3.1":
version "1.3.1"