Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e95e2321b4 | ||
|
|
8b1bf09ff1 | ||
|
|
6383e9e4bf | ||
|
|
515ce9253d | ||
|
|
23ed384802 | ||
|
|
9ae31aa26e |
2
.github/workflows/revert.yaml
vendored
2
.github/workflows/revert.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Release
|
||||
name: Revert
|
||||
on:
|
||||
# manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
describe.skip('Wallet Dropdown', () => {
|
||||
beforeEach(() => {
|
||||
describe('Wallet Dropdown', () => {
|
||||
before(() => {
|
||||
cy.visit('/pool')
|
||||
})
|
||||
|
||||
@@ -12,7 +12,6 @@ describe.skip('Wallet Dropdown', () => {
|
||||
})
|
||||
|
||||
it('should select a language', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-select-language')).click()
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
|
||||
cy.get(getTestSelector('wallet-header')).should('contain', 'Taal')
|
||||
@@ -22,24 +21,20 @@ describe.skip('Wallet Dropdown', () => {
|
||||
})
|
||||
|
||||
it('should be able to view transactions', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-transactions')).click()
|
||||
cy.get(getTestSelector('wallet-empty-transaction-text')).should('exist')
|
||||
cy.get(getTestSelector('wallet-back')).click()
|
||||
})
|
||||
|
||||
it('should change the theme when not connected', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-disconnect')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).contains('Light theme').should('exist')
|
||||
cy.get(getTestSelector('wallet-select-theme')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).contains('Dark theme').should('exist')
|
||||
cy.get(getTestSelector('wallet-select-theme')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).contains('Light theme').should('exist')
|
||||
})
|
||||
|
||||
it('should select a language when not connected', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-disconnect')).click()
|
||||
cy.get(getTestSelector('wallet-select-language')).click()
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
|
||||
cy.get(getTestSelector('wallet-header')).should('contain', 'Taal')
|
||||
@@ -49,8 +44,6 @@ describe.skip('Wallet Dropdown', () => {
|
||||
})
|
||||
|
||||
it('should open the wallet connect modal from the drop down when not connected', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-disconnect')).click()
|
||||
cy.get(getTestSelector('wallet-connect-wallet')).click()
|
||||
cy.get(getTestSelector('wallet-modal')).should('exist')
|
||||
cy.get(getTestSelector('wallet-modal-close')).click()
|
||||
|
||||
@@ -5,11 +5,18 @@ import { useWeb3React } from '@web3-react/core'
|
||||
import { AVERAGE_L1_BLOCK_TIME } from 'constants/chainInfo'
|
||||
import useInterval from 'lib/hooks/useInterval'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useHasPendingApproval } from 'state/transactions/hooks'
|
||||
import { ApproveTransactionInfo } from 'state/transactions/types'
|
||||
|
||||
import { PermitSignature, usePermitAllowance, useUpdatePermitAllowance } from './usePermitAllowance'
|
||||
import { useTokenAllowance, useUpdateTokenAllowance } from './useTokenAllowance'
|
||||
|
||||
enum SyncState {
|
||||
PENDING,
|
||||
SYNCING,
|
||||
SYNCED,
|
||||
}
|
||||
|
||||
export enum PermitState {
|
||||
INVALID,
|
||||
LOADING,
|
||||
@@ -19,8 +26,9 @@ export enum PermitState {
|
||||
|
||||
export interface Permit {
|
||||
state: PermitState
|
||||
isSyncing?: boolean
|
||||
signature?: PermitSignature
|
||||
callback?: (sPendingApproval: boolean) => Promise<{
|
||||
callback?: () => Promise<{
|
||||
response: ContractTransaction
|
||||
info: ApproveTransactionInfo
|
||||
} | void>
|
||||
@@ -28,7 +36,7 @@ export interface Permit {
|
||||
|
||||
export default function usePermit(amount?: CurrencyAmount<Token>, spender?: string): Permit {
|
||||
const { account } = useWeb3React()
|
||||
const tokenAllowance = useTokenAllowance(amount?.currency, account, PERMIT2_ADDRESS)
|
||||
const { tokenAllowance, isSyncing: isApprovalSyncing } = useTokenAllowance(amount?.currency, account, PERMIT2_ADDRESS)
|
||||
const updateTokenAllowance = useUpdateTokenAllowance(amount, PERMIT2_ADDRESS)
|
||||
const isAllowed = useMemo(
|
||||
() => amount && (tokenAllowance?.greaterThan(amount) || tokenAllowance?.equalTo(amount)),
|
||||
@@ -71,19 +79,38 @@ export default function usePermit(amount?: CurrencyAmount<Token>, spender?: stri
|
||||
true
|
||||
)
|
||||
|
||||
const callback = useCallback(
|
||||
async (isPendingApproval: boolean) => {
|
||||
let info
|
||||
if (!isAllowed && !isPendingApproval) {
|
||||
info = await updateTokenAllowance()
|
||||
}
|
||||
if (!isPermitted && !isSigned) {
|
||||
await updatePermitAllowance()
|
||||
}
|
||||
return info
|
||||
},
|
||||
[isAllowed, isPermitted, isSigned, updatePermitAllowance, updateTokenAllowance]
|
||||
)
|
||||
// Permit2 should be marked syncing from the time approval is submitted (pending) until it is
|
||||
// synced in tokenAllowance, to avoid re-prompting the user for an already-submitted approval.
|
||||
// It should *not* be marked syncing if not permitted, because the user must still take action.
|
||||
const [syncState, setSyncState] = useState(SyncState.SYNCED)
|
||||
const isSyncing = isPermitted || isSigned ? false : syncState !== SyncState.SYNCED
|
||||
const hasPendingApproval = useHasPendingApproval(amount?.currency, PERMIT2_ADDRESS)
|
||||
useEffect(() => {
|
||||
if (hasPendingApproval) {
|
||||
setSyncState(SyncState.PENDING)
|
||||
} else {
|
||||
setSyncState((state) => {
|
||||
if (state === SyncState.PENDING && isApprovalSyncing) {
|
||||
return SyncState.SYNCING
|
||||
} else if (state === SyncState.SYNCING && !isApprovalSyncing) {
|
||||
return SyncState.SYNCED
|
||||
} else {
|
||||
return state
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [hasPendingApproval, isApprovalSyncing])
|
||||
|
||||
const callback = useCallback(async () => {
|
||||
let info
|
||||
if (!isAllowed && !hasPendingApproval) {
|
||||
info = await updateTokenAllowance()
|
||||
}
|
||||
if (!isPermitted && !isSigned) {
|
||||
await updatePermitAllowance()
|
||||
}
|
||||
return info
|
||||
}, [hasPendingApproval, isAllowed, isPermitted, isSigned, updatePermitAllowance, updateTokenAllowance])
|
||||
|
||||
return useMemo(() => {
|
||||
if (!amount) {
|
||||
@@ -97,6 +124,6 @@ export default function usePermit(amount?: CurrencyAmount<Token>, spender?: stri
|
||||
return { state: PermitState.PERMITTED, signature }
|
||||
}
|
||||
}
|
||||
return { state: PermitState.PERMIT_NEEDED, callback }
|
||||
}, [amount, callback, isAllowed, isPermitted, isSigned, permitAllowance, signature, tokenAllowance])
|
||||
return { state: PermitState.PERMIT_NEEDED, isSyncing, callback }
|
||||
}, [amount, callback, isAllowed, isPermitted, isSigned, isSyncing, permitAllowance, signature, tokenAllowance])
|
||||
}
|
||||
|
||||
@@ -8,16 +8,23 @@ import { calculateGasMargin } from 'utils/calculateGasMargin'
|
||||
|
||||
import { useTokenContract } from './useContract'
|
||||
|
||||
export function useTokenAllowance(token?: Token, owner?: string, spender?: string): CurrencyAmount<Token> | undefined {
|
||||
export function useTokenAllowance(
|
||||
token?: Token,
|
||||
owner?: string,
|
||||
spender?: string
|
||||
): {
|
||||
tokenAllowance: CurrencyAmount<Token> | undefined
|
||||
isSyncing: boolean
|
||||
} {
|
||||
const contract = useTokenContract(token?.address, false)
|
||||
|
||||
const inputs = useMemo(() => [owner, spender], [owner, spender])
|
||||
const allowance = useSingleCallResult(contract, 'allowance', inputs).result
|
||||
const { result, syncing: isSyncing } = useSingleCallResult(contract, 'allowance', inputs)
|
||||
|
||||
return useMemo(
|
||||
() => (token && allowance ? CurrencyAmount.fromRawAmount(token, allowance.toString()) : undefined),
|
||||
[token, allowance]
|
||||
)
|
||||
return useMemo(() => {
|
||||
const tokenAllowance = token && result && CurrencyAmount.fromRawAmount(token, result.toString())
|
||||
return { tokenAllowance, isSyncing }
|
||||
}, [isSyncing, result, token])
|
||||
}
|
||||
|
||||
export function useUpdateTokenAllowance(amount: CurrencyAmount<Token> | undefined, spender: string) {
|
||||
|
||||
@@ -25,22 +25,22 @@ function useApprovalStateForSpender(
|
||||
const { account } = useWeb3React()
|
||||
const token = amountToApprove?.currency?.isToken ? amountToApprove.currency : undefined
|
||||
|
||||
const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
|
||||
const { tokenAllowance } = useTokenAllowance(token, account ?? undefined, spender)
|
||||
const pendingApproval = useIsPendingApproval(token, spender)
|
||||
|
||||
return useMemo(() => {
|
||||
if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
|
||||
if (amountToApprove.currency.isNative) return ApprovalState.APPROVED
|
||||
// we might not have enough data to know whether or not we need to approve
|
||||
if (!currentAllowance) return ApprovalState.UNKNOWN
|
||||
if (!tokenAllowance) return ApprovalState.UNKNOWN
|
||||
|
||||
// amountToApprove will be defined if currentAllowance is
|
||||
return currentAllowance.lessThan(amountToApprove)
|
||||
// amountToApprove will be defined if tokenAllowance is
|
||||
return tokenAllowance.lessThan(amountToApprove)
|
||||
? pendingApproval
|
||||
? ApprovalState.PENDING
|
||||
: ApprovalState.NOT_APPROVED
|
||||
: ApprovalState.APPROVED
|
||||
}, [amountToApprove, currentAllowance, pendingApproval, spender])
|
||||
}, [amountToApprove, pendingApproval, spender, tokenAllowance])
|
||||
}
|
||||
|
||||
export function useApproval(
|
||||
|
||||
@@ -3,6 +3,7 @@ import { BrowserEvent, ElementName, EventName, PageName } from '@uniswap/analyti
|
||||
import { BaseButton } from 'components/Button'
|
||||
import { LandingPageVariant, useLandingPageFlag } from 'featureFlags/flags/landingPage'
|
||||
import Swap from 'pages/Swap'
|
||||
import { useEffect } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Link as NativeLink } from 'react-router-dom'
|
||||
import { useIsDarkMode } from 'state/user/hooks'
|
||||
@@ -53,17 +54,17 @@ const ContentContainer = styled.div<{ isDarkMode: boolean }>`
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: min(720px, 90%);
|
||||
position: absolute;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
z-index: ${Z_INDEX.dropdown};
|
||||
padding: 32px 0;
|
||||
padding: 32px 0 80px;
|
||||
transition: ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.ease} opacity`};
|
||||
|
||||
* {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@media screen and (min-width: ${BREAKPOINTS.sm}px) {
|
||||
@media screen and (min-width: ${BREAKPOINTS.md}px) {
|
||||
padding: 64px 0;
|
||||
}
|
||||
`
|
||||
@@ -171,6 +172,18 @@ export default function Landing() {
|
||||
|
||||
const landingPageFlag = useLandingPageFlag()
|
||||
|
||||
useEffect(() => {
|
||||
if (landingPageFlag) {
|
||||
document.body.style.overflow = 'hidden'
|
||||
return () => {
|
||||
document.body.style.overflow = 'auto'
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
// need to have a return so the hook doesn't throw.
|
||||
}
|
||||
}, [landingPageFlag])
|
||||
|
||||
if (landingPageFlag === LandingPageVariant.Control || !isOpen) return null
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { sendAnalyticsEvent, Trace, TraceEvent } from '@uniswap/analytics'
|
||||
import { BrowserEvent, ElementName, EventName, PageName, SectionName } from '@uniswap/analytics-events'
|
||||
import { PERMIT2_ADDRESS } from '@uniswap/permit2-sdk'
|
||||
import { Trade } from '@uniswap/router-sdk'
|
||||
import { Currency, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core'
|
||||
import { UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk'
|
||||
@@ -28,7 +27,7 @@ import { Text } from 'rebass'
|
||||
import { useToggleWalletModal } from 'state/application/hooks'
|
||||
import { InterfaceTrade } from 'state/routing/types'
|
||||
import { TradeState } from 'state/routing/types'
|
||||
import { useHasPendingApproval, useTransactionAdder } from 'state/transactions/hooks'
|
||||
import { useTransactionAdder } from 'state/transactions/hooks'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { currencyAmountToPreciseFloat, formatTransactionAmount } from 'utils/formatNumbers'
|
||||
|
||||
@@ -300,14 +299,14 @@ export default function Swap({ className }: { className?: string }) {
|
||||
permit2Enabled ? maximumAmountIn : undefined,
|
||||
permit2Enabled && chainId ? UNIVERSAL_ROUTER_ADDRESS(chainId) : undefined
|
||||
)
|
||||
const isApprovalPending = permit.isSyncing
|
||||
const [isPermitPending, setIsPermitPending] = useState(false)
|
||||
const [isPermitFailed, setIsPermitFailed] = useState(false)
|
||||
const addTransaction = useTransactionAdder()
|
||||
const isApprovalPending = useHasPendingApproval(maximumAmountIn?.currency, PERMIT2_ADDRESS)
|
||||
const updatePermit = useCallback(async () => {
|
||||
setIsPermitPending(true)
|
||||
try {
|
||||
const approval = await permit.callback?.(isApprovalPending)
|
||||
const approval = await permit.callback?.()
|
||||
if (approval) {
|
||||
sendAnalyticsEvent(EventName.APPROVE_TOKEN_TXN_SUBMITTED, {
|
||||
chain_id: chainId,
|
||||
@@ -325,14 +324,7 @@ export default function Swap({ className }: { className?: string }) {
|
||||
} finally {
|
||||
setIsPermitPending(false)
|
||||
}
|
||||
}, [
|
||||
addTransaction,
|
||||
chainId,
|
||||
isApprovalPending,
|
||||
maximumAmountIn?.currency.address,
|
||||
maximumAmountIn?.currency.symbol,
|
||||
permit,
|
||||
])
|
||||
}, [addTransaction, chainId, maximumAmountIn?.currency.address, maximumAmountIn?.currency.symbol, permit])
|
||||
|
||||
// check whether the user has approved the router on the input token
|
||||
const [approvalState, approveCallback] = useApproveCallbackFromTrade(
|
||||
|
||||
Reference in New Issue
Block a user