ci(release): publish latest release

parent aa949db3
IPFS hash of the deployment:
- CIDv0: `QmWqSfbLDY22q6duTWFocMGoy317SVW13YLpc1yrkrHoeJ`
- CIDv1: `bafybeid6hw4k6jbgyrrwnqtzrgszxdpwotrphjy2nys4mssjakpzbsbztm`
- CIDv0: `QmSx41VP5fQ6sZTMUmtCBn74MEsfmxA1Y7X6jNw7CLMJyx`
- CIDv1: `bafybeiceqbv4zz2qxq34hgptfzhv7bbtfzif2cdqnn4hpc23vsbcbn6w7m`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
......@@ -10,15 +10,15 @@ You can also access the Uniswap Interface from an IPFS gateway.
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://bafybeid6hw4k6jbgyrrwnqtzrgszxdpwotrphjy2nys4mssjakpzbsbztm.ipfs.dweb.link/
- https://bafybeid6hw4k6jbgyrrwnqtzrgszxdpwotrphjy2nys4mssjakpzbsbztm.ipfs.cf-ipfs.com/
- [ipfs://QmWqSfbLDY22q6duTWFocMGoy317SVW13YLpc1yrkrHoeJ/](ipfs://QmWqSfbLDY22q6duTWFocMGoy317SVW13YLpc1yrkrHoeJ/)
- https://bafybeiceqbv4zz2qxq34hgptfzhv7bbtfzif2cdqnn4hpc23vsbcbn6w7m.ipfs.dweb.link/
- https://bafybeiceqbv4zz2qxq34hgptfzhv7bbtfzif2cdqnn4hpc23vsbcbn6w7m.ipfs.cf-ipfs.com/
- [ipfs://QmSx41VP5fQ6sZTMUmtCBn74MEsfmxA1Y7X6jNw7CLMJyx/](ipfs://QmSx41VP5fQ6sZTMUmtCBn74MEsfmxA1Y7X6jNw7CLMJyx/)
## 5.35.0 (2024-06-13)
### 5.35.1 (2024-06-14)
### Features
### Bug Fixes
* **web:** Add ZKsync - prod (#9083) 90c6c69
* **web:** Revert 8059 fallback to gql (token balances) - prod (#9094) 8ce2c94
web/5.35.0
\ No newline at end of file
web/5.35.1
\ No newline at end of file
import { SwapEventName } from '@uniswap/analytics-events'
import { ChainId } from '@uniswap/sdk-core'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { UNI, USDC_MAINNET } from '../../../src/constants/tokens'
import { getBalance, getTestSelector } from '../../utils'
......@@ -101,22 +100,5 @@ describe('Swap', () => {
getBalance(USDC_MAINNET).should('eq', finalBalance)
})
})
describe('multichain balances', () => {
it('shows balances for disconnected chains', () => {
cy.hardhat().then((hardhat) => {
cy.visit('/swap', {
featureFlags: [{ flag: FeatureFlags.MultichainUX, value: true }],
})
cy.get('#swap-currency-input .open-currency-select-button').click()
cy.get(getTestSelector('chain-selector')).last().click()
cy.contains('Arbitrum').click()
const sendSpy = cy.spy(hardhat.provider, 'send')
cy.wrap(sendSpy).should('not.be.calledWith', 'wallet_switchEthereumChain')
cy.get(getTestSelector('common-base-USDC')).click()
cy.get('#swap-currency-input').contains(`Balance: 0.002`)
})
})
})
})
})
......@@ -272,7 +272,7 @@ const SwapCurrencyInputPanel = forwardRef<HTMLInputElement, SwapCurrencyInputPan
const account = useAccount()
const { chainId } = useSwapAndLimitContext()
const chainAllowed = useIsSupportedChainId(chainId)
const selectedCurrencyBalance = useCurrencyBalance(account.address, currency ?? undefined, chainId)
const selectedCurrencyBalance = useCurrencyBalance(account.address, currency ?? undefined)
const theme = useTheme()
const { formatCurrencyAmount } = useFormatter()
......
......@@ -92,7 +92,7 @@ it('renders currency rows correctly with balances', () => {
isAddressSearch=""
showCurrencyAmount
balances={{
[`1-${DAI.address.toLowerCase()}`]: { usdValue: 2, balance: 2 },
[DAI.address.toLowerCase()]: { usdValue: 2, balance: 2 },
}}
/>
)
......
......@@ -15,7 +15,6 @@ import { ThemedText } from 'theme/components'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { WalletEventName } from 'uniswap/src/features/telemetry/constants'
import { shortenAddress } from 'utilities/src/addresses'
import { currencyKey } from 'utils/currencyKey'
import { NumberType, useFormatter } from 'utils/formatNumbers'
import { useIsUserAddedToken } from '../../../hooks/Tokens'
import { TokenFromList } from '../../../state/lists/tokenFromList'
......@@ -26,16 +25,16 @@ import { MouseoverTooltip, TooltipSize } from '../../Tooltip'
import { LoadingRows, MenuItem } from '../styled'
import { scrollbarStyle } from './index.css'
function currencyListRowKey(data: Currency | CurrencyListRow): string {
function currencyKey(data: Currency | CurrencyListRow): string {
if (data instanceof CurrencyListSectionTitle) {
return data.label
}
if (data instanceof CurrencyListRow) {
return currencyKey(data.currency!)
return data.currency?.isToken ? data.currency.address : 'ETHER'
}
return currencyKey(data)
return data.isToken ? data.address : 'ETHER'
}
const ROW_ITEM_SIZE = 56
......@@ -156,7 +155,7 @@ export function CurrencyRow({
showAddress?: boolean
}) {
const account = useAccount()
const key = currencyListRowKey(currency)
const key = currencyKey(currency)
const customAdded = useIsUserAddedToken(currency)
const warning = useTokenWarning(currency?.isNative ? undefined : currency?.address, currency.chainId)
const isBlockedToken = !!warning && !warning.canProceed
......@@ -327,11 +326,12 @@ export default function CurrencyList({
}
const currency: Currency = row.currency
const key = currencyKey(currency)
const balance =
tryParseCurrencyAmount(String(balances[key]?.balance ?? 0), currency) ??
CurrencyAmount.fromRawAmount(currency, 0)
tryParseCurrencyAmount(
String(balances[currency.isNative ? 'ETH' : currency.address?.toLowerCase()]?.balance ?? 0),
currency
) ?? CurrencyAmount.fromRawAmount(currency, 0)
const isSelected = Boolean(currency && selectedCurrency && selectedCurrency.equals(currency))
const otherSelected = Boolean(currency && otherCurrency && otherCurrency.equals(currency))
......@@ -375,7 +375,7 @@ export default function CurrencyList({
const itemKey = useCallback((index: number, data: typeof currencies) => {
const currency = data[index]
return currencyListRowKey(currency)
return currencyKey(currency)
}, [])
return (
......
......@@ -21,7 +21,6 @@ import { isAddress } from 'utilities/src/addresses'
import { useAccount } from 'hooks/useAccount'
import { useSwapAndLimitContext } from 'state/swap/hooks'
import { currencyKey } from 'utils/currencyKey'
import Column from '../Column'
import Row, { RowBetween } from '../Row'
import CommonBases from './CommonBases'
......@@ -225,8 +224,12 @@ export function CurrencySearch({
isAddressSearch
)}
balance={
tryParseCurrencyAmount(String(balanceMap[currencyKey(searchCurrency)]?.balance ?? 0), searchCurrency) ??
CurrencyAmount.fromRawAmount(searchCurrency, 0)
tryParseCurrencyAmount(
String(
balanceMap[searchCurrency.isNative ? 'ETH' : searchCurrency.address?.toLowerCase()]?.balance ?? 0
),
searchCurrency
) ?? CurrencyAmount.fromRawAmount(searchCurrency, 0)
}
/>
</Column>
......
......@@ -21,7 +21,6 @@ import {
useTopTokensQuery,
} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { isSameAddress } from 'utilities/src/addresses'
import { currencyKey } from 'utils/currencyKey'
interface CurrencySearchParams {
searchQuery?: string
......@@ -141,8 +140,11 @@ export function useCurrencySearchResults({
// Filter out tokens with balances so they aren't duplicated when we merge below.
const filteredListTokens = fullBaseList.filter((token) => {
const key = currencyKey(token)
return !(key in balanceMap)
if (token.isNative) {
return !((token.symbol ?? 'ETH') in balanceMap)
} else {
return !(token.address?.toLowerCase() in balanceMap)
}
})
if (balancesLoading) {
......@@ -162,13 +164,12 @@ export function useCurrencySearchResults({
// This is where we apply extra filtering based on the callsite's
// customization, on top of the basic searchQuery filtering.
const currencyFilter = (currency: Currency) => {
const key = currencyKey(currency)
if (filters?.onlyShowCurrenciesWithBalance) {
if (currency.isNative) {
return balanceMap[key]?.usdValue > 0
return balanceMap[currency.symbol ?? 'ETH']?.usdValue > 0
}
return balanceMap[key]?.usdValue > 0
return balanceMap[currency.address?.toLowerCase()]?.usdValue > 0
}
if (currency.isNative && filters?.disableNonToken) {
......@@ -180,7 +181,7 @@ export function useCurrencySearchResults({
if (selectedCurrency?.equals(currency) || otherSelectedCurrency?.equals(currency)) {
return true
}
return balanceMap[key]?.usdValue > 0
return balanceMap[currency.address.toLowerCase()]?.usdValue > 0
}
return true
......
......@@ -5,13 +5,12 @@ import { ReactNode, useState } from 'react'
import { X } from 'react-feather'
import styled from 'styled-components'
import { ThemedText } from 'theme/components'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { useTokenBalance } from 'lib/hooks/useCurrencyBalance'
import { Text } from 'ui/src'
import { logger } from 'utilities/src/logger/logger'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { UNI } from '../../constants/tokens'
import useENS from '../../hooks/useENS'
import { useTokenBalance } from '../../state/connection/hooks'
import { useDelegateCallback } from '../../state/governance/hooks'
import AddressInputPanel from '../AddressInputPanel'
import { ButtonPrimary } from '../Button'
......
import { ChainId, SOCKS_CONTROLLER_ADDRESSES, Token } from '@uniswap/sdk-core'
import { useAccount } from 'hooks/useAccount'
import { useTokenBalance } from 'lib/hooks/useCurrencyBalance'
import { useMemo } from 'react'
import { useTokenBalance } from 'state/connection/hooks'
// technically a 721, not an ERC20, but suffices for our purposes
const SOCKS = new Token(ChainId.MAINNET, SOCKS_CONTROLLER_ADDRESSES[ChainId.MAINNET], 0)
......
......@@ -67,11 +67,11 @@ describe('useTokenBalances', () => {
} as any)
const { balanceMap, loading } = renderHook(() => useTokenBalances()).result.current
expect(balanceMap).toEqual({
'1-0x123': {
'0x123': {
balance: 123,
usdValue: 123,
},
'1-native': {
ETH: {
balance: 123,
usdValue: 123,
},
......
import { useTokenBalancesQuery } from 'graphql/data/apollo/TokenBalancesProvider'
import { supportedChainIdFromGQLChain } from 'graphql/data/util'
import { useAccount } from 'hooks/useAccount'
import { TokenBalances } from 'lib/hooks/useTokenList/sorting'
import { useMemo } from 'react'
import { PortfolioTokenBalancePartsFragment } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { currencyKeyFromGraphQL } from 'utils/currencyKey'
/**
* Returns the user's token balances via graphql as a map and list.
*/
export function useTokenBalances({ cacheOnly }: { cacheOnly?: boolean } = {}): {
export function useTokenBalances(): {
balanceMap: TokenBalances
balanceList: readonly (PortfolioTokenBalancePartsFragment | undefined)[]
loading: boolean
} {
const { data, loading } = useTokenBalancesQuery({ cacheOnly })
const { chainId } = useAccount()
const { data, loading } = useTokenBalancesQuery()
return useMemo(() => {
const balanceList = data?.portfolios?.[0]?.tokenBalances ?? []
const balanceMap =
balanceList?.reduce((balanceMap, tokenBalance) => {
if (!tokenBalance?.token) {
return balanceMap
}
const key = currencyKeyFromGraphQL({
address: tokenBalance.token.address,
chain: tokenBalance.token.chain,
standard: tokenBalance.token.standard,
})
if (tokenBalance.denominatedValue?.value !== undefined) {
const usdValue = tokenBalance.denominatedValue?.value
const balance = tokenBalance.quantity
balanceMap[key] = { usdValue, balance: balance ?? 0 }
const address =
tokenBalance?.token?.standard === 'ERC20'
? tokenBalance.token?.address?.toLowerCase()
: tokenBalance?.token?.symbol ?? 'ETH'
if (
tokenBalance?.token?.chain &&
supportedChainIdFromGQLChain(tokenBalance.token?.chain) === chainId &&
address
) {
const usdValue = tokenBalance.denominatedValue?.value ?? 0
const balance = tokenBalance.quantity ?? 0
balanceMap[address] = { usdValue, balance }
}
return balanceMap
}, {} as TokenBalances) ?? {}
return { balanceMap, balanceList, loading }
}, [data?.portfolios, loading])
}, [chainId, data?.portfolios, loading])
}
import { Interface } from '@ethersproject/abi'
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
import { useMemo } from 'react'
import { useTokenBalances } from 'hooks/useTokenBalances'
import { currencyKey } from 'utils/currencyKey'
import { Interface } from '@ethersproject/abi'
import { useMultipleContractSingleData, useSingleContractMultipleData } from 'lib/hooks/multicall'
import { useMemo } from 'react'
import ERC20ABI from 'uniswap/src/abis/erc20.json'
import { Erc20Interface } from 'uniswap/src/abis/types/Erc20'
......@@ -18,7 +14,7 @@ import { useInterfaceMulticall } from '../../hooks/useContract'
/**
* Returns a map of the given addresses to their eventually consistent ETH balances.
*/
function useNativeCurrencyBalances(uncheckedAddresses?: (string | undefined)[]): {
export function useNativeCurrencyBalances(uncheckedAddresses?: (string | undefined)[]): {
[address: string]: CurrencyAmount<Currency> | undefined
} {
const { chainId } = useAccount()
......@@ -57,18 +53,14 @@ const tokenBalancesGasRequirement = { gasRequired: 185_000 }
/**
* Returns a map of token addresses to their eventually consistent token balances for a single account.
*/
export function useRpcTokenBalancesWithLoadingIndicator(
export function useTokenBalancesWithLoadingIndicator(
address?: string,
tokens?: (Token | undefined)[],
skip?: boolean
tokens?: (Token | undefined)[]
): [{ [tokenAddress: string]: CurrencyAmount<Token> | undefined }, boolean] {
const { chainId } = useAccount()
const validatedTokens: Token[] = useMemo(
() =>
skip
? []
: tokens?.filter((t?: Token): t is Token => isAddress(t?.address) !== false && t?.chainId === chainId) ?? [],
[chainId, tokens, skip]
() => tokens?.filter((t?: Token): t is Token => isAddress(t?.address) !== false && t?.chainId === chainId) ?? [],
[chainId, tokens]
)
const validatedTokenAddresses = useMemo(() => validatedTokens.map((vt) => vt.address), [validatedTokens])
......@@ -100,14 +92,30 @@ export function useRpcTokenBalancesWithLoadingIndicator(
)
}
function useRpcTokenBalances(
export function useTokenBalances(
address?: string,
tokens?: (Token | undefined)[]
): { [tokenAddress: string]: CurrencyAmount<Token> | undefined } {
return useRpcTokenBalancesWithLoadingIndicator(address, tokens)[0]
return useTokenBalancesWithLoadingIndicator(address, tokens)[0]
}
// get the balance for a single token/account combo
export function useTokenBalance(account?: string, token?: Token): CurrencyAmount<Token> | undefined {
const tokenBalances = useTokenBalances(
account,
useMemo(() => [token], [token])
)
if (!token) {
return undefined
}
return tokenBalances[token.address]
}
function useRpcCurrencyBalances(
/**
* Returns balances for tokens on currently-connected chainId via RPC.
* See useTokenBalancesQuery for multichain portfolio balances via GQL.
*/
export function useCurrencyBalances(
account?: string,
currencies?: (Currency | undefined)[]
): (CurrencyAmount<Currency> | undefined)[] {
......@@ -117,7 +125,7 @@ function useRpcCurrencyBalances(
)
const { chainId } = useAccount()
const tokenBalances = useRpcTokenBalances(account, tokens)
const tokenBalances = useTokenBalances(account, tokens)
const containsETH: boolean = useMemo(() => currencies?.some((currency) => currency?.isNative) ?? false, [currencies])
const ethBalance = useNativeCurrencyBalances(useMemo(() => (containsETH ? [account] : []), [containsETH, account]))
......@@ -139,77 +147,12 @@ function useRpcCurrencyBalances(
)
}
/**
* Returns raw balances as CurrencyAmounts for tokens in users balanceMap via graphql.
* Balances from graphql are used as a fallback when user is not connected to chain.
* Currently they're returned from graphql formatted so we need to convert to the base unit.
*/
function useGqlCurrencyBalances(
account?: string,
currencies?: (Currency | undefined)[]
): (CurrencyAmount<Currency> | undefined)[] {
const { balanceMap } = useTokenBalances({ cacheOnly: true })
return useMemo(() => {
if (!account || !currencies) {
return []
}
return currencies.map((currency) => {
if (!currency) {
return undefined
}
const key = currencyKey(currency)
const balance = balanceMap[key]
if (balance) {
const balanceQuantityRaw = JSBI.BigInt(Math.floor(balance.balance * Math.pow(10, currency.decimals ?? 18)))
return CurrencyAmount.fromRawAmount(currency, balanceQuantityRaw)
} else {
return CurrencyAmount.fromRawAmount(currency, 0)
}
})
}, [account, balanceMap, currencies])
}
/**
* Returns balances for tokens on currently-connected chainId via RPC.
* Falls back to graphql TokenBalances if user is not connected to chain, a.k.a !isSynced.
*/
export function useCurrencyBalances(
account?: string,
currencies?: (Currency | undefined)[],
chainId?: number
): (CurrencyAmount<Currency> | undefined)[] {
const { chainId: providerChainId } = useAccount()
const isSynced = chainId === providerChainId
const gqlCurrencyBalances = useGqlCurrencyBalances(account, currencies)
const rpcCurrencyBalances = useRpcCurrencyBalances(account, currencies)
return useMemo(() => {
if (!account || !currencies) {
return []
}
return isSynced ? rpcCurrencyBalances : gqlCurrencyBalances
}, [account, currencies, isSynced, gqlCurrencyBalances, rpcCurrencyBalances])
}
// get the balance for a single token/account combo
export function useTokenBalance(account?: string, token?: Token): CurrencyAmount<Token> | undefined {
return useCurrencyBalance(account, token) as CurrencyAmount<Token> | undefined
}
export default function useCurrencyBalance(
account?: string,
currency?: Currency,
chainId?: number
currency?: Currency
): CurrencyAmount<Currency> | undefined {
return useCurrencyBalances(
account,
useMemo(() => [currency], [currency]),
chainId
useMemo(() => [currency], [currency])
)[0]
}
......@@ -2,7 +2,6 @@ import { ChainId, Token } from '@uniswap/sdk-core'
import { nativeOnChain } from 'constants/tokens'
import { supportedChainIdFromGQLChain } from 'graphql/data/util'
import { PortfolioTokenBalancePartsFragment } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { currencyKey } from 'utils/currencyKey'
import { SplitOptions, splitHiddenTokens } from 'utils/splitHiddenTokens'
/** Sorts currency amounts (descending). */
......@@ -21,8 +20,8 @@ export type TokenBalances = { [tokenAddress: string]: { usdValue: number; balanc
/** Sorts tokens by currency amount (descending), then safety, then symbol (ascending). */
function tokenComparator(balances: TokenBalances, a: Token, b: Token) {
const aAddress = currencyKey(a)
const bAddress = currencyKey(b)
const aAddress = a.isNative ? 'ETH' : a.address?.toLowerCase()
const bAddress = b.isNative ? 'ETH' : b.address?.toLowerCase()
// Sorts by balances
const balanceComparison = balanceComparator(balances[aAddress]?.usdValue, balances[bAddress]?.usdValue)
if (balanceComparison !== 0) {
......
import { BigNumber } from '@ethersproject/bignumber'
import { parseEther } from '@ethersproject/units'
import { ChainId, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import { UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk'
import { getURAddress, useNftUniversalRouterAddress } from 'graphql/data/nft/NftUniversalRouterAddress'
import { useAccount } from 'hooks/useAccount'
import usePermit2Allowance, { AllowanceState } from 'hooks/usePermit2Allowance'
import useCurrencyBalance, { useTokenBalance } from 'lib/hooks/useCurrencyBalance'
import { useBag } from 'nft/hooks'
import { useTokenBalance } from 'lib/hooks/useCurrencyBalance'
import { useBag, useWalletBalance } from 'nft/hooks'
import { useBagTotalEthPrice } from 'nft/hooks/useBagTotalEthPrice'
import useDerivedPayWithAnyTokenSwapInfo from 'nft/hooks/useDerivedPayWithAnyTokenSwapInfo'
import usePayWithAnyTokenSwap from 'nft/hooks/usePayWithAnyTokenSwap'
......@@ -17,7 +18,6 @@ import { TEST_TOKEN_1, TEST_TRADE_EXACT_INPUT, USE_CONNECTED_ACCOUNT, toCurrency
import { mocked } from 'test-utils/mocked'
import { render, screen } from 'test-utils/render'
import { nativeOnChain } from 'constants/tokens'
import { BagFooter } from './BagFooter'
jest.mock('hooks/useAccount', () => ({
......@@ -25,6 +25,7 @@ jest.mock('hooks/useAccount', () => ({
}))
jest.mock('nft/hooks/useBagTotalEthPrice')
jest.mock('nft/hooks/useWalletBalance')
jest.mock('nft/hooks/useBag')
jest.mock('nft/hooks/useTokenInput')
jest.mock('lib/hooks/useCurrencyBalance')
......@@ -59,7 +60,12 @@ describe('BagFooter.tsx', () => {
itemsInBag: [],
}) as ReturnType<typeof useBag>
mocked(useBagTotalEthPrice).mockReturnValue(BigNumber.from(12))
mocked(useCurrencyBalance).mockReturnValue(CurrencyAmount.fromRawAmount(nativeOnChain(ChainId.MAINNET), 100))
mocked(useWalletBalance).mockReturnValue({
address: '',
balance: '100',
weiBalance: parseEther('0'),
provider: undefined,
})
mocked(usePermit2Allowance).mockReturnValue({
state: AllowanceState.ALLOWED,
......@@ -126,7 +132,12 @@ describe('BagFooter.tsx', () => {
})
it('insufficient balance', () => {
mocked(useCurrencyBalance).mockReturnValue(CurrencyAmount.fromRawAmount(nativeOnChain(ChainId.MAINNET), 0))
mocked(useWalletBalance).mockReturnValue({
address: '',
balance: '0',
weiBalance: parseEther('0'),
provider: undefined,
})
renderBagFooter()
const buyButton = getBuyButton()
......
import { BigNumber } from '@ethersproject/bignumber'
import { formatEther } from '@ethersproject/units'
import { formatEther, parseEther } from '@ethersproject/units'
import { InterfaceElementName, NFTEventName } from '@uniswap/analytics-events'
import { ChainId, Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks'
......@@ -19,7 +19,7 @@ import usePermit2Allowance, { AllowanceState } from 'hooks/usePermit2Allowance'
import { useStablecoinValue } from 'hooks/useStablecoinPrice'
import { useSwitchChain } from 'hooks/useSwitchChain'
import { Trans, t } from 'i18n'
import useCurrencyBalance, { useTokenBalance } from 'lib/hooks/useCurrencyBalance'
import { useTokenBalance } from 'lib/hooks/useCurrencyBalance'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useBag } from 'nft/hooks/useBag'
import { useBagTotalEthPrice } from 'nft/hooks/useBagTotalEthPrice'
......@@ -29,6 +29,7 @@ import usePayWithAnyTokenSwap from 'nft/hooks/usePayWithAnyTokenSwap'
import { PriceImpact, usePriceImpact } from 'nft/hooks/usePriceImpact'
import { useSubscribeTransactionState } from 'nft/hooks/useSubscribeTransactionState'
import { useTokenInput } from 'nft/hooks/useTokenInput'
import { useWalletBalance } from 'nft/hooks/useWalletBalance'
import { BagStatus } from 'nft/types'
import { PropsWithChildren, useEffect, useMemo, useState } from 'react'
import { AlertTriangle, ChevronDown } from 'react-feather'
......@@ -38,9 +39,6 @@ import { ThemedText } from 'theme/components'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { NumberType, useFormatter } from 'utils/formatNumbers'
import { NATIVE_CHAIN_ID } from 'constants/tokens'
import JSBI from 'jsbi'
import { BuyButtonStateData, BuyButtonStates, getBuyButtonStateData } from './ButtonStates'
const FooterContainer = styled.div`
......@@ -332,9 +330,7 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) =
const fiatValueTradeOutput = useStablecoinValue(parsedOutputAmount)
const usdcValue = usingPayWithAnyToken ? fiatValueTradeInput : fiatValueTradeOutput
const nativeCurrency = useCurrency(NATIVE_CHAIN_ID)
const nativeCurencyBalance = useCurrencyBalance(account.address ?? undefined, nativeCurrency)
const { balance: balanceInEth } = useWalletBalance()
const sufficientBalance = useMemo(() => {
if (!connected || account.chainId !== ChainId.MAINNET) {
return undefined
......@@ -350,22 +346,8 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) =
return !inputCurrencyBalance.lessThan(inputAmount)
}
if (!nativeCurrency) {
return undefined
}
const totalEthPriceCurrencyAmount = CurrencyAmount.fromRawAmount(nativeCurrency, JSBI.BigInt(totalEthPrice))
return nativeCurencyBalance?.greaterThan(totalEthPriceCurrencyAmount)
}, [
connected,
account.chainId,
inputCurrency,
nativeCurrency,
totalEthPrice,
nativeCurencyBalance,
trade?.inputAmount,
inputCurrencyBalance,
])
return parseEther(balanceInEth).gte(totalEthPrice)
}, [connected, account.chainId, inputCurrency, balanceInEth, totalEthPrice, trade?.inputAmount, inputCurrencyBalance])
useEffect(() => {
setBagStatus(BagStatus.ADDING_TO_BAG)
......
......@@ -8,7 +8,7 @@ import { Column, Row } from 'nft/components/Flex'
import { CrossIcon } from 'nft/components/icons'
import { FilterSidebar } from 'nft/components/profile/view/FilterSidebar'
import { subhead } from 'nft/css/common.css'
import { useBag, useFiltersExpanded, useSellAsset, useWalletCollections } from 'nft/hooks'
import { useBag, useFiltersExpanded, useSellAsset, useWalletBalance, useWalletCollections } from 'nft/hooks'
import { ScreenBreakpointsPaddings } from 'nft/pages/collection/index.css'
import { WalletCollection } from 'nft/types'
import { Dispatch, SetStateAction, Suspense, useCallback, useEffect, useMemo, useState } from 'react'
......@@ -18,7 +18,6 @@ import styled from 'styled-components'
import { useInfiniteQuery } from '@tanstack/react-query'
import { useIsMobile } from 'hooks/screenSize'
import { useAccount } from 'hooks/useAccount'
import { getOSCollectionsInfiniteQueryOptions } from 'nft/queries/openSea/OSCollectionsFetcher'
import { EmptyWalletModule } from './EmptyWalletContent'
import * as styles from './ProfilePage.css'
......@@ -54,7 +53,7 @@ const FILTER_SIDEBAR_WIDTH = 300
const PADDING = 16
export const ProfilePage = () => {
const account = useAccount()
const { address } = useWalletBalance()
const walletCollections = useWalletCollections((state) => state.walletCollections)
const setWalletCollections = useWalletCollections((state) => state.setWalletCollections)
const { resetSellAssets } = useSellAsset(({ reset }) => ({
......@@ -71,7 +70,7 @@ export const ProfilePage = () => {
hasNextPage,
isFetchingNextPage,
isSuccess,
} = useInfiniteQuery(getOSCollectionsInfiniteQueryOptions(account.address ?? ''))
} = useInfiniteQuery(getOSCollectionsInfiniteQueryOptions(address))
const ownerCollections = useMemo(
() => (isSuccess ? ownerCollectionsData?.pages.map((page) => page.data).flat() : null),
......@@ -163,7 +162,7 @@ const ProfilePageNfts = ({
isFiltersExpanded: boolean
setFiltersExpanded: (filtersExpanded: boolean) => void
}) => {
const account = useAccount()
const { address } = useWalletBalance()
const setCollectionFilters = useWalletCollections((state) => state.setCollectionFilters)
const collectionFilters = useWalletCollections((state) => state.collectionFilters)
const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters)
......@@ -177,7 +176,7 @@ const ProfilePageNfts = ({
loading,
hasNext,
loadMore,
} = useNftBalance(account.address ?? '', collectionFilters, [], DEFAULT_WALLET_ASSET_QUERY_AMOUNT)
} = useNftBalance(address, collectionFilters, [], DEFAULT_WALLET_ASSET_QUERY_AMOUNT)
const { gridX } = useSpring({
gridX: isFiltersExpanded ? FILTER_SIDEBAR_WIDTH : -PADDING,
......
......@@ -16,4 +16,5 @@ export { useSendTransaction } from './useSendTransaction'
export { useSubscribeScrollState } from './useSubscribeScrollState'
export { useTransactionResponse } from './useTransactionResponse'
export { useNativeUsdPrice, useUsdPriceofNftAsset } from './useUsdPrice'
export { useWalletBalance } from './useWalletBalance'
export { useWalletCollections } from './useWalletCollections'
import { BigNumber } from '@ethersproject/bignumber'
import type { Web3Provider } from '@ethersproject/providers'
import { parseEther } from '@ethersproject/units'
import { useWeb3React } from '@web3-react/core'
import { useAccount } from 'hooks/useAccount'
import { useNativeCurrencyBalances } from 'state/connection/hooks'
interface WalletBalanceProps {
address: string
balance: string
weiBalance: BigNumber
provider?: Web3Provider
}
export function useWalletBalance(): WalletBalanceProps {
const account = useAccount()
const { provider } = useWeb3React()
const balanceString =
useNativeCurrencyBalances(account.address ? [account.address] : [])?.[account.address ?? '']?.toSignificant(3) ||
'0'
return account.address
? {
address: account.address,
balance: balanceString,
weiBalance: parseEther(balanceString),
provider,
}
: {
address: '',
balance: '0',
weiBalance: parseEther('0'),
provider: undefined,
}
}
......@@ -15,13 +15,12 @@ import { ReactNode, useMemo } from 'react'
import { Text } from 'rebass'
import styled, { useTheme } from 'styled-components'
import { BackArrowLink, StyledInternalLink, ThemedText } from 'theme/components'
import { useRpcTokenBalancesWithLoadingIndicator } from 'lib/hooks/useCurrencyBalance'
import { LightCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column'
import QuestionHelper from '../../components/QuestionHelper'
import { AutoRow } from '../../components/Row'
import { Dots } from '../../components/swap/styled'
import { useTokenBalancesWithLoadingIndicator } from '../../state/connection/hooks'
import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks'
export const MigrateHeader = styled(ThemedText.H1Small)`
......@@ -90,10 +89,7 @@ export default function MigrateV2() {
}, [tokenPairsWithLiquidityTokens])
// fetch pair balances
const [pairBalances, fetchingPairBalances] = useRpcTokenBalancesWithLoadingIndicator(
account.address,
allLiquidityTokens
)
const [pairBalances, fetchingPairBalances] = useTokenBalancesWithLoadingIndicator(account.address, allLiquidityTokens)
// filter for v2 liquidity tokens that the user has a balance in
const tokenPairsWithV2Balance = useMemo(() => {
......
......@@ -3,7 +3,6 @@ import { Pair } from '@uniswap/v2-sdk'
import { V2Unsupported } from 'components/V2Unsupported'
import { useAccount } from 'hooks/useAccount'
import { useNetworkSupportsV2 } from 'hooks/useNetworkSupportsV2'
import { useTokenBalances } from 'hooks/useTokenBalances'
import { Trans } from 'i18n'
import JSBI from 'jsbi'
import { PoolVersionMenu } from 'pages/Pool/shared'
......@@ -15,7 +14,6 @@ import styled, { useTheme } from 'styled-components'
import { ExternalLink, HideSmall, ThemedText } from 'theme/components'
import { ProtocolVersion } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { currencyKey } from 'utils/currencyKey'
import { ButtonOutlined, ButtonPrimary, ButtonSecondary } from '../../components/Button'
import Card from '../../components/Card'
import { AutoColumn } from '../../components/Column'
......@@ -26,6 +24,7 @@ import { CardBGImage, CardNoise, CardSection, DataCard } from '../../components/
import { Dots } from '../../components/swap/styled'
import { BIG_INT_ZERO } from '../../constants/misc'
import { useV2Pairs } from '../../hooks/useV2Pairs'
import { useTokenBalancesWithLoadingIndicator } from '../../state/connection/hooks'
import { useStakingInfo } from '../../state/stake/hooks'
import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks'
......@@ -100,16 +99,22 @@ export default function Pool() {
() => trackedTokenPairs.map((tokens) => ({ liquidityToken: toV2LiquidityToken(tokens), tokens })),
[trackedTokenPairs]
)
const { balanceMap, loading: fetchingV2PairBalances } = useTokenBalances()
const liquidityTokens = useMemo(
() => tokenPairsWithLiquidityTokens.map((tpwlt) => tpwlt.liquidityToken),
[tokenPairsWithLiquidityTokens]
)
const [v2PairsBalances, fetchingV2PairBalances] = useTokenBalancesWithLoadingIndicator(
account.address,
liquidityTokens
)
// fetch the reserves for all V2 pools in which the user has a balance
const liquidityTokensWithBalances = useMemo(
() =>
tokenPairsWithLiquidityTokens.filter(({ liquidityToken }) => {
const liquidityTokenKey = currencyKey(liquidityToken)
return balanceMap[liquidityTokenKey]?.balance > 0
}),
[tokenPairsWithLiquidityTokens, balanceMap]
tokenPairsWithLiquidityTokens.filter(({ liquidityToken }) =>
v2PairsBalances[liquidityToken.address]?.greaterThan('0')
),
[tokenPairsWithLiquidityTokens, v2PairsBalances]
)
const v2Pairs = useV2Pairs(liquidityTokensWithBalances.map(({ tokens }) => tokens))
......
......@@ -60,7 +60,6 @@ import { maxAmountSpend } from 'utils/maxAmountSpend'
import { largerPercentValue } from 'utils/percent'
import { computeRealizedPriceImpact, warningSeverity } from 'utils/prices'
import { didUserReject } from 'utils/swapErrorToUserReadableMessage'
import { getIsReviewableQuote } from '.'
import { OutputTaxTooltipBody } from './TaxTooltipBody'
......@@ -240,12 +239,10 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF
swapError: undefined,
swapResult: undefined,
})
const previousConnectedChainId = usePrevious(connectedChainId)
const previousPrefilledState = usePrevious(prefilledState)
useEffect(() => {
if (multichainUXEnabled) {
return
}
const chainChanged = previousConnectedChainId && previousConnectedChainId !== connectedChainId
const prefilledInputChanged =
previousPrefilledState?.inputCurrency &&
......@@ -265,7 +262,6 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF
}
}, [
connectedChainId,
multichainUXEnabled,
prefilledState.inputCurrency,
prefilledState?.outputCurrency,
previousConnectedChainId,
......@@ -464,7 +460,6 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF
const inputCurrency = currencies[Field.INPUT] ?? undefined
const selectChain = useSelectChain()
const switchingChain = useAppSelector((state) => state.wallets.switchingChain)
const targetChain = switchingChain ? switchingChain : undefined
const switchingChainIsSupported = useIsSupportedChainId(targetChain)
......
......@@ -106,7 +106,7 @@ export function Swap({
{/* TODO: Move SwapContextProvider inside Swap tab ONLY after SwapHeader removes references to trade / autoSlippage */}
<SwapAndLimitContext.Consumer>
{({ currentTab }) => (
<SwapContextProvider multichainUXEnabled={multichainUXEnabled}>
<SwapContextProvider>
<Flex width="100%">
<SwapWrapper isDark={isDark} className={className} id="swap-page">
<SwapHeader compact={compact || !screenSize.sm} syncTabToUrl={syncTabToUrl} />
......
......@@ -8,9 +8,9 @@ import { useAppDispatch, useAppSelector } from 'state/hooks'
import { AppState } from 'state/reducer'
import { useAccount } from 'hooks/useAccount'
import useCurrencyBalance from 'lib/hooks/useCurrencyBalance'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useV2Pair } from '../../hooks/useV2Pairs'
import { useTokenBalances } from '../connection/hooks'
import { Field, typeInput } from './actions'
export function useBurnState(): AppState['burn'] {
......@@ -38,7 +38,8 @@ export function useDerivedBurnInfo(
const [, pair] = useV2Pair(currencyA, currencyB)
// balances
const userLiquidity = useCurrencyBalance(account.address, pair?.liquidityToken) as CurrencyAmount<Token>
const relevantTokenBalances = useTokenBalances(account.address, [pair?.liquidityToken])
const userLiquidity: undefined | CurrencyAmount<Token> = relevantTokenBalances?.[pair?.liquidityToken?.address ?? '']
const [tokenA, tokenB] = [currencyA?.wrapped, currencyB?.wrapped]
const tokens = {
......
export { default as useCurrencyBalance, useCurrencyBalances, useTokenBalance } from 'lib/hooks/useCurrencyBalance'
export {
default as useCurrencyBalance,
useCurrencyBalances,
useNativeCurrencyBalances,
useTokenBalance,
useTokenBalances,
useTokenBalancesWithLoadingIndicator,
} from 'lib/hooks/useCurrencyBalance'
......@@ -12,7 +12,6 @@ import { useCurrencyBalances } from 'lib/hooks/useCurrencyBalance'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useMemo } from 'react'
import { SendState } from 'state/send/SendContext'
import { useSwapAndLimitContext } from 'state/swap/hooks'
import { useUnitagByAddress, useUnitagByName } from 'uniswap/src/features/unitags/hooks'
import { isAddress } from 'utilities/src/addresses'
import { useCreateTransferTransaction } from 'utils/transfer'
......@@ -42,7 +41,6 @@ export type SendInfo = {
export function useDerivedSendInfo(state: SendState): SendInfo {
const account = useAccount()
const { provider } = useWeb3React()
const { chainId } = useSwapAndLimitContext()
const { exactAmountToken, exactAmountFiat, inputInFiat, inputCurrency, recipient, validatedRecipientData } = state
const { unitag: recipientInputUnitag } = useUnitagByName(validatedRecipientData ? undefined : recipient)
......@@ -80,7 +78,7 @@ export function useDerivedSendInfo(state: SendState): SendInfo {
unitag?.username,
])
const nativeCurrency = useCurrency(NATIVE_CHAIN_ID, chainId)
const nativeCurrency = useCurrency(NATIVE_CHAIN_ID, account.chainId)
const [inputCurrencyBalance, nativeCurencyBalance] = useCurrencyBalances(
account.address,
useMemo(() => [inputCurrency, nativeCurrency], [inputCurrency, nativeCurrency])
......@@ -94,21 +92,21 @@ export function useDerivedSendInfo(state: SendState): SendInfo {
const transferInfo = useMemo(() => {
return {
provider,
account: account.address,
chainId,
address: account.address,
chaindId: account.chainId,
currencyAmount: parsedTokenAmount,
toAddress: recipientData?.address,
}
}, [account.address, chainId, parsedTokenAmount, provider, recipientData?.address])
}, [account.address, account.chainId, parsedTokenAmount, provider, recipientData?.address])
const transferTransaction = useCreateTransferTransaction(transferInfo)
const gasFee = useTransactionGasFee(transferTransaction, GasSpeed.Normal, !transferTransaction)
const gasFeeCurrencyAmount = useMemo(() => {
if (!chainId || !gasFee?.value) {
if (!account.chainId || !gasFee?.value) {
return undefined
}
return CurrencyAmount.fromRawAmount(nativeOnChain(chainId), gasFee.value)
}, [chainId, gasFee?.value])
return CurrencyAmount.fromRawAmount(nativeOnChain(account.chainId), gasFee.value)
}, [account.chainId, gasFee?.value])
const inputError = useMemo(() => {
const insufficientBalance = parsedTokenAmount && inputCurrencyBalance?.lessThan(parsedTokenAmount)
......
......@@ -3,7 +3,6 @@ import { useAccount } from 'hooks/useAccount'
import usePrevious from 'hooks/usePrevious'
import { PropsWithChildren, useEffect, useMemo, useState } from 'react'
import { SwapTab } from 'uniswap/src/types/screens/interface'
import { useDerivedSwapInfo } from './hooks'
import { CurrencyState, SwapAndLimitContext, SwapContext, SwapState, initialSwapState } from './types'
......@@ -37,25 +36,6 @@ export function SwapAndLimitContextProvider({
const account = useAccount()
const previousConnectedChainId = usePrevious(account.chainId)
const previousInitialInputCurrency = usePrevious(initialInputCurrency)
const previousInitialOutputCurrency = usePrevious(initialOutputCurrency)
useEffect(() => {
if (!multichainUXEnabled) {
return
}
if (previousInitialInputCurrency && previousInitialInputCurrency !== initialInputCurrency) {
setCurrencyState((prev) => ({ ...prev, inputCurrency: initialInputCurrency }))
}
}, [
multichainUXEnabled,
initialInputCurrency,
initialOutputCurrency,
previousInitialInputCurrency,
previousInitialOutputCurrency,
])
const previousPrefilledState = usePrevious(prefilledState)
useEffect(() => {
......@@ -112,13 +92,7 @@ export function SwapAndLimitContextProvider({
return <SwapAndLimitContext.Provider value={value}>{children}</SwapAndLimitContext.Provider>
}
export function SwapContextProvider({
multichainUXEnabled,
children,
}: {
multichainUXEnabled?: boolean
children: React.ReactNode
}) {
export function SwapContextProvider({ children }: { children: React.ReactNode }) {
const [swapState, setSwapState] = useState<SwapState>({
...initialSwapState,
})
......@@ -129,13 +103,10 @@ export function SwapContextProvider({
useEffect(() => {
const chainChanged = previousConnectedChainId && previousConnectedChainId !== connectedChainId
if (multichainUXEnabled) {
return
}
if (chainChanged) {
setSwapState((prev) => ({ ...prev, typedValue: '' }))
}
}, [connectedChainId, previousConnectedChainId, multichainUXEnabled])
}, [connectedChainId, previousConnectedChainId])
return <SwapContext.Provider value={{ swapState, setSwapState, derivedSwapInfo }}>{children}</SwapContext.Provider>
}
......@@ -127,9 +127,8 @@ export function useSwapActionHandlers(): {
// from the current swap inputs, compute the best trade and return it.
export function useDerivedSwapInfo(state: SwapState): SwapInfo {
const account = useAccount()
const { chainId } = useSwapAndLimitContext()
const nativeCurrency = useNativeCurrency(chainId)
const balance = useCurrencyBalance(account.address, nativeCurrency, chainId)
const nativeCurrency = useNativeCurrency(account.chainId)
const balance = useCurrencyBalance(account.address, nativeCurrency)
const {
currencyState: { inputCurrency, outputCurrency },
......@@ -143,8 +142,7 @@ export function useDerivedSwapInfo(state: SwapState): SwapInfo {
const relevantTokenBalances = useCurrencyBalances(
account.address,
useMemo(() => [inputCurrency ?? undefined, outputCurrency ?? undefined], [inputCurrency, outputCurrency]),
chainId
useMemo(() => [inputCurrency ?? undefined, outputCurrency ?? undefined], [inputCurrency, outputCurrency])
)
const isExactIn: boolean = independentField === Field.INPUT
......
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