Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b9a23d920 | ||
|
|
120ad935fa | ||
|
|
4eaf16b624 | ||
|
|
857e2915ab | ||
|
|
7410c81b42 | ||
|
|
fb05439d32 | ||
|
|
fb7eade70b | ||
|
|
bd2b2c487a | ||
|
|
2f004ed1d9 | ||
|
|
db257c73f2 | ||
|
|
7c37b9d00e | ||
|
|
7688c527f0 |
19
.eslintrc.js
19
.eslintrc.js
@@ -4,4 +4,23 @@ require('@uniswap/eslint-config/load')
|
||||
|
||||
module.exports = {
|
||||
extends: '@uniswap/eslint-config/react',
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
rules: {
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: [
|
||||
{
|
||||
name: 'zustand',
|
||||
importNames: ['default'],
|
||||
message: 'Default import from zustand is deprecated. Import `{ create }` instead.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -42,6 +42,8 @@ jobs:
|
||||
needs: tag
|
||||
if: ${{ needs.tag.outputs.new_tag != null }}
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
|
||||
@@ -14,9 +14,8 @@ coverage:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 5%
|
||||
threshold: 1%
|
||||
if_ci_failed: error
|
||||
patch:
|
||||
default:
|
||||
enabled: no
|
||||
if_not_found: success
|
||||
target: 80%
|
||||
@@ -53,4 +53,11 @@ describe('Testing nfts', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('nft-view-self-nfts')).click()
|
||||
})
|
||||
|
||||
it('should close the sidebar when navigating to NFT details', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('mini-portfolio-nav-nfts')).click()
|
||||
cy.get(getTestSelector('mini-portfolio-nft')).first().click()
|
||||
cy.contains('Buy crypto').should('not.be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -132,8 +132,8 @@
|
||||
"@reach/dialog": "^0.10.3",
|
||||
"@reach/portal": "^0.10.3",
|
||||
"@reduxjs/toolkit": "^1.6.1",
|
||||
"@sentry/react": "^7.40.0",
|
||||
"@sentry/tracing": "^7.40.0",
|
||||
"@sentry/react": "^7.45.0",
|
||||
"@sentry/tracing": "^7.45.0",
|
||||
"@types/react-window-infinite-loader": "^1.0.6",
|
||||
"@uniswap/analytics": "^1.3.1",
|
||||
"@uniswap/analytics-events": "^2.8.0",
|
||||
|
||||
@@ -60,7 +60,7 @@ const Socks = () => {
|
||||
const MiniWalletIcon = ({ connection, side }: { connection: Connection; side: 'left' | 'right' }) => {
|
||||
return (
|
||||
<MiniIconContainer side={side}>
|
||||
<MiniImg src={connection.icon} alt={`${connection.name} icon`} />
|
||||
<MiniImg src={connection.getIcon?.()} alt={`${connection.getName()} icon`} />
|
||||
</MiniIconContainer>
|
||||
)
|
||||
}
|
||||
@@ -71,7 +71,7 @@ const MainWalletIcon = ({ connection, size }: { connection: Connection; size: nu
|
||||
|
||||
if (!account) {
|
||||
return null
|
||||
} else if (avatar || (connection.type === ConnectionType.INJECTED && connection.name === 'MetaMask')) {
|
||||
} else if (avatar || (connection.type === ConnectionType.INJECTED && connection.getName() === 'MetaMask')) {
|
||||
return <Identicon size={size} />
|
||||
} else {
|
||||
return <Unicon address={account} size={size} />
|
||||
|
||||
@@ -1,38 +1,91 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { render, screen } from 'test-utils'
|
||||
import { SupportedChainId, Token, WETH9 } from '@uniswap/sdk-core'
|
||||
import { FeeAmount, Pool } from '@uniswap/v3-sdk'
|
||||
import { USDC_MAINNET } from 'constants/tokens'
|
||||
import { useToken } from 'hooks/Tokens'
|
||||
import { usePool } from 'hooks/usePools'
|
||||
import { PoolState } from 'hooks/usePools'
|
||||
import { render } from 'test-utils'
|
||||
import { unwrappedToken } from 'utils/unwrappedToken'
|
||||
|
||||
import PositionListItem from '.'
|
||||
|
||||
jest.mock('hooks/Tokens', () => {
|
||||
const originalModule = jest.requireActual('hooks/Tokens')
|
||||
const uniSDK = jest.requireActual('@uniswap/sdk-core')
|
||||
jest.mock('utils/unwrappedToken')
|
||||
const mockUnwrappedToken = unwrappedToken as jest.MockedFunction<typeof unwrappedToken>
|
||||
|
||||
jest.mock('hooks/usePools')
|
||||
const mockUsePool = usePool as jest.MockedFunction<typeof usePool>
|
||||
|
||||
jest.mock('hooks/Tokens')
|
||||
const mockUseToken = useToken as jest.MockedFunction<typeof useToken>
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
jest.mock('components/DoubleLogo', () => () => <div />)
|
||||
|
||||
jest.mock('@web3-react/core', () => {
|
||||
const web3React = jest.requireActual('@web3-react/core')
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
useToken: jest.fn(
|
||||
() =>
|
||||
new uniSDK.Token(
|
||||
1,
|
||||
'0x39AA39c021dfbaE8faC545936693aC917d5E7563',
|
||||
8,
|
||||
'https://www.example.com',
|
||||
'example.com coin'
|
||||
)
|
||||
),
|
||||
...web3React,
|
||||
useWeb3React: () => ({
|
||||
chainId: 1,
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
test('PositionListItem should not render when the name contains a url', () => {
|
||||
const susToken0Address = '0x39AA39c021dfbaE8faC545936693aC917d5E7563'
|
||||
|
||||
beforeEach(() => {
|
||||
const susToken0 = new Token(1, susToken0Address, 8, 'https://www.example.com', 'example.com coin')
|
||||
mockUseToken.mockImplementation((tokenAddress?: string | null | undefined) => {
|
||||
if (!tokenAddress) return null
|
||||
if (tokenAddress === susToken0.address) return susToken0
|
||||
return new Token(1, tokenAddress, 8, 'symbol', 'name')
|
||||
})
|
||||
mockUsePool.mockReturnValue([
|
||||
PoolState.EXISTS,
|
||||
new Pool(susToken0, USDC_MAINNET, FeeAmount.HIGH, '2437312313659959819381354528', '10272714736694327408', -69633),
|
||||
])
|
||||
mockUnwrappedToken.mockReturnValue(susToken0)
|
||||
})
|
||||
|
||||
test('PositionListItem should not render when token0 symbol contains a url', () => {
|
||||
const positionDetails = {
|
||||
token0: '0x39AA39c021dfbaE8faC545936693aC917d5E7563',
|
||||
token1: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
||||
token0: susToken0Address,
|
||||
token1: USDC_MAINNET.address,
|
||||
tokenId: BigNumber.from(436148),
|
||||
fee: 100,
|
||||
liquidity: BigNumber.from('0x5c985aff8059be04'),
|
||||
tickLower: -800,
|
||||
tickUpper: 1600,
|
||||
}
|
||||
render(<PositionListItem {...positionDetails} />)
|
||||
screen.debug()
|
||||
expect(screen.queryByText('.com', { exact: false })).toBe(null)
|
||||
const { container } = render(<PositionListItem {...positionDetails} />)
|
||||
expect(container).toBeEmptyDOMElement()
|
||||
})
|
||||
|
||||
test('PositionListItem should not render when token1 symbol contains a url', () => {
|
||||
const positionDetails = {
|
||||
token0: USDC_MAINNET.address,
|
||||
token1: susToken0Address,
|
||||
tokenId: BigNumber.from(436148),
|
||||
fee: 100,
|
||||
liquidity: BigNumber.from('0x5c985aff8059be04'),
|
||||
tickLower: -800,
|
||||
tickUpper: 1600,
|
||||
}
|
||||
const { container } = render(<PositionListItem {...positionDetails} />)
|
||||
expect(container).toBeEmptyDOMElement()
|
||||
})
|
||||
|
||||
test('PositionListItem should render a position', () => {
|
||||
const positionDetails = {
|
||||
token0: USDC_MAINNET.address,
|
||||
token1: WETH9[SupportedChainId.MAINNET].address,
|
||||
tokenId: BigNumber.from(436148),
|
||||
fee: 100,
|
||||
liquidity: BigNumber.from('0x5c985aff8059be04'),
|
||||
tickLower: -800,
|
||||
tickUpper: 1600,
|
||||
}
|
||||
const { container } = render(<PositionListItem {...positionDetails} />)
|
||||
expect(container).not.toBeEmptyDOMElement()
|
||||
})
|
||||
|
||||
@@ -203,12 +203,9 @@ export default function PositionListItem({
|
||||
|
||||
const removed = liquidity?.eq(0)
|
||||
|
||||
const containsURL = useMemo(
|
||||
() => [token0?.name, token0?.symbol, token1?.name, token1?.symbol].some((testString) => hasURL(testString)),
|
||||
[token0?.name, token0?.symbol, token1?.name, token1?.symbol]
|
||||
)
|
||||
const shouldHidePosition = hasURL(token0?.symbol) || hasURL(token1?.symbol)
|
||||
|
||||
if (containsURL) {
|
||||
if (shouldHidePosition) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import Row from 'components/Row'
|
||||
import { useToggleWalletDrawer } from 'components/WalletDropdown'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import { NftCard } from 'nft/components/card'
|
||||
import { detailsHref } from 'nft/components/card/utils'
|
||||
import { VerifiedIcon } from 'nft/components/icons'
|
||||
import { WalletAsset } from 'nft/types'
|
||||
import { floorFormatter } from 'nft/utils'
|
||||
@@ -50,8 +51,8 @@ export function NFT({
|
||||
const trace = useTrace()
|
||||
|
||||
const navigateToNFTDetails = () => {
|
||||
navigate(`/nfts/asset/${asset.asset_contract.address}/${asset.tokenId}`)
|
||||
toggleWalletDrawer()
|
||||
navigate(detailsHref(asset))
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -62,10 +63,7 @@ export function NFT({
|
||||
display={{ disabledInfo: true }}
|
||||
isSelected={false}
|
||||
isDisabled={false}
|
||||
selectAsset={navigateToNFTDetails}
|
||||
unselectAsset={() => {
|
||||
/* */
|
||||
}}
|
||||
onCardClick={navigateToNFTDetails}
|
||||
sendAnalyticsEvent={() =>
|
||||
sendAnalyticsEvent(SharedEventName.ELEMENT_CLICKED, {
|
||||
element: InterfaceElementName.MINI_PORTFOLIO_NFT_ITEM,
|
||||
@@ -77,6 +75,7 @@ export function NFT({
|
||||
}
|
||||
mediaShouldBePlaying={mediaShouldBePlaying}
|
||||
setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia}
|
||||
testId="mini-portfolio-nft"
|
||||
/>
|
||||
<NFTDetails asset={asset} />
|
||||
</NFTContainer>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { SupportedChainId, WETH9 } from '@uniswap/sdk-core'
|
||||
import { FeeAmount, Pool, Position } from '@uniswap/v3-sdk'
|
||||
import { USDC_MAINNET } from 'constants/tokens'
|
||||
import { render } from 'test-utils'
|
||||
|
||||
import Pools from '.'
|
||||
import useMultiChainPositions from './useMultiChainPositions'
|
||||
|
||||
jest.mock('./useMultiChainPositions')
|
||||
const mockUseMultiChainPositions = useMultiChainPositions as jest.MockedFunction<typeof useMultiChainPositions>
|
||||
|
||||
const owner = '0xf5b6bb25f5beaea03dd014c6ef9fa9f3926bf36c'
|
||||
|
||||
const pool = new Pool(
|
||||
USDC_MAINNET,
|
||||
WETH9[SupportedChainId.MAINNET],
|
||||
FeeAmount.MEDIUM,
|
||||
'1851127709498178402383049949138810',
|
||||
'7076437181775065414',
|
||||
201189
|
||||
)
|
||||
|
||||
const position = new Position({
|
||||
pool,
|
||||
liquidity: 1341008833950736,
|
||||
tickLower: 200040,
|
||||
tickUpper: 202560,
|
||||
})
|
||||
const details = {
|
||||
nonce: BigNumber.from('0'),
|
||||
tokenId: BigNumber.from('0'),
|
||||
operator: '0x0',
|
||||
token0: USDC_MAINNET.address,
|
||||
token1: WETH9[SupportedChainId.MAINNET].address,
|
||||
fee: FeeAmount.MEDIUM,
|
||||
tickLower: -100,
|
||||
tickUpper: 100,
|
||||
liquidity: BigNumber.from('9000'),
|
||||
feeGrowthInside0LastX128: BigNumber.from('0'),
|
||||
feeGrowthInside1LastX128: BigNumber.from('0'),
|
||||
tokensOwed0: BigNumber.from('0'),
|
||||
tokensOwed1: BigNumber.from('0'),
|
||||
}
|
||||
const useMultiChainPositionsReturnValue = {
|
||||
positions: [
|
||||
{
|
||||
owner,
|
||||
chainId: SupportedChainId.MAINNET,
|
||||
position,
|
||||
pool,
|
||||
details,
|
||||
inRange: true,
|
||||
closed: false,
|
||||
},
|
||||
],
|
||||
loading: false,
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
mockUseMultiChainPositions.mockReturnValue(useMultiChainPositionsReturnValue)
|
||||
})
|
||||
test('Pools should render LP positions', () => {
|
||||
const props = { account: owner }
|
||||
const { container } = render(<Pools {...props} />)
|
||||
expect(container).not.toBeEmptyDOMElement()
|
||||
})
|
||||
@@ -111,15 +111,9 @@ function PositionListItem({ positionInfo }: { positionInfo: PositionInfo }) {
|
||||
[chainId, pool.token0.address, pool.token0.symbol, pool.token1.address, pool.token1.symbol]
|
||||
)
|
||||
|
||||
const containsURL = useMemo(
|
||||
() =>
|
||||
[pool.token0.name, pool.token0.symbol, pool.token1.name, pool.token1.symbol].some((testString) =>
|
||||
hasURL(testString)
|
||||
),
|
||||
[pool]
|
||||
)
|
||||
const shouldHidePosition = hasURL(pool.token0.symbol) || hasURL(pool.token1.symbol)
|
||||
|
||||
if (containsURL) {
|
||||
if (shouldHidePosition) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { SupportedChainId } from '@uniswap/sdk-core'
|
||||
import { DAI_ARBITRUM } from '@uniswap/smart-order-router'
|
||||
import { DAI, USDC_ARBITRUM, USDC_MAINNET } from 'constants/tokens'
|
||||
import { render } from 'test-utils'
|
||||
|
||||
import { PortfolioLogo } from './PortfolioLogo'
|
||||
|
||||
describe('PortfolioLogo', () => {
|
||||
it('renders without L2 icon', () => {
|
||||
const { container } = render(<PortfolioLogo chainId={SupportedChainId.MAINNET} currencies={[DAI, USDC_MAINNET]} />)
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('renders with L2 icon', () => {
|
||||
const { container } = render(
|
||||
<PortfolioLogo chainId={SupportedChainId.ARBITRUM_ONE} currencies={[DAI_ARBITRUM, USDC_ARBITRUM]} />
|
||||
)
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
@@ -9,7 +9,7 @@ import useTokenLogoSource from 'hooks/useAssetLogoSource'
|
||||
import useENSAvatar from 'hooks/useENSAvatar'
|
||||
import React from 'react'
|
||||
import { Loader } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
const UnknownContract = styled(UnknownStatus)`
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
`
|
||||
@@ -57,34 +57,28 @@ const ENSAvatarImg = styled.img`
|
||||
width: 40px;
|
||||
`
|
||||
|
||||
const StyledChainLogo = styled.img<{ isSquare: boolean }>`
|
||||
height: ${({ isSquare }) => (isSquare ? '16px' : '14px')};
|
||||
width: ${({ isSquare }) => (isSquare ? '16px' : '14px')};
|
||||
margin-top: ${({ isSquare }) => (isSquare ? '0px' : '1px')};
|
||||
margin-left: ${({ isSquare }) => (isSquare ? '0px' : '1px')};
|
||||
position: absolute;
|
||||
top: 68%;
|
||||
left: 68%;
|
||||
const StyledChainLogo = styled.img`
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
`
|
||||
|
||||
const ChainLogoSquareBackground = styled.div`
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
border-radius: 4px;
|
||||
background-color: ${({ theme }) => theme.backgroundSurface};
|
||||
const SquareChainLogo = styled.img`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const L2LogoContainer = styled.div<{ $backgroundColor?: string }>`
|
||||
background-color: ${({ $backgroundColor }) => $backgroundColor};
|
||||
border-radius: 2px;
|
||||
height: 16px;
|
||||
left: 60%;
|
||||
position: absolute;
|
||||
top: 60%;
|
||||
left: 60%;
|
||||
`
|
||||
|
||||
const SquareBackgroundForNonSquareLogo = styled.div`
|
||||
height: 16px;
|
||||
outline: 2px solid ${({ theme }) => theme.backgroundSurface};
|
||||
width: 16px;
|
||||
border-radius: 2px;
|
||||
background-color: ${({ theme }) => theme.textPrimary};
|
||||
position: absolute;
|
||||
top: 68%;
|
||||
left: 68%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`
|
||||
|
||||
/**
|
||||
@@ -101,6 +95,7 @@ export function PortfolioLogo({
|
||||
const { squareLogoUrl, logoUrl } = getChainInfo(chainId)
|
||||
const chainLogo = squareLogoUrl ?? logoUrl
|
||||
const { avatar, loading } = useENSAvatar(accountAddress, false)
|
||||
const theme = useTheme()
|
||||
|
||||
const [src, nextSrc] = useTokenLogoSource(currencies?.[0]?.wrapped.address, chainId, currencies?.[0]?.isNative)
|
||||
const [src2, nextSrc2] = useTokenLogoSource(currencies?.[1]?.wrapped.address, chainId, currencies?.[1]?.isNative)
|
||||
@@ -147,13 +142,15 @@ export function PortfolioLogo({
|
||||
}
|
||||
|
||||
const L2Logo =
|
||||
chainId === SupportedChainId.MAINNET ? null : (
|
||||
<div>
|
||||
{chainLogo && <ChainLogoSquareBackground />}
|
||||
{!squareLogoUrl && logoUrl && <SquareBackgroundForNonSquareLogo />}
|
||||
{chainLogo && <StyledChainLogo isSquare={!!squareLogoUrl} src={chainLogo} alt="chainLogo" />}
|
||||
</div>
|
||||
)
|
||||
chainId !== SupportedChainId.MAINNET && chainLogo ? (
|
||||
<L2LogoContainer $backgroundColor={squareLogoUrl ? theme.backgroundSurface : theme.textPrimary}>
|
||||
{squareLogoUrl ? (
|
||||
<SquareChainLogo src={chainLogo} alt="chainLogo" />
|
||||
) : (
|
||||
<StyledChainLogo src={chainLogo} alt="chainLogo" />
|
||||
)}
|
||||
</L2LogoContainer>
|
||||
) : null
|
||||
|
||||
return (
|
||||
<StyledLogoParentContainer>
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`PortfolioLogo renders with L2 icon 1`] = `
|
||||
.c3 {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: row;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
gap: 2px;
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.c1 .c2:nth-child(n) {
|
||||
width: 19px;
|
||||
height: 40px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.c1 .c2:nth-child(1) {
|
||||
border-radius: 20px 0 0 20px;
|
||||
object-position: 0 0;
|
||||
}
|
||||
|
||||
.c1 .c2:nth-child(2) {
|
||||
border-radius: 0 20px 20px 0;
|
||||
object-position: 100% 0;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.c5 {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
.c4 {
|
||||
background-color: #0D111C;
|
||||
border-radius: 2px;
|
||||
height: 16px;
|
||||
left: 60%;
|
||||
position: absolute;
|
||||
top: 60%;
|
||||
outline: 2px solid #FFFFFF;
|
||||
width: 16px;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
<div>
|
||||
<div
|
||||
class="c0"
|
||||
>
|
||||
<div
|
||||
class="c1"
|
||||
>
|
||||
<img
|
||||
class="c2 c3"
|
||||
src="https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/arbitrum/assets/0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1/logo.png"
|
||||
/>
|
||||
<img
|
||||
class="c2 c3"
|
||||
src="https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/arbitrum/assets/0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8/logo.png"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="c4"
|
||||
>
|
||||
<img
|
||||
alt="chainLogo"
|
||||
class="c5"
|
||||
src="arbitrum_logo.svg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PortfolioLogo renders without L2 icon 1`] = `
|
||||
.c3 {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: row;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
gap: 2px;
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.c1 .c2:nth-child(n) {
|
||||
width: 19px;
|
||||
height: 40px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.c1 .c2:nth-child(1) {
|
||||
border-radius: 20px 0 0 20px;
|
||||
object-position: 0 0;
|
||||
}
|
||||
|
||||
.c1 .c2:nth-child(2) {
|
||||
border-radius: 0 20px 20px 0;
|
||||
object-position: 100% 0;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
<div>
|
||||
<div
|
||||
class="c0"
|
||||
>
|
||||
<div
|
||||
class="c1"
|
||||
>
|
||||
<img
|
||||
class="c2 c3"
|
||||
src="https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png"
|
||||
/>
|
||||
<img
|
||||
class="c2 c3"
|
||||
src="https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -55,6 +55,7 @@ const PageWrapper = styled.div`
|
||||
|
||||
interface Page {
|
||||
title: React.ReactNode
|
||||
key: string
|
||||
component: ({ account }: { account: string }) => JSX.Element
|
||||
loggingElementName: string
|
||||
}
|
||||
@@ -62,13 +63,25 @@ interface Page {
|
||||
const Pages: Array<Page> = [
|
||||
{
|
||||
title: <Trans>Tokens</Trans>,
|
||||
key: 'tokens',
|
||||
component: Tokens,
|
||||
loggingElementName: InterfaceElementName.MINI_PORTFOLIO_TOKENS_TAB,
|
||||
},
|
||||
{ title: <Trans>NFTs</Trans>, component: NFTs, loggingElementName: InterfaceElementName.MINI_PORTFOLIO_NFT_TAB },
|
||||
{ title: <Trans>Pools</Trans>, component: Pools, loggingElementName: InterfaceElementName.MINI_PORTFOLIO_POOLS_TAB },
|
||||
{
|
||||
title: <Trans>NFTs</Trans>,
|
||||
key: 'nfts',
|
||||
component: NFTs,
|
||||
loggingElementName: InterfaceElementName.MINI_PORTFOLIO_NFT_TAB,
|
||||
},
|
||||
{
|
||||
title: <Trans>Pools</Trans>,
|
||||
key: 'pools',
|
||||
component: Pools,
|
||||
loggingElementName: InterfaceElementName.MINI_PORTFOLIO_POOLS_TAB,
|
||||
},
|
||||
{
|
||||
title: <Trans>Activity</Trans>,
|
||||
key: 'activity',
|
||||
component: ActivityTab,
|
||||
loggingElementName: InterfaceElementName.MINI_PORTFOLIO_ACTIVITY_TAB,
|
||||
},
|
||||
@@ -83,7 +96,7 @@ function MiniPortfolio({ account }: { account: string }) {
|
||||
return (
|
||||
<Wrapper>
|
||||
<Nav>
|
||||
{Pages.map(({ title, loggingElementName }, index) => {
|
||||
{Pages.map(({ title, loggingElementName, key }, index) => {
|
||||
if (shouldDisableNFTRoutes && loggingElementName.includes('nft')) return null
|
||||
return (
|
||||
<TraceEvent
|
||||
@@ -93,6 +106,7 @@ function MiniPortfolio({ account }: { account: string }) {
|
||||
key={index}
|
||||
>
|
||||
<NavItem
|
||||
data-testid={`mini-portfolio-nav-${key}`}
|
||||
onClick={() => setCurrentPage(index)}
|
||||
active={currentPage === index}
|
||||
key={`Mini Portfolio page ${index}`}
|
||||
|
||||
@@ -72,7 +72,7 @@ export default function Option({ connection, pendingConnectionType, activate }:
|
||||
<TraceEvent
|
||||
events={[BrowserEvent.onClick]}
|
||||
name={InterfaceEventName.WALLET_SELECTED}
|
||||
properties={{ wallet_type: connection.name }}
|
||||
properties={{ wallet_type: connection.getName() }}
|
||||
element={InterfaceElementName.WALLET_TYPE_OPTION}
|
||||
>
|
||||
<OptionCardClickable
|
||||
@@ -83,9 +83,9 @@ export default function Option({ connection, pendingConnectionType, activate }:
|
||||
>
|
||||
<OptionCardLeft>
|
||||
<IconWrapper>
|
||||
<img src={connection.icon} alt="Icon" />
|
||||
<img src={connection.getIcon?.()} alt="Icon" />
|
||||
</IconWrapper>
|
||||
<HeaderText>{connection.name}</HeaderText>
|
||||
<HeaderText>{connection.getName()}</HeaderText>
|
||||
{connection.isNew && <NewBadge />}
|
||||
</OptionCardLeft>
|
||||
{isPending && <Loader />}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AutoColumn } from 'components/Column'
|
||||
import { AutoRow } from 'components/Row'
|
||||
import { useWalletDrawer } from 'components/WalletDropdown'
|
||||
import IconButton from 'components/WalletDropdown/IconButton'
|
||||
import { Connection, ConnectionType, networkConnection, useConnections } from 'connection'
|
||||
import { Connection, ConnectionType, getConnections, networkConnection } from 'connection'
|
||||
import { useGetConnection } from 'connection'
|
||||
import { ErrorCode } from 'connection/utils'
|
||||
import { isSupportedChain } from 'constants/chains'
|
||||
@@ -91,7 +91,7 @@ export default function WalletModal({ openSettings }: { openSettings: () => void
|
||||
const [pendingConnection, setPendingConnection] = useState<Connection | undefined>()
|
||||
const [pendingError, setPendingError] = useState<any>()
|
||||
|
||||
const connections = useConnections()
|
||||
const connections = getConnections()
|
||||
const getConnection = useGetConnection()
|
||||
|
||||
useEffect(() => {
|
||||
@@ -116,7 +116,7 @@ export default function WalletModal({ openSettings }: { openSettings: () => void
|
||||
// When new wallet is successfully set by the user, trigger logging of Amplitude analytics event.
|
||||
useEffect(() => {
|
||||
if (account && account !== lastActiveWalletAddress) {
|
||||
const walletName = getConnection(connector).name
|
||||
const walletName = getConnection(connector).getName()
|
||||
const peerWalletAgent = provider ? getWalletMeta(provider)?.agent : undefined
|
||||
const isReconnect =
|
||||
connectedWallets.filter((wallet) => wallet.account === account && wallet.walletType === walletName).length > 0
|
||||
@@ -141,6 +141,9 @@ export default function WalletModal({ openSettings }: { openSettings: () => void
|
||||
|
||||
const tryActivation = useCallback(
|
||||
async (connection: Connection) => {
|
||||
// Skips wallet connection if the connection should override the default behavior, i.e. install metamask or launch coinbase app
|
||||
if (connection.overrideActivate?.()) return
|
||||
|
||||
// log selected wallet
|
||||
sendEvent({
|
||||
category: 'Wallet',
|
||||
@@ -165,7 +168,7 @@ export default function WalletModal({ openSettings }: { openSettings: () => void
|
||||
|
||||
sendAnalyticsEvent(InterfaceEventName.WALLET_CONNECT_TXN_COMPLETED, {
|
||||
result: WalletConnectionResult.FAILED,
|
||||
wallet_type: connection.name,
|
||||
wallet_type: connection.getName(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -190,11 +193,11 @@ export default function WalletModal({ openSettings }: { openSettings: () => void
|
||||
<OptionGrid data-testid="option-grid">
|
||||
{connections.map((connection) =>
|
||||
// Hides Uniswap Wallet if mgtm is disabled
|
||||
connection.shouldDisplay && !(connection.type === ConnectionType.UNIWALLET && !mgtmEnabled) ? (
|
||||
connection.shouldDisplay() && !(connection.type === ConnectionType.UNIWALLET && !mgtmEnabled) ? (
|
||||
<Option
|
||||
key={connection.name}
|
||||
key={connection.getName()}
|
||||
connection={connection}
|
||||
activate={connection.overrideActivate ?? (() => tryActivation(connection))}
|
||||
activate={() => tryActivation(connection)}
|
||||
pendingConnectionType={pendingConnection?.type}
|
||||
/>
|
||||
) : null
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function Web3Provider({ children }: { children: ReactNode }) {
|
||||
const connections = useOrderedConnections()
|
||||
const connectors: [Connector, Web3ReactHooks][] = connections.map(({ hooks, connector }) => [connector, hooks])
|
||||
|
||||
const key = useMemo(() => connections.map((connection) => connection.name).join('-'), [connections])
|
||||
const key = useMemo(() => connections.map((connection) => connection.getName()).join('-'), [connections])
|
||||
|
||||
return (
|
||||
<Web3ReactProvider connectors={connectors} key={key}>
|
||||
|
||||
@@ -1,111 +1,145 @@
|
||||
// eslint-disable-next-line jest/no-export
|
||||
export {}
|
||||
import { ConnectionType, getConnections, useGetConnection } from 'connection'
|
||||
import { renderHook } from 'test-utils'
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules()
|
||||
jest.resetAllMocks()
|
||||
})
|
||||
|
||||
it('Non-injected Desktop', async () => {
|
||||
jest.mock('connection/utils', () => ({ isInjected: false, isMetaMaskWallet: false, isCoinbaseWallet: false }))
|
||||
jest.mock('utils/userAgent', () => ({ isMobile: false }))
|
||||
const connection = await import('connection')
|
||||
expect(connection.darkInjectedConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.darkInjectedConnection.name).toBe('MetaMask')
|
||||
expect(connection.darkInjectedConnection.overrideActivate).toBeDefined()
|
||||
expect(connection.coinbaseWalletConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.uniwalletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.walletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.getConnections(true).filter((c) => c.shouldDisplay).length).toEqual(4)
|
||||
})
|
||||
const UserAgentMock = jest.requireMock('utils/userAgent')
|
||||
jest.mock('utils/userAgent', () => ({
|
||||
isMobile: false,
|
||||
}))
|
||||
|
||||
it('MetaMask Injected Desktop', async () => {
|
||||
jest.mock('connection/utils', () => ({ isInjected: true, isMetaMaskWallet: true, isCoinbaseWallet: false }))
|
||||
jest.mock('utils/userAgent', () => ({ isMobile: false }))
|
||||
const connection = await import('connection')
|
||||
expect(connection.darkInjectedConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.darkInjectedConnection.name).toBe('MetaMask')
|
||||
expect(connection.darkInjectedConnection.overrideActivate).toBeUndefined()
|
||||
expect(connection.coinbaseWalletConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.uniwalletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.walletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.getConnections(true).filter((c) => c.shouldDisplay).length).toEqual(4)
|
||||
})
|
||||
describe('connection utility/metadata tests', () => {
|
||||
const createWalletEnvironment = (ethereum: Window['window']['ethereum'], isMobile = false) => {
|
||||
UserAgentMock.isMobile = isMobile
|
||||
global.window.ethereum = ethereum
|
||||
|
||||
it('Coinbase Injected Desktop', async () => {
|
||||
jest.mock('connection/utils', () => ({ isInjected: true, isMetaMaskWallet: false, isCoinbaseWallet: true }))
|
||||
jest.mock('utils/userAgent', () => ({ isMobile: false }))
|
||||
const connection = await import('connection')
|
||||
expect(connection.darkInjectedConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.darkInjectedConnection.name).toBe('MetaMask')
|
||||
expect(connection.darkInjectedConnection.overrideActivate).toBeDefined()
|
||||
expect(connection.coinbaseWalletConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.uniwalletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.walletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.getConnections(true).filter((c) => c.shouldDisplay).length).toEqual(4)
|
||||
})
|
||||
const displayed = getConnections().filter((c) => c.shouldDisplay())
|
||||
const getConnection = renderHook(() => useGetConnection()).result.current
|
||||
const injected = getConnection(ConnectionType.INJECTED)
|
||||
const coinbase = getConnection(ConnectionType.COINBASE_WALLET)
|
||||
const uniswap = getConnection(ConnectionType.UNIWALLET)
|
||||
const walletconnect = getConnection(ConnectionType.WALLET_CONNECT)
|
||||
|
||||
it('Coinbase and MetaMask Injected Desktop', async () => {
|
||||
jest.mock('connection/utils', () => ({ isInjected: true, isMetaMaskWallet: true, isCoinbaseWallet: true }))
|
||||
jest.mock('utils/userAgent', () => ({ isMobile: false }))
|
||||
const connection = await import('connection')
|
||||
expect(connection.darkInjectedConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.darkInjectedConnection.name).toBe('MetaMask')
|
||||
expect(connection.darkInjectedConnection.overrideActivate).toBeUndefined()
|
||||
expect(connection.coinbaseWalletConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.uniwalletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.walletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.getConnections(true).filter((c) => c.shouldDisplay).length).toEqual(4)
|
||||
})
|
||||
return { displayed, injected, coinbase, uniswap, walletconnect }
|
||||
}
|
||||
|
||||
it('Generic Injected Desktop', async () => {
|
||||
jest.mock('connection/utils', () => ({ isInjected: true, isMetaMaskWallet: false, isCoinbaseWallet: false }))
|
||||
jest.mock('utils/userAgent', () => ({ isMobile: false }))
|
||||
const connection = await import('connection')
|
||||
expect(connection.darkInjectedConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.darkInjectedConnection.name).toBe('Browser Wallet')
|
||||
expect(connection.darkInjectedConnection.overrideActivate).toBeUndefined()
|
||||
expect(connection.coinbaseWalletConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.uniwalletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.walletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.getConnections(true).filter((c) => c.shouldDisplay).length).toEqual(4)
|
||||
})
|
||||
it('Non-injected Desktop', async () => {
|
||||
const { displayed, injected } = createWalletEnvironment(undefined)
|
||||
|
||||
it('Generic Injected Mobile Browser', async () => {
|
||||
jest.mock('connection/utils', () => ({ isInjected: true, isMetaMaskWallet: false, isCoinbaseWallet: false }))
|
||||
jest.mock('utils/userAgent', () => ({ isMobile: true }))
|
||||
const connection = await import('connection')
|
||||
expect(connection.darkInjectedConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.darkInjectedConnection.name).toBe('Browser Wallet')
|
||||
})
|
||||
expect(displayed.includes(injected)).toBe(true)
|
||||
expect(injected.getName()).toBe('MetaMask')
|
||||
expect(injected.overrideActivate?.()).toBeTruthy()
|
||||
|
||||
it('MetaMask Mobile Browser', async () => {
|
||||
jest.mock('connection/utils', () => ({ isInjected: true, isMetaMaskWallet: true, isCoinbaseWallet: false }))
|
||||
jest.mock('utils/userAgent', () => ({ isMobile: true }))
|
||||
const connection = await import('connection')
|
||||
expect(connection.darkInjectedConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.darkInjectedConnection.name).toBe('MetaMask')
|
||||
expect(connection.getConnections(true).filter((c) => c.shouldDisplay).length).toEqual(1)
|
||||
})
|
||||
expect(displayed.length).toEqual(4)
|
||||
})
|
||||
|
||||
it('Coinbase Mobile Browser', async () => {
|
||||
jest.mock('connection/utils', () => ({ isInjected: true, isMetaMaskWallet: false, isCoinbaseWallet: true }))
|
||||
jest.mock('utils/userAgent', () => ({ isMobile: true }))
|
||||
const connection = await import('connection')
|
||||
it('MetaMask-Injected Desktop', async () => {
|
||||
const { displayed, injected } = createWalletEnvironment({ isMetaMask: true })
|
||||
|
||||
expect(connection.coinbaseWalletConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.coinbaseWalletConnection.overrideActivate).toBeUndefined()
|
||||
expect(connection.getConnections(true).filter((c) => c.shouldDisplay).length).toEqual(1)
|
||||
})
|
||||
expect(displayed.includes(injected)).toBe(true)
|
||||
expect(injected.getName()).toBe('MetaMask')
|
||||
expect(injected.overrideActivate?.()).toBeFalsy()
|
||||
|
||||
it('mWeb Browser', async () => {
|
||||
jest.mock('connection/utils', () => ({ isInjected: false, isMetaMaskWallet: false, isCoinbaseWallet: false }))
|
||||
jest.mock('utils/userAgent', () => ({ isMobile: true }))
|
||||
const connection = await import('connection')
|
||||
expect(connection.darkInjectedConnection.shouldDisplay).toBe(false)
|
||||
expect(connection.coinbaseWalletConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.coinbaseWalletConnection.overrideActivate).toBeDefined()
|
||||
expect(connection.uniwalletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.walletConnectConnection.shouldDisplay).toBe(true)
|
||||
expect(connection.getConnections(true).filter((c) => c.shouldDisplay).length).toEqual(3)
|
||||
expect(displayed.length).toEqual(4)
|
||||
})
|
||||
|
||||
it('Coinbase-Injected Desktop', async () => {
|
||||
const { displayed, injected, coinbase } = createWalletEnvironment({ isCoinbaseWallet: true })
|
||||
|
||||
expect(displayed.includes(coinbase)).toBe(true)
|
||||
expect(displayed.includes(injected)).toBe(true)
|
||||
expect(injected.getName()).toBe('MetaMask')
|
||||
expect(injected.overrideActivate?.()).toBeTruthy()
|
||||
|
||||
expect(displayed.length).toEqual(4)
|
||||
})
|
||||
|
||||
it('Coinbase and MetaMask Injected Desktop', async () => {
|
||||
const { displayed, injected, coinbase } = createWalletEnvironment({ isCoinbaseWallet: true, isMetaMask: true })
|
||||
|
||||
expect(displayed.includes(coinbase)).toBe(true)
|
||||
expect(displayed.includes(injected)).toBe(true)
|
||||
expect(injected.getName()).toBe('MetaMask')
|
||||
expect(injected.overrideActivate?.()).toBeFalsy()
|
||||
|
||||
expect(displayed.length).toEqual(4)
|
||||
})
|
||||
|
||||
it('Generic Injected Desktop', async () => {
|
||||
const { displayed, injected } = createWalletEnvironment({ isTrustWallet: true })
|
||||
|
||||
expect(displayed.includes(injected)).toBe(true)
|
||||
expect(injected.getName()).toBe('Browser Wallet')
|
||||
expect(injected.overrideActivate?.()).toBeFalsy()
|
||||
|
||||
expect(displayed.length).toEqual(4)
|
||||
})
|
||||
|
||||
it('Generic Browser Wallet that injects as MetaMask', async () => {
|
||||
const { displayed, injected } = createWalletEnvironment({ isRabby: true, isMetaMask: true })
|
||||
|
||||
expect(displayed.includes(injected)).toBe(true)
|
||||
expect(injected.getName()).toBe('Browser Wallet')
|
||||
expect(injected.overrideActivate?.()).toBeFalsy()
|
||||
|
||||
expect(displayed.length).toEqual(4)
|
||||
})
|
||||
|
||||
it('Generic Wallet Browser with delayed injection', async () => {
|
||||
const { injected } = createWalletEnvironment(undefined)
|
||||
|
||||
expect(injected.getName()).toBe('MetaMask')
|
||||
expect(injected.overrideActivate?.()).toBeTruthy()
|
||||
|
||||
createWalletEnvironment({ isTrustWallet: true })
|
||||
|
||||
expect(injected.getName()).toBe('Browser Wallet')
|
||||
expect(injected.overrideActivate?.()).toBeFalsy()
|
||||
})
|
||||
|
||||
const UNKNOWN_INJECTOR = { isRandomWallet: true } as Window['window']['ethereum']
|
||||
it('Generic Unknown Injected Wallet Browser', async () => {
|
||||
const { displayed, injected } = createWalletEnvironment(UNKNOWN_INJECTOR, true)
|
||||
|
||||
expect(displayed.includes(injected)).toBe(true)
|
||||
expect(injected.getName()).toBe('Browser Wallet')
|
||||
expect(injected.overrideActivate?.()).toBeFalsy()
|
||||
|
||||
// Ensures we provide multiple connection options if in an unknown injected browser
|
||||
expect(displayed.length).toEqual(4)
|
||||
})
|
||||
|
||||
it('MetaMask Mobile Browser', async () => {
|
||||
const { displayed, injected } = createWalletEnvironment({ isMetaMask: true }, true)
|
||||
|
||||
expect(displayed.includes(injected)).toBe(true)
|
||||
expect(injected.getName()).toBe('MetaMask')
|
||||
expect(injected.overrideActivate?.()).toBeFalsy()
|
||||
expect(displayed.length).toEqual(1)
|
||||
})
|
||||
|
||||
it('Coinbase Mobile Browser', async () => {
|
||||
const { displayed, coinbase } = createWalletEnvironment({ isCoinbaseWallet: true }, true)
|
||||
|
||||
expect(displayed.includes(coinbase)).toBe(true)
|
||||
// Expect coinbase option to not override activation in a the cb mobile browser
|
||||
expect(coinbase.overrideActivate?.()).toBeFalsy()
|
||||
expect(displayed.length).toEqual(1)
|
||||
})
|
||||
|
||||
it('Uninjected mWeb Browser', async () => {
|
||||
const { displayed, injected, coinbase, walletconnect } = createWalletEnvironment(undefined, true)
|
||||
|
||||
expect(displayed.includes(coinbase)).toBe(true)
|
||||
expect(displayed.includes(walletconnect)).toBe(true)
|
||||
// Don't show injected connection on plain mWeb browser
|
||||
expect(displayed.includes(injected)).toBe(false)
|
||||
// Expect coinbase option to launch coinbase app in a regular mobile browser
|
||||
expect(coinbase.overrideActivate?.()).toBeTruthy()
|
||||
|
||||
expect(displayed.length).toEqual(3)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,17 +9,15 @@ import GNOSIS_ICON_URL from 'assets/images/gnosis.png'
|
||||
import METAMASK_ICON_URL from 'assets/images/metamask.svg'
|
||||
import UNIWALLET_ICON_URL from 'assets/images/uniwallet.svg'
|
||||
import WALLET_CONNECT_ICON_URL from 'assets/images/walletConnectIcon.svg'
|
||||
import INJECTED_DARK_ICON_URL from 'assets/svg/browser-wallet-dark.svg'
|
||||
import INJECTED_LIGHT_ICON_URL from 'assets/svg/browser-wallet-light.svg'
|
||||
import UNISWAP_LOGO_URL from 'assets/svg/logo.svg'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useCallback } from 'react'
|
||||
import { useIsDarkMode } from 'theme/components/ThemeToggle'
|
||||
import { isMobile, isNonIOSPhone } from 'utils/userAgent'
|
||||
|
||||
import { RPC_URLS } from '../constants/networks'
|
||||
import { RPC_PROVIDERS } from '../constants/providers'
|
||||
import { isCoinbaseWallet, isInjected, isMetaMaskWallet } from './utils'
|
||||
import { getIsCoinbaseWallet, getIsInjected, getIsMetaMaskWallet } from './utils'
|
||||
import { UniwalletConnect, WalletConnectPopup } from './WalletConnect'
|
||||
|
||||
export enum ConnectionType {
|
||||
@@ -32,13 +30,14 @@ export enum ConnectionType {
|
||||
}
|
||||
|
||||
export interface Connection {
|
||||
name: string
|
||||
getName(): string
|
||||
connector: Connector
|
||||
hooks: Web3ReactHooks
|
||||
type: ConnectionType
|
||||
icon?: string
|
||||
shouldDisplay?: boolean
|
||||
overrideActivate?: () => void
|
||||
// TODO(WEB-3130): add darkmode check for icons
|
||||
getIcon?(): string
|
||||
shouldDisplay(): boolean
|
||||
overrideActivate?: () => boolean
|
||||
isNew?: boolean
|
||||
}
|
||||
|
||||
@@ -50,73 +49,72 @@ const [web3Network, web3NetworkHooks] = initializeConnector<Network>(
|
||||
(actions) => new Network({ actions, urlMap: RPC_PROVIDERS, defaultChainId: 1 })
|
||||
)
|
||||
export const networkConnection: Connection = {
|
||||
name: 'Network',
|
||||
getName: () => 'Network',
|
||||
connector: web3Network,
|
||||
hooks: web3NetworkHooks,
|
||||
type: ConnectionType.NETWORK,
|
||||
shouldDisplay: false,
|
||||
shouldDisplay: () => false,
|
||||
}
|
||||
|
||||
const isCoinbaseWalletBrowser = isMobile && isCoinbaseWallet
|
||||
const isMetaMaskBrowser = isMobile && isMetaMaskWallet
|
||||
const getIsInjectedMobileBrowser = isCoinbaseWalletBrowser || isMetaMaskBrowser
|
||||
const getIsCoinbaseWalletBrowser = () => isMobile && getIsCoinbaseWallet()
|
||||
const getIsMetaMaskBrowser = () => isMobile && getIsMetaMaskWallet()
|
||||
const getIsInjectedMobileBrowser = () => getIsCoinbaseWalletBrowser() || getIsMetaMaskBrowser()
|
||||
|
||||
const getShouldAdvertiseMetaMask = !isMetaMaskWallet && !isMobile && (!isInjected || isCoinbaseWallet)
|
||||
const isGenericInjector = isInjected && !isMetaMaskWallet && !isCoinbaseWallet
|
||||
const getShouldAdvertiseMetaMask = () =>
|
||||
!getIsMetaMaskWallet() && !isMobile && (!getIsInjected() || getIsCoinbaseWallet())
|
||||
const getIsGenericInjector = () => getIsInjected() && !getIsMetaMaskWallet() && !getIsCoinbaseWallet()
|
||||
|
||||
const [web3Injected, web3InjectedHooks] = initializeConnector<MetaMask>((actions) => new MetaMask({ actions, onError }))
|
||||
const baseInjectedConnection: Omit<Connection, 'icon'> = {
|
||||
name: isGenericInjector ? 'Browser Wallet' : 'MetaMask',
|
||||
const injectedConnection: Connection = {
|
||||
// TODO(WEB-3131) re-add "Install MetaMask" string when no injector is present
|
||||
getName: () => (getIsGenericInjector() ? 'Browser Wallet' : 'MetaMask'),
|
||||
connector: web3Injected,
|
||||
hooks: web3InjectedHooks,
|
||||
type: ConnectionType.INJECTED,
|
||||
shouldDisplay: isMetaMaskWallet || getShouldAdvertiseMetaMask || isGenericInjector,
|
||||
getIcon: () => (getIsGenericInjector() ? INJECTED_LIGHT_ICON_URL : METAMASK_ICON_URL),
|
||||
shouldDisplay: () => getIsMetaMaskWallet() || getShouldAdvertiseMetaMask() || getIsGenericInjector(),
|
||||
// If on non-injected, non-mobile browser, prompt user to install Metamask
|
||||
overrideActivate: getShouldAdvertiseMetaMask ? () => window.open('https://metamask.io/', 'inst_metamask') : undefined,
|
||||
}
|
||||
|
||||
export const darkInjectedConnection: Connection = {
|
||||
...baseInjectedConnection,
|
||||
icon: isGenericInjector ? INJECTED_DARK_ICON_URL : METAMASK_ICON_URL,
|
||||
}
|
||||
|
||||
export const lightInjectedConnection: Connection = {
|
||||
...baseInjectedConnection,
|
||||
icon: isGenericInjector ? INJECTED_LIGHT_ICON_URL : METAMASK_ICON_URL,
|
||||
overrideActivate: () => {
|
||||
if (getShouldAdvertiseMetaMask()) {
|
||||
window.open('https://metamask.io/', 'inst_metamask')
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
}
|
||||
|
||||
const [web3GnosisSafe, web3GnosisSafeHooks] = initializeConnector<GnosisSafe>((actions) => new GnosisSafe({ actions }))
|
||||
export const gnosisSafeConnection: Connection = {
|
||||
name: 'Gnosis Safe',
|
||||
getName: () => 'Gnosis Safe',
|
||||
connector: web3GnosisSafe,
|
||||
hooks: web3GnosisSafeHooks,
|
||||
type: ConnectionType.GNOSIS_SAFE,
|
||||
icon: GNOSIS_ICON_URL,
|
||||
shouldDisplay: false,
|
||||
getIcon: () => GNOSIS_ICON_URL,
|
||||
shouldDisplay: () => false,
|
||||
}
|
||||
|
||||
const [web3WalletConnect, web3WalletConnectHooks] = initializeConnector<WalletConnectPopup>(
|
||||
(actions) => new WalletConnectPopup({ actions, onError })
|
||||
)
|
||||
export const walletConnectConnection: Connection = {
|
||||
name: 'WalletConnect',
|
||||
getName: () => 'WalletConnect',
|
||||
connector: web3WalletConnect,
|
||||
hooks: web3WalletConnectHooks,
|
||||
type: ConnectionType.WALLET_CONNECT,
|
||||
icon: WALLET_CONNECT_ICON_URL,
|
||||
shouldDisplay: !getIsInjectedMobileBrowser,
|
||||
getIcon: () => WALLET_CONNECT_ICON_URL,
|
||||
shouldDisplay: () => !getIsInjectedMobileBrowser(),
|
||||
}
|
||||
|
||||
const [web3UniwalletConnect, web3UniwalletConnectHooks] = initializeConnector<UniwalletConnect>(
|
||||
(actions) => new UniwalletConnect({ actions, onError })
|
||||
)
|
||||
export const uniwalletConnectConnection: Connection = {
|
||||
name: 'Uniswap Wallet',
|
||||
getName: () => 'Uniswap Wallet',
|
||||
connector: web3UniwalletConnect,
|
||||
hooks: web3UniwalletConnectHooks,
|
||||
type: ConnectionType.UNIWALLET,
|
||||
icon: UNIWALLET_ICON_URL,
|
||||
shouldDisplay: Boolean(!getIsInjectedMobileBrowser && !isNonIOSPhone),
|
||||
getIcon: () => UNIWALLET_ICON_URL,
|
||||
shouldDisplay: () => Boolean(!getIsInjectedMobileBrowser() && !isNonIOSPhone),
|
||||
isNew: true,
|
||||
}
|
||||
|
||||
@@ -134,24 +132,28 @@ const [web3CoinbaseWallet, web3CoinbaseWalletHooks] = initializeConnector<Coinba
|
||||
})
|
||||
)
|
||||
|
||||
export const coinbaseWalletConnection: Connection = {
|
||||
name: 'Coinbase Wallet',
|
||||
const coinbaseWalletConnection: Connection = {
|
||||
getName: () => 'Coinbase Wallet',
|
||||
connector: web3CoinbaseWallet,
|
||||
hooks: web3CoinbaseWalletHooks,
|
||||
type: ConnectionType.COINBASE_WALLET,
|
||||
icon: COINBASE_ICON_URL,
|
||||
shouldDisplay: Boolean((isMobile && !getIsInjectedMobileBrowser) || !isMobile || isCoinbaseWalletBrowser),
|
||||
getIcon: () => COINBASE_ICON_URL,
|
||||
shouldDisplay: () =>
|
||||
Boolean((isMobile && !getIsInjectedMobileBrowser()) || !isMobile || getIsCoinbaseWalletBrowser()),
|
||||
// If on a mobile browser that isn't the coinbase wallet browser, deeplink to the coinbase wallet app
|
||||
overrideActivate:
|
||||
isMobile && !getIsInjectedMobileBrowser
|
||||
? () => window.open('https://go.cb-w.com/mtUDhEZPy1', 'cbwallet')
|
||||
: undefined,
|
||||
overrideActivate: () => {
|
||||
if (isMobile && !getIsInjectedMobileBrowser()) {
|
||||
window.open('https://go.cb-w.com/mtUDhEZPy1', 'cbwallet')
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
}
|
||||
|
||||
export function getConnections(isDarkMode: boolean) {
|
||||
export function getConnections() {
|
||||
return [
|
||||
uniwalletConnectConnection,
|
||||
isDarkMode ? darkInjectedConnection : lightInjectedConnection,
|
||||
injectedConnection,
|
||||
walletConnectConnection,
|
||||
coinbaseWalletConnection,
|
||||
gnosisSafeConnection,
|
||||
@@ -159,38 +161,29 @@ export function getConnections(isDarkMode: boolean) {
|
||||
]
|
||||
}
|
||||
|
||||
export function useConnections() {
|
||||
const isDarkMode = useIsDarkMode()
|
||||
return getConnections(isDarkMode)
|
||||
}
|
||||
|
||||
export function useGetConnection() {
|
||||
const isDarkMode = useIsDarkMode()
|
||||
return useCallback(
|
||||
(c: Connector | ConnectionType) => {
|
||||
if (c instanceof Connector) {
|
||||
const connection = getConnections(isDarkMode).find((connection) => connection.connector === c)
|
||||
if (!connection) {
|
||||
throw Error('unsupported connector')
|
||||
}
|
||||
return connection
|
||||
} else {
|
||||
switch (c) {
|
||||
case ConnectionType.INJECTED:
|
||||
return isDarkMode ? darkInjectedConnection : lightInjectedConnection
|
||||
case ConnectionType.COINBASE_WALLET:
|
||||
return coinbaseWalletConnection
|
||||
case ConnectionType.WALLET_CONNECT:
|
||||
return walletConnectConnection
|
||||
case ConnectionType.UNIWALLET:
|
||||
return uniwalletConnectConnection
|
||||
case ConnectionType.NETWORK:
|
||||
return networkConnection
|
||||
case ConnectionType.GNOSIS_SAFE:
|
||||
return gnosisSafeConnection
|
||||
}
|
||||
return useCallback((c: Connector | ConnectionType) => {
|
||||
if (c instanceof Connector) {
|
||||
const connection = getConnections().find((connection) => connection.connector === c)
|
||||
if (!connection) {
|
||||
throw Error('unsupported connector')
|
||||
}
|
||||
},
|
||||
[isDarkMode]
|
||||
)
|
||||
return connection
|
||||
} else {
|
||||
switch (c) {
|
||||
case ConnectionType.INJECTED:
|
||||
return injectedConnection
|
||||
case ConnectionType.COINBASE_WALLET:
|
||||
return coinbaseWalletConnection
|
||||
case ConnectionType.WALLET_CONNECT:
|
||||
return walletConnectConnection
|
||||
case ConnectionType.UNIWALLET:
|
||||
return uniwalletConnectConnection
|
||||
case ConnectionType.NETWORK:
|
||||
return networkConnection
|
||||
case ConnectionType.GNOSIS_SAFE:
|
||||
return gnosisSafeConnection
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
export const isInjected = Boolean(window.ethereum)
|
||||
export const getIsInjected = () => Boolean(window.ethereum)
|
||||
|
||||
// When using Brave browser, `isMetaMask` is set to true when using the built-in wallet
|
||||
// This variable should be true only when using the MetaMask extension
|
||||
// https://wallet-docs.brave.com/ethereum/wallet-detection#compatability-with-metamask
|
||||
type NonMetaMaskFlag = 'isRabby' | 'isBraveWallet' | 'isTrustWallet' | 'isLedgerConnect'
|
||||
const allNonMetamaskFlags: NonMetaMaskFlag[] = ['isRabby', 'isBraveWallet', 'isTrustWallet', 'isLedgerConnect']
|
||||
export const isMetaMaskWallet = Boolean(
|
||||
window.ethereum?.isMetaMask && !allNonMetamaskFlags.some((flag) => window.ethereum?.[flag])
|
||||
)
|
||||
export const getIsMetaMaskWallet = () =>
|
||||
Boolean(window.ethereum?.isMetaMask && !allNonMetamaskFlags.some((flag) => window.ethereum?.[flag]))
|
||||
|
||||
export const isCoinbaseWallet = Boolean(window.ethereum?.isCoinbaseWallet)
|
||||
export const getIsCoinbaseWallet = () => Boolean(window.ethereum?.isCoinbaseWallet)
|
||||
|
||||
// https://eips.ethereum.org/EIPS/eip-1193#provider-errors
|
||||
export enum ErrorCode {
|
||||
|
||||
@@ -489,7 +489,7 @@ class ExtendedEther extends Ether {
|
||||
public get wrapped(): Token {
|
||||
const wrapped = WRAPPED_NATIVE_CURRENCY[this.chainId]
|
||||
if (wrapped) return wrapped
|
||||
throw new Error('Unsupported chain ID')
|
||||
throw new Error(`Unsupported chain ID: ${this.chainId}`)
|
||||
}
|
||||
|
||||
private static _cachedExtendedEther: { [chainId: number]: NativeCurrency } = {}
|
||||
|
||||
@@ -186,22 +186,20 @@ const Container = ({
|
||||
isSelected,
|
||||
isDisabled,
|
||||
detailsHref,
|
||||
doNotLinkToDetails = false,
|
||||
testId,
|
||||
onClick,
|
||||
children,
|
||||
}: {
|
||||
isSelected: boolean
|
||||
isDisabled: boolean
|
||||
detailsHref: string
|
||||
doNotLinkToDetails: boolean
|
||||
detailsHref?: string
|
||||
testId?: string
|
||||
children: ReactNode
|
||||
onClick?: (e: React.MouseEvent) => void
|
||||
}) => {
|
||||
return (
|
||||
<CardContainer isSelected={isSelected} isDisabled={isDisabled} testId={testId} onClick={onClick}>
|
||||
<StyledLink to={doNotLinkToDetails ? '' : detailsHref}>{children}</StyledLink>
|
||||
{detailsHref ? <StyledLink to={detailsHref}>{children}</StyledLink> : children}
|
||||
</CardContainer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ interface NftCardProps {
|
||||
display: NftCardDisplayProps
|
||||
isSelected: boolean
|
||||
isDisabled: boolean
|
||||
selectAsset: () => void
|
||||
unselectAsset: () => void
|
||||
onClick?: () => void
|
||||
selectAsset?: () => void
|
||||
unselectAsset?: () => void
|
||||
onButtonClick?: () => void
|
||||
onCardClick?: () => void
|
||||
sendAnalyticsEvent?: () => void
|
||||
doNotLinkToDetails?: boolean
|
||||
mediaShouldBePlaying: boolean
|
||||
uniformAspectRatio?: UniformAspectRatio
|
||||
setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void
|
||||
@@ -38,6 +38,12 @@ export interface NftCardDisplayProps {
|
||||
disabledInfo?: ReactNode
|
||||
}
|
||||
|
||||
/**
|
||||
* NftCard is a component that displays an NFT asset.
|
||||
*
|
||||
* By default, clicking on the card will navigate to the details page.
|
||||
* If you wish to override this behavior, pass a value for the onCardClick prop.
|
||||
*/
|
||||
export const NftCard = ({
|
||||
asset,
|
||||
display,
|
||||
@@ -45,9 +51,9 @@ export const NftCard = ({
|
||||
selectAsset,
|
||||
unselectAsset,
|
||||
isDisabled,
|
||||
onClick,
|
||||
onButtonClick,
|
||||
onCardClick,
|
||||
sendAnalyticsEvent,
|
||||
doNotLinkToDetails = false,
|
||||
mediaShouldBePlaying,
|
||||
uniformAspectRatio = UniformAspectRatios.square,
|
||||
setUniformAspectRatio,
|
||||
@@ -57,7 +63,13 @@ export const NftCard = ({
|
||||
testId,
|
||||
hideDetails = false,
|
||||
}: NftCardProps) => {
|
||||
const clickActionButton = useSelectAsset(selectAsset, unselectAsset, isSelected, isDisabled, onClick)
|
||||
const clickActionButton = useSelectAsset({
|
||||
selectAsset,
|
||||
unselectAsset,
|
||||
isSelected,
|
||||
isDisabled,
|
||||
onClick: onButtonClick,
|
||||
})
|
||||
const { bagExpanded, setBagExpanded } = useBag(
|
||||
(state) => ({
|
||||
bagExpanded: state.bagExpanded,
|
||||
@@ -77,11 +89,11 @@ export const NftCard = ({
|
||||
<Card.Container
|
||||
isSelected={isSelected}
|
||||
isDisabled={isDisabled}
|
||||
detailsHref={detailsHref(asset)}
|
||||
doNotLinkToDetails={doNotLinkToDetails}
|
||||
detailsHref={onCardClick ? undefined : detailsHref(asset)}
|
||||
testId={testId}
|
||||
onClick={() => {
|
||||
if (bagExpanded) setBagExpanded({ bagExpanded: false })
|
||||
onCardClick?.()
|
||||
sendAnalyticsEvent?.()
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -96,13 +96,19 @@ export function getNftDisplayComponent(
|
||||
}
|
||||
}
|
||||
|
||||
export function useSelectAsset(
|
||||
selectAsset: () => void,
|
||||
unselectAsset: () => void,
|
||||
isSelected: boolean,
|
||||
isDisabled: boolean,
|
||||
export function useSelectAsset({
|
||||
selectAsset,
|
||||
unselectAsset,
|
||||
isSelected,
|
||||
isDisabled,
|
||||
onClick,
|
||||
}: {
|
||||
selectAsset?: () => void
|
||||
unselectAsset?: () => void
|
||||
isSelected: boolean
|
||||
isDisabled: boolean
|
||||
onClick?: () => void
|
||||
) {
|
||||
}) {
|
||||
return useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
@@ -117,7 +123,7 @@ export function useSelectAsset(
|
||||
return
|
||||
}
|
||||
|
||||
return isSelected ? unselectAsset() : selectAsset()
|
||||
return isSelected ? unselectAsset?.() : selectAsset?.()
|
||||
},
|
||||
[selectAsset, isDisabled, onClick, unselectAsset, isSelected]
|
||||
)
|
||||
|
||||
@@ -3,10 +3,12 @@ import { useTrace } from '@uniswap/analytics'
|
||||
import { sendAnalyticsEvent } from '@uniswap/analytics'
|
||||
import { NFTEventName } from '@uniswap/analytics-events'
|
||||
import { NftCard, NftCardDisplayProps } from 'nft/components/card'
|
||||
import { detailsHref } from 'nft/components/card/utils'
|
||||
import { VerifiedIcon } from 'nft/components/icons'
|
||||
import { useBag, useIsMobile, useSellAsset } from 'nft/hooks'
|
||||
import { WalletAsset } from 'nft/types'
|
||||
import { useMemo } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
interface ViewMyNftsAssetProps {
|
||||
asset: WalletAsset
|
||||
@@ -27,6 +29,7 @@ export const ViewMyNftsAsset = ({
|
||||
const cartExpanded = useBag((state) => state.bagExpanded)
|
||||
const toggleCart = useBag((state) => state.toggleBag)
|
||||
const isMobile = useIsMobile()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const isSelected = useMemo(() => {
|
||||
return sellAssets.some(
|
||||
@@ -35,7 +38,7 @@ export const ViewMyNftsAsset = ({
|
||||
}, [asset, sellAssets])
|
||||
|
||||
const trace = useTrace()
|
||||
const onCardClick = () => handleSelect(isSelected)
|
||||
const toggleSelect = () => handleSelect(isSelected)
|
||||
|
||||
const handleSelect = (removeAsset: boolean) => {
|
||||
if (removeAsset) {
|
||||
@@ -79,11 +82,13 @@ export const ViewMyNftsAsset = ({
|
||||
isDisabled={Boolean(isDisabled)}
|
||||
selectAsset={() => handleSelect(false)}
|
||||
unselectAsset={() => handleSelect(true)}
|
||||
onClick={onCardClick}
|
||||
onButtonClick={toggleSelect}
|
||||
onCardClick={() => {
|
||||
if (!hideDetails) navigate(detailsHref(asset))
|
||||
}}
|
||||
mediaShouldBePlaying={mediaShouldBePlaying}
|
||||
setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia}
|
||||
testId="nft-profile-asset"
|
||||
doNotLinkToDetails={hideDetails}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NftStandard } from 'graphql/data/__generated__/types-and-hooks'
|
||||
import { BagItem, BagItemStatus, BagStatus, UpdatedGenieAsset } from 'nft/types'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
interface BagState {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NftAssetSortableField } from 'graphql/data/__generated__/types-and-hooks'
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
export enum SortBy {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools, persist } from 'zustand/middleware'
|
||||
|
||||
interface State {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
interface State {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
interface NFTClaim {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
export type MarketplaceOption = { name: string; icon: string }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CollectionRow, ListingRow, ListingStatus } from 'nft/types'
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
interface NFTListState {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
import { OpenSeaAsset } from '../types'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
interface PriceRangeProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
import { ProfilePageStateType } from '../types'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
import { GenieAsset } from '../types'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
import { ListingMarket, WalletAsset } from '../types'
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ContractReceipt } from '@ethersproject/contracts'
|
||||
import type { JsonRpcSigner } from '@ethersproject/providers'
|
||||
import { sendAnalyticsEvent } from '@uniswap/analytics'
|
||||
import { NFTEventName } from '@uniswap/analytics-events'
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
import ERC721 from '../../abis/erc721.json'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools, persist } from 'zustand/middleware'
|
||||
|
||||
import { GenieAsset } from '../types'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { TokenTradeInput } from 'graphql/data/__generated__/types-and-hooks'
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
interface TokenInputState {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
interface traitOpen {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TxResponse } from 'nft/types'
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
type TransactionResponseValue = TxResponse | undefined
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NftStandard } from 'graphql/data/__generated__/types-and-hooks'
|
||||
import create from 'zustand'
|
||||
import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
import { WalletAsset, WalletCollection } from '../types'
|
||||
|
||||
@@ -17,10 +17,13 @@ const AMPLITUDE_DUMMY_KEY = '00000000000000000000000000000000'
|
||||
export const STATSIG_DUMMY_KEY = 'client-0000000000000000000000000000000000000000000'
|
||||
|
||||
Sentry.init({
|
||||
// General configuration:
|
||||
dsn: process.env.REACT_APP_SENTRY_DSN,
|
||||
release: process.env.REACT_APP_GIT_COMMIT_HASH,
|
||||
environment: getEnvName(),
|
||||
// Exception reporting configuration:
|
||||
enabled: isSentryEnabled(),
|
||||
// Performance tracing configuration:
|
||||
tracesSampleRate: Number(process.env.REACT_APP_SENTRY_TRACES_SAMPLE_RATE ?? 0),
|
||||
integrations: [
|
||||
new BrowserTracing({
|
||||
@@ -28,13 +31,6 @@ Sentry.init({
|
||||
startTransactionOnPageLoad: true,
|
||||
}),
|
||||
],
|
||||
/**
|
||||
* TODO(INFRA-143)
|
||||
* According to Sentry, this shouldn't be necessary, as they default to `3` when not set.
|
||||
* Unfortunately, that doesn't work right now, so we workaround it by explicitly setting
|
||||
* the `normalizeDepth` to `10`. This should be removed once the issue is fixed.
|
||||
*/
|
||||
normalizeDepth: 10,
|
||||
})
|
||||
|
||||
initializeAnalytics(AMPLITUDE_DUMMY_KEY, OriginApplication.INTERFACE, {
|
||||
|
||||
98
yarn.lock
98
yarn.lock
@@ -3190,67 +3190,75 @@
|
||||
dependencies:
|
||||
cross-fetch "^3.1.5"
|
||||
|
||||
"@sentry/browser@7.42.0":
|
||||
version "7.42.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.42.0.tgz#357731e5ab65a226c98370f9e487fe48cadab765"
|
||||
integrity sha512-xTwfvrQPmYTkAvGivoJFadPLKLDS2N57D/18NA1gcrnF8NwR+I28x3I9ziVUiMCYX+6nJuqBNlMALAEPbb2G5A==
|
||||
"@sentry-internal/tracing@7.45.0":
|
||||
version "7.45.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.45.0.tgz#01f347d0d1b42451b340b32b12923dc22e042d27"
|
||||
integrity sha512-0aIDY2OvUX7k2XHaimOlWkboXoQvJ9dEKvfpu0Wh0YxfUTGPa+wplUdg3WVdkk018sq1L11MKmj4MPZyYUvXhw==
|
||||
dependencies:
|
||||
"@sentry/core" "7.42.0"
|
||||
"@sentry/replay" "7.42.0"
|
||||
"@sentry/types" "7.42.0"
|
||||
"@sentry/utils" "7.42.0"
|
||||
"@sentry/core" "7.45.0"
|
||||
"@sentry/types" "7.45.0"
|
||||
"@sentry/utils" "7.45.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/core@7.42.0":
|
||||
version "7.42.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.42.0.tgz#3333a1b868e8e69b6fbc8e5a9e9281be49321ac7"
|
||||
integrity sha512-vNcTyoQz5kUXo5vMGDyc5BJMO0UugPvMfYMQVxqt/BuDNR30LVhY+DL2tW1DFZDvRvyn5At+H7kSTj6GFrANXQ==
|
||||
"@sentry/browser@7.45.0":
|
||||
version "7.45.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.45.0.tgz#c9f8031ad184558d08c374d4e4ee30996cc5b543"
|
||||
integrity sha512-/dUrUwnI34voMj+jSJT7b5Jun+xy1utVyzzwTq3Oc22N+SB17ZOX9svZ4jl1Lu6tVJPVjPyvL6zlcbrbMwqFjg==
|
||||
dependencies:
|
||||
"@sentry/types" "7.42.0"
|
||||
"@sentry/utils" "7.42.0"
|
||||
"@sentry-internal/tracing" "7.45.0"
|
||||
"@sentry/core" "7.45.0"
|
||||
"@sentry/replay" "7.45.0"
|
||||
"@sentry/types" "7.45.0"
|
||||
"@sentry/utils" "7.45.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/react@^7.40.0":
|
||||
version "7.42.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.42.0.tgz#7662faef398032f253fe5b174860e1ec5f76ddd5"
|
||||
integrity sha512-DOGK+vuSZq5lTiqVU6wVay0AUMjtSPZu25gzLIXntfoqw36CLUswP7ew61+Tas6tpXDdf4lR3uxJRwySiQLopw==
|
||||
"@sentry/core@7.45.0":
|
||||
version "7.45.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.45.0.tgz#87fdb283c211f145e508cc8ff89dabdf2fbcfc39"
|
||||
integrity sha512-xJfdTS4lRmHvZI/A5MazdnKhBJFkisKu6G9EGNLlZLre+6W4PH5sb7QX4+xoBdqG7v10Jvdia112vi762ojO2w==
|
||||
dependencies:
|
||||
"@sentry/browser" "7.42.0"
|
||||
"@sentry/types" "7.42.0"
|
||||
"@sentry/utils" "7.42.0"
|
||||
"@sentry/types" "7.45.0"
|
||||
"@sentry/utils" "7.45.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/react@^7.45.0":
|
||||
version "7.45.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.45.0.tgz#9d3634b7b93bc1fa0872cdec49c1fbacd4ef2506"
|
||||
integrity sha512-Dbz85nfvMUikbLHUuIt6fBNPmTvThFn+rWB5KS1NIOJifyWAdpIU3X7yCUJE5xhsUObNLiHlNJlqhaQI4nR1bQ==
|
||||
dependencies:
|
||||
"@sentry/browser" "7.45.0"
|
||||
"@sentry/types" "7.45.0"
|
||||
"@sentry/utils" "7.45.0"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/replay@7.42.0":
|
||||
version "7.42.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.42.0.tgz#03be2bdab35e0f2d4e415a770c23d50dba8d73b5"
|
||||
integrity sha512-81HQm20hrW0+0eZ5sZf8KsSekkAlI0/u/M+9ZmOn2bHpGihqAM/O/lrXhTzaRXdpX/9NSwSCGY9k7LIRNMKaEQ==
|
||||
"@sentry/replay@7.45.0":
|
||||
version "7.45.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.45.0.tgz#1da15e8c419bb77ec7475c7b1879d11f17edad20"
|
||||
integrity sha512-smM7FIcFIyKu30BqCl8BzLo1gH/z9WwXdGX6V0fNvHab9fJZ09+xjFn+LmIyo6N8H8jjwsup0+yQ12kiF/ZsEw==
|
||||
dependencies:
|
||||
"@sentry/core" "7.42.0"
|
||||
"@sentry/types" "7.42.0"
|
||||
"@sentry/utils" "7.42.0"
|
||||
"@sentry/core" "7.45.0"
|
||||
"@sentry/types" "7.45.0"
|
||||
"@sentry/utils" "7.45.0"
|
||||
|
||||
"@sentry/tracing@^7.40.0":
|
||||
version "7.42.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.42.0.tgz#bcdac21e1cb5f786465e6252625bd4bf0736e631"
|
||||
integrity sha512-0veGu3Ntweuj1pwWrJIFHmVdow4yufCreGIhsNDyrclwOjaTY3uI8iA6N62+hhtxOvqv+xueB98K1DvT5liPCQ==
|
||||
"@sentry/tracing@^7.45.0":
|
||||
version "7.45.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.45.0.tgz#77fe1075b3fdfd5026bf8d816a855bbe992b64a3"
|
||||
integrity sha512-FsoFmZPzTBGvWeJH73NxSF1ot61Zw3aIZo5XolengiKnRmcrQOFxebtMKBiZ61QBRYGqsm5uT7QB7zITU6Ikgg==
|
||||
dependencies:
|
||||
"@sentry/core" "7.42.0"
|
||||
"@sentry/types" "7.42.0"
|
||||
"@sentry/utils" "7.42.0"
|
||||
tslib "^1.9.3"
|
||||
"@sentry-internal/tracing" "7.45.0"
|
||||
|
||||
"@sentry/types@7.42.0":
|
||||
version "7.42.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.42.0.tgz#e5019cd41a8c4a98c296e2ff28d6adab4b2eb14e"
|
||||
integrity sha512-Ga0xaBIR/peuXQ88hI9a5TNY3GLNoH8jpsgPaAjAtRHkLsTx0y3AR+PrD7pUysza9QjvG+Qux01DRvLgaNKOHA==
|
||||
"@sentry/types@7.45.0":
|
||||
version "7.45.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.45.0.tgz#b5e2db7a421f6090398565b0a72fb3bbdc94233a"
|
||||
integrity sha512-iFt7msfUK8LCodFF3RKUyaxy9tJv/gpWhzxUFyNxtuVwlpmd+q6mtsFGn8Af3pbpm8A+MKyz1ebMwXj0PQqknw==
|
||||
|
||||
"@sentry/utils@7.42.0":
|
||||
version "7.42.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.42.0.tgz#fcffd0404836cb56975fbef9e889a42cc55de596"
|
||||
integrity sha512-cBiDZVipC+is+IVgsTQLJyZWUZQxlLZ9GarNT+XZOZ5BFh0acFtz88hO6+S7vGmhcx2aCvsdC9yb2Yf+BphK6Q==
|
||||
"@sentry/utils@7.45.0":
|
||||
version "7.45.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.45.0.tgz#e13e075098578557ec3a0decf735cbad6a26ce63"
|
||||
integrity sha512-aTY7qqtNUudd09SH5DVSKMm3iQ6ZeWufduc0I9bPZe6UMM09BDc4KmjmrzRkdQ+VaOmHo7+v+HZKQk5f+AbuTQ==
|
||||
dependencies:
|
||||
"@sentry/types" "7.42.0"
|
||||
"@sentry/types" "7.45.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sinonjs/commons@^1.7.0":
|
||||
|
||||
Reference in New Issue
Block a user