Commit 1802f501 authored by Mike Grabowski's avatar Mike Grabowski Committed by GitHub

chore: use @uniswap eslint preset (#5556)

* feat: replace eslint with preset

* chore: update

* add empty line

* Revert changes

* chore: replace colors

* chore: tweaks

* Revert "chore: replace colors"

This reverts commit 3462420ecbdd9c5275a895643dad1586e92226a0.

* bring lint back

* chore: tweaks

* chore: add note

* chore: fix yarn lock

* chore: fix yarn.lock 2

* chore: use ESLint from npm

* Chore: update lockfile

* tweaks

* chore: initial take fixing some lint issues

* tweaks

* chore: another take

* chore: further tweaks

* chore: fix further

* feat: ignore Jest for cypress

* revert change

* chore: update to latest preset

* tmp lets see if this works

* chore: turn error into warning

* chore: remove warnings

* chore: deduplicate yarn lock

* feat: add recommended ESLint extension in case someone has Prettier instead

* upgrade to latest uniswap config

* chore: update todo

* remove patch

* find to some

* name

* chore: tweak yarn lock

* update

* cleanup

* update name for filter

* nl

* no unsafe finally

* chore: update doc

* fix

* fix

* one more fix

* one more file

* chore: Fix two last build issues

* add generated back

* fix lint after merge

* chore: fix tests

* remove

* one more
parent 2aa1b18d
...@@ -2,3 +2,4 @@ ...@@ -2,3 +2,4 @@
*.d.ts *.d.ts
/src/graphql/data/__generated__/types-and-hooks.ts /src/graphql/data/__generated__/types-and-hooks.ts
/src/graphql/thegraph/__generated__/types-and-hooks.ts /src/graphql/thegraph/__generated__/types-and-hooks.ts
/src/schema/schema.graphql
/* eslint-env node */
require('@uniswap/eslint-config/load')
module.exports = {
extends: '@uniswap/eslint-config/react',
}
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
// Allows for the parsing of JSX
"jsx": true
}
},
"settings": {
"react": {
"version": "detect"
},
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"]
},
"import/resolver": {
"typescript": {
"alwaysTryTypes": true
}
}
},
"ignorePatterns": [
"src/types/v3",
"src/abis/types",
"src/locales/**/*.js",
"src/locales/**/en-US.po",
"node_modules",
"coverage",
"build",
"dist",
".DS_Store",
".env.local",
".env.development.local",
".env.test.local",
".env.production.local",
".idea/",
".vscode/",
"package-lock.json",
"yarn.lock"
],
"extends": [
"react-app",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended",
"plugin:import/typescript"
],
"plugins": ["import", "simple-import-sort", "unused-imports"],
"rules": {
"import/no-unused-modules": [2, { "unusedExports": true }],
"unused-imports/no-unused-imports": "error",
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
"@typescript-eslint/explicit-function-return-type": "off",
"prettier/prettier": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"react/react-in-jsx-scope": "off",
"react/jsx-curly-brace-presence": ["error", { "props": "never", "children": "never" }],
"object-shorthand": ["error", "always"],
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "ethers",
"message": "Please import from '@ethersproject/module' directly to support tree-shaking."
},
{
"name": "styled-components",
"message": "Please import from styled-components/macro."
},
{
"name": "@lingui/macro",
"importNames": ["t"],
"message": "Please use <Trans> instead of t."
}
],
"patterns": [
{
"group": ["**/dist"],
"message": "Do not import from dist/ - this is an implementation detail, and breaks tree-shaking."
},
{
"group": ["!styled-components/macro"]
}
]
}
],
"@typescript-eslint/no-restricted-imports": [
"error",
{
"paths": [
{
"name": "@ethersproject/providers",
"message": "Please only use Providers instantiated in constants/providers to improve traceability.",
"allowTypeImports": true
}
]
}
]
}
}
/src/schema/schema.graphql
\ No newline at end of file
{
"semi": false,
"singleQuote": true,
"printWidth": 120
}
{
"recommendations": [
"dbaeumer.vscode-eslint"
],
"unwantedRecommendations": []
}
...@@ -5,15 +5,12 @@ ...@@ -5,15 +5,12 @@
"editor.formatOnSaveMode": "file", "editor.formatOnSaveMode": "file",
"editor.tabCompletion": "on", "editor.tabCompletion": "on",
"editor.tabSize": 2, "editor.tabSize": 2,
"editor.formatOnSave": true, "editor.formatOnSave": false,
"editor.inlineSuggest.enabled": true, "editor.inlineSuggest.enabled": true,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll": true "source.fixAll": true
}, },
"files.eol": "\n", "files.eol": "\n",
"eslint.enable": true, "eslint.enable": true,
"eslint.debug": true, "eslint.debug": true
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
} }
/* eslint-env node */
import type { CodegenConfig } from '@graphql-codegen/cli' import type { CodegenConfig } from '@graphql-codegen/cli'
// Generates TS objects from the schemas returned by graphql queries // Generates TS objects from the schemas returned by graphql queries
......
/* eslint-env node */
import type { CodegenConfig } from '@graphql-codegen/cli' import type { CodegenConfig } from '@graphql-codegen/cli'
// Generates TS objects from the schemas returned by graphql queries // Generates TS objects from the schemas returned by graphql queries
......
/* eslint-env node */
const isDev = process.env.NODE_ENV === 'development' const isDev = process.env.NODE_ENV === 'development'
module.exports = { module.exports = {
......
...@@ -8,7 +8,8 @@ describe( ...@@ -8,7 +8,8 @@ describe(
}, },
() => { () => {
it('loads swap page', () => { it('loads swap page', () => {
// We *must* wait in order to space out the retry attempts. // TODO: We *must* wait in order to space out the retry attempts. Find a better way to do this.
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(ONE_MINUTE) cy.wait(ONE_MINUTE)
.visit('/', { .visit('/', {
retryOnStatusCodeFailure: true, retryOnStatusCodeFailure: true,
......
...@@ -85,8 +85,7 @@ beforeEach(() => { ...@@ -85,8 +85,7 @@ beforeEach(() => {
}) })
}) })
Cypress.on('uncaught:exception', (_err, _runnable) => { Cypress.on('uncaught:exception', () => {
// returning false here prevents Cypress from // returning false here prevents Cypress from failing the test
// failing the test
return false return false
}) })
// Utility to match GraphQL mutation based on the query name // Utility to match GraphQL mutation based on the query name
export const hasQuery = (req: any, queryName: string) => { export const hasQuery = (req: any, queryName: string) => {
const { body } = req const { body } = req
return body.hasOwnProperty('query') && body.query.includes(queryName) return Object.prototype.hasOwnProperty.call(body, 'query') && body.query.includes(queryName)
} }
// Alias query if queryName matches // Alias query if queryName matches
......
/* eslint-disable */ /* eslint-env node */
require('dotenv').config({ path: '.env.production' }) require('dotenv').config({ path: '.env.production' })
const { exec } = require('child_process') const { exec } = require('child_process')
const dataConfig = require('./graphql.config') const dataConfig = require('./graphql.config')
const thegraphConfig = require('./graphql_thegraph.config') const thegraphConfig = require('./graphql_thegraph.config')
/* eslint-enable */
function fetchSchema(url, outputFile) { function fetchSchema(url, outputFile) {
exec( exec(
......
/* eslint-env node */
module.exports = { module.exports = {
src: './src', src: './src',
language: 'typescript', language: 'typescript',
......
// eslint-disable-next-line @typescript-eslint/no-var-requires /* eslint-env node */
const defaultConfig = require('./graphql.config') const defaultConfig = require('./graphql.config')
module.exports = { module.exports = {
......
// eslint-disable-next-line @typescript-eslint/no-var-requires /* eslint-env node */
const { exec } = require('child_process') const { exec } = require('child_process')
const isWindows = process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE) const isWindows = process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE)
......
...@@ -14,18 +14,15 @@ import { ...@@ -14,18 +14,15 @@ import {
CollectFeesTransactionInfo, CollectFeesTransactionInfo,
CreateV3PoolTransactionInfo, CreateV3PoolTransactionInfo,
DelegateTransactionInfo, DelegateTransactionInfo,
DepositLiquidityStakingTransactionInfo,
ExactInputSwapTransactionInfo, ExactInputSwapTransactionInfo,
ExactOutputSwapTransactionInfo, ExactOutputSwapTransactionInfo,
ExecuteTransactionInfo, ExecuteTransactionInfo,
MigrateV2LiquidityToV3TransactionInfo, MigrateV2LiquidityToV3TransactionInfo,
QueueTransactionInfo, QueueTransactionInfo,
RemoveLiquidityV3TransactionInfo, RemoveLiquidityV3TransactionInfo,
SubmitProposalTransactionInfo,
TransactionInfo, TransactionInfo,
TransactionType, TransactionType,
VoteTransactionInfo, VoteTransactionInfo,
WithdrawLiquidityStakingTransactionInfo,
WrapTransactionInfo, WrapTransactionInfo,
} from '../../state/transactions/types' } from '../../state/transactions/types'
...@@ -83,7 +80,7 @@ function ClaimSummary({ info: { recipient, uniAmountRaw } }: { info: ClaimTransa ...@@ -83,7 +80,7 @@ function ClaimSummary({ info: { recipient, uniAmountRaw } }: { info: ClaimTransa
) )
} }
function SubmitProposalTransactionSummary(_: { info: SubmitProposalTransactionInfo }) { function SubmitProposalTransactionSummary() {
return <Trans>Submit new proposal</Trans> return <Trans>Submit new proposal</Trans>
} }
...@@ -175,13 +172,13 @@ function WrapSummary({ info: { chainId, currencyAmountRaw, unwrapped } }: { info ...@@ -175,13 +172,13 @@ function WrapSummary({ info: { chainId, currencyAmountRaw, unwrapped } }: { info
} }
} }
function DepositLiquidityStakingSummary(_: { info: DepositLiquidityStakingTransactionInfo }) { function DepositLiquidityStakingSummary() {
// not worth rendering the tokens since you can should no longer deposit liquidity in the staking contracts // not worth rendering the tokens since you can should no longer deposit liquidity in the staking contracts
// todo: deprecate and delete the code paths that allow this, show user more information // todo: deprecate and delete the code paths that allow this, show user more information
return <Trans>Deposit liquidity</Trans> return <Trans>Deposit liquidity</Trans>
} }
function WithdrawLiquidityStakingSummary(_: { info: WithdrawLiquidityStakingTransactionInfo }) { function WithdrawLiquidityStakingSummary() {
return <Trans>Withdraw deposited liquidity</Trans> return <Trans>Withdraw deposited liquidity</Trans>
} }
...@@ -319,10 +316,10 @@ export function TransactionSummary({ info }: { info: TransactionInfo }) { ...@@ -319,10 +316,10 @@ export function TransactionSummary({ info }: { info: TransactionInfo }) {
return <ClaimSummary info={info} /> return <ClaimSummary info={info} />
case TransactionType.DEPOSIT_LIQUIDITY_STAKING: case TransactionType.DEPOSIT_LIQUIDITY_STAKING:
return <DepositLiquidityStakingSummary info={info} /> return <DepositLiquidityStakingSummary />
case TransactionType.WITHDRAW_LIQUIDITY_STAKING: case TransactionType.WITHDRAW_LIQUIDITY_STAKING:
return <WithdrawLiquidityStakingSummary info={info} /> return <WithdrawLiquidityStakingSummary />
case TransactionType.SWAP: case TransactionType.SWAP:
return <SwapSummary info={info} /> return <SwapSummary info={info} />
...@@ -358,6 +355,6 @@ export function TransactionSummary({ info }: { info: TransactionInfo }) { ...@@ -358,6 +355,6 @@ export function TransactionSummary({ info }: { info: TransactionInfo }) {
return <ExecuteSummary info={info} /> return <ExecuteSummary info={info} />
case TransactionType.SUBMIT_PROPOSAL: case TransactionType.SUBMIT_PROPOSAL:
return <SubmitProposalTransactionSummary info={info} /> return <SubmitProposalTransactionSummary />
} }
} }
...@@ -39,26 +39,32 @@ const getCurrency = ({ info, chainId }: { info: TransactionInfo; chainId: number ...@@ -39,26 +39,32 @@ const getCurrency = ({ info, chainId }: { info: TransactionInfo; chainId: number
switch (info.type) { switch (info.type) {
case TransactionType.ADD_LIQUIDITY_V3_POOL: case TransactionType.ADD_LIQUIDITY_V3_POOL:
case TransactionType.REMOVE_LIQUIDITY_V3: case TransactionType.REMOVE_LIQUIDITY_V3:
case TransactionType.CREATE_V3_POOL: case TransactionType.CREATE_V3_POOL: {
const { baseCurrencyId, quoteCurrencyId } = info const { baseCurrencyId, quoteCurrencyId } = info
return { currencyId0: baseCurrencyId, currencyId1: quoteCurrencyId } return { currencyId0: baseCurrencyId, currencyId1: quoteCurrencyId }
case TransactionType.SWAP: }
case TransactionType.SWAP: {
const { inputCurrencyId, outputCurrencyId } = info const { inputCurrencyId, outputCurrencyId } = info
return { currencyId0: inputCurrencyId, currencyId1: outputCurrencyId } return { currencyId0: inputCurrencyId, currencyId1: outputCurrencyId }
case TransactionType.WRAP: }
case TransactionType.WRAP: {
const { unwrapped } = info const { unwrapped } = info
const native = info.chainId ? nativeOnChain(info.chainId) : undefined const native = info.chainId ? nativeOnChain(info.chainId) : undefined
const base = 'ETH' const base = 'ETH'
const wrappedCurrency = native?.wrapped.address ?? 'WETH' const wrappedCurrency = native?.wrapped.address ?? 'WETH'
return { currencyId0: unwrapped ? wrappedCurrency : base, currencyId1: unwrapped ? base : wrappedCurrency } return { currencyId0: unwrapped ? wrappedCurrency : base, currencyId1: unwrapped ? base : wrappedCurrency }
case TransactionType.COLLECT_FEES: }
case TransactionType.COLLECT_FEES: {
const { currencyId0, currencyId1 } = info const { currencyId0, currencyId1 } = info
return { currencyId0, currencyId1 } return { currencyId0, currencyId1 }
case TransactionType.APPROVAL: }
case TransactionType.APPROVAL: {
return { currencyId0: info.tokenAddress, currencyId1: undefined } return { currencyId0: info.tokenAddress, currencyId1: undefined }
case TransactionType.CLAIM: }
case TransactionType.CLAIM: {
const uniAddress = chainId ? UNI_ADDRESS[chainId] : undefined const uniAddress = chainId ? UNI_ADDRESS[chainId] : undefined
return { currencyId0: uniAddress, currencyId1: undefined } return { currencyId0: uniAddress, currencyId1: undefined }
}
default: default:
return { currencyId0: undefined, currencyId1: undefined } return { currencyId0: undefined, currencyId1: undefined }
} }
......
...@@ -164,7 +164,7 @@ function FeatureFlagGroup({ name, children }: PropsWithChildren<{ name: string } ...@@ -164,7 +164,7 @@ function FeatureFlagGroup({ name, children }: PropsWithChildren<{ name: string }
) )
} }
function FeatureFlagOption({ variant, featureFlag, value, label }: FeatureFlagProps) { function FeatureFlagOption({ variant, featureFlag, label }: FeatureFlagProps) {
const updateFlag = useUpdateFlag() const updateFlag = useUpdateFlag()
const [count, setCount] = useState(0) const [count, setCount] = useState(0)
const featureFlags = useAtomValue(featureFlagSettings) const featureFlags = useAtomValue(featureFlagSettings)
......
...@@ -67,7 +67,7 @@ const MOONPAY_SUPPORTED_CURRENCY_CODES = [ ...@@ -67,7 +67,7 @@ const MOONPAY_SUPPORTED_CURRENCY_CODES = [
export default function FiatOnrampModal() { export default function FiatOnrampModal() {
const { account } = useWeb3React() const { account } = useWeb3React()
const theme = useTheme() const theme = useTheme()
const closeModal = useCloseModal(ApplicationModal.FIAT_ONRAMP) const closeModal = useCloseModal()
const fiatOnrampModalOpen = useModalIsOpen(ApplicationModal.FIAT_ONRAMP) const fiatOnrampModalOpen = useModalIsOpen(ApplicationModal.FIAT_ONRAMP)
const [signedIframeUrl, setSignedIframeUrl] = useState<string | null>(null) const [signedIframeUrl, setSignedIframeUrl] = useState<string | null>(null)
......
...@@ -76,7 +76,7 @@ export function AddRemoveTabs({ ...@@ -76,7 +76,7 @@ export function AddRemoveTabs({
// detect if back should redirect to v3 or v2 pool page // detect if back should redirect to v3 or v2 pool page
const poolLink = location.pathname.includes('add/v2') const poolLink = location.pathname.includes('add/v2')
? '/pool/v2' ? '/pool/v2'
: '/pool' + (!!positionID ? `/${positionID.toString()}` : '') : '/pool' + (positionID ? `/${positionID.toString()}` : '')
return ( return (
<Tabs> <Tabs>
......
...@@ -51,7 +51,7 @@ const ToggleElement = styled.span<{ isActive?: boolean; bgColor?: string; isInit ...@@ -51,7 +51,7 @@ const ToggleElement = styled.span<{ isActive?: boolean; bgColor?: string; isInit
${({ isActive, isInitialToggleLoad }) => (isInitialToggleLoad ? 'none' : isActive ? turnOnToggle : turnOffToggle)} ${({ isActive, isInitialToggleLoad }) => (isInitialToggleLoad ? 'none' : isActive ? turnOnToggle : turnOffToggle)}
ease-in; ease-in;
background: ${({ theme, bgColor, isActive }) => background: ${({ theme, bgColor, isActive }) =>
isActive ? bgColor ?? theme.accentAction : !!bgColor ? theme.deprecated_bg4 : theme.textTertiary}; isActive ? bgColor ?? theme.accentAction : bgColor ? theme.deprecated_bg4 : theme.textTertiary};
border-radius: 50%; border-radius: 50%;
height: 24px; height: 24px;
:hover { :hover {
......
...@@ -222,7 +222,7 @@ export default function TokenDetailsSkeleton() { ...@@ -222,7 +222,7 @@ export default function TokenDetailsSkeleton() {
const { chainName } = useParams<{ chainName?: string }>() const { chainName } = useParams<{ chainName?: string }>()
return ( return (
<LeftPanel> <LeftPanel>
<BreadcrumbNavLink to={{ chainName } ? `/tokens/${chainName}` : `/explore`}> <BreadcrumbNavLink to={chainName ? `/tokens/${chainName}` : `/explore`}>
<ArrowLeft size={14} /> Tokens <ArrowLeft size={14} /> Tokens
</BreadcrumbNavLink> </BreadcrumbNavLink>
<TokenInfoContainer> <TokenInfoContainer>
......
...@@ -51,24 +51,13 @@ export const StatsWrapper = styled.div` ...@@ -51,24 +51,13 @@ export const StatsWrapper = styled.div`
type NumericStat = number | undefined | null type NumericStat = number | undefined | null
function Stat({ function Stat({ value, title, description }: { value: NumericStat; title: ReactNode; description?: ReactNode }) {
value,
title,
description,
isPrice = false,
}: {
value: NumericStat
title: ReactNode
description?: ReactNode
isPrice?: boolean
}) {
return ( return (
<StatWrapper> <StatWrapper>
<StatTitle> <StatTitle>
{title} {title}
{description && <InfoTip text={description}></InfoTip>} {description && <InfoTip text={description}></InfoTip>}
</StatTitle> </StatTitle>
<StatPrice>{formatNumber(value, NumberType.FiatTokenStats)}</StatPrice> <StatPrice>{formatNumber(value, NumberType.FiatTokenStats)}</StatPrice>
</StatWrapper> </StatWrapper>
) )
...@@ -106,8 +95,8 @@ export default function StatsSection(props: StatsSectionProps) { ...@@ -106,8 +95,8 @@ export default function StatsSection(props: StatsSectionProps) {
/> />
</StatPair> </StatPair>
<StatPair> <StatPair>
<Stat value={priceLow52W} title={<Trans>52W low</Trans>} isPrice={true} /> <Stat value={priceLow52W} title={<Trans>52W low</Trans>} />
<Stat value={priceHigh52W} title={<Trans>52W high</Trans>} isPrice={true} /> <Stat value={priceHigh52W} title={<Trans>52W high</Trans>} />
</StatPair> </StatPair>
</TokenStatsSection> </TokenStatsSection>
</StatsWrapper> </StatsWrapper>
......
...@@ -192,7 +192,7 @@ const AuthenticatedHeader = () => { ...@@ -192,7 +192,7 @@ const AuthenticatedHeader = () => {
explorer, explorer,
} = getChainInfoOrDefault(chainId ? chainId : SupportedChainId.MAINNET) } = getChainInfoOrDefault(chainId ? chainId : SupportedChainId.MAINNET)
const navigate = useNavigate() const navigate = useNavigate()
const closeModal = useCloseModal(ApplicationModal.WALLET_DROPDOWN) const closeModal = useCloseModal()
const setSellPageState = useProfilePageState((state) => state.setProfilePageState) const setSellPageState = useProfilePageState((state) => state.setProfilePageState)
const resetSellAssets = useSellAsset((state) => state.reset) const resetSellAssets = useSellAsset((state) => state.reset)
const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters) const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters)
......
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import * as connectionUtils from 'connection/utils' import * as connectionUtils from 'connection/utils'
import { ApplicationModal } from 'state/application/reducer'
import { nativeOnChain } from '../../constants/tokens' import { nativeOnChain } from '../../constants/tokens'
import { render, screen } from '../../test-utils' import { render, screen } from '../../test-utils'
...@@ -20,7 +18,7 @@ jest.mock('utils/userAgent', () => ({ ...@@ -20,7 +18,7 @@ jest.mock('utils/userAgent', () => ({
jest.mock('.../../state/application/hooks', () => { jest.mock('.../../state/application/hooks', () => {
return { return {
useModalIsOpen: (_modal: ApplicationModal) => true, useModalIsOpen: () => true,
useToggleWalletModal: () => { useToggleWalletModal: () => {
return return
}, },
...@@ -29,7 +27,7 @@ jest.mock('.../../state/application/hooks', () => { ...@@ -29,7 +27,7 @@ jest.mock('.../../state/application/hooks', () => {
jest.mock('hooks/useStablecoinPrice', () => { jest.mock('hooks/useStablecoinPrice', () => {
return { return {
useStablecoinValue: (_currencyAmount: CurrencyAmount<Currency> | undefined | null) => { useStablecoinValue: () => {
return return
}, },
} }
...@@ -38,10 +36,10 @@ jest.mock('hooks/useStablecoinPrice', () => { ...@@ -38,10 +36,10 @@ jest.mock('hooks/useStablecoinPrice', () => {
jest.mock('lib/hooks/useCurrencyBalance', () => { jest.mock('lib/hooks/useCurrencyBalance', () => {
return { return {
__esModule: true, __esModule: true,
default: (account?: string, currency?: Currency) => { default: () => {
return return
}, },
useTokenBalance: (account?: string, token?: Token) => { useTokenBalance: () => {
return return
}, },
} }
......
...@@ -300,7 +300,7 @@ export default function Web3Status() { ...@@ -300,7 +300,7 @@ export default function Web3Status() {
const allTransactions = useAllTransactions() const allTransactions = useAllTransactions()
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)
const walletRef = useRef<HTMLDivElement>(null) const walletRef = useRef<HTMLDivElement>(null)
const closeModal = useCloseModal(ApplicationModal.WALLET_DROPDOWN) const closeModal = useCloseModal()
const isOpen = useModalIsOpen(ApplicationModal.WALLET_DROPDOWN) const isOpen = useModalIsOpen(ApplicationModal.WALLET_DROPDOWN)
useOnClickOutside(ref, isOpen ? closeModal : undefined, [walletRef]) useOnClickOutside(ref, isOpen ? closeModal : undefined, [walletRef])
......
...@@ -3,9 +3,11 @@ import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from './chains' ...@@ -3,9 +3,11 @@ import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from './chains'
describe('chains', () => { describe('chains', () => {
describe('ALL_SUPPORTED_CHAIN_IDS', () => { describe('ALL_SUPPORTED_CHAIN_IDS', () => {
it('contains all the values in the SupportedChainId enum', () => { it('contains all the values in the SupportedChainId enum', () => {
Object.values(SupportedChainId).forEach((chainId) => { Object.values(SupportedChainId)
if (typeof chainId === 'number') expect(ALL_SUPPORTED_CHAIN_IDS.includes(chainId as number)).toBeTruthy() .filter((chainId) => typeof chainId === 'number')
}) .forEach((chainId) => {
expect(ALL_SUPPORTED_CHAIN_IDS.includes(chainId as number)).toBeTruthy()
})
}) })
it('contains no duplicates', () => { it('contains no duplicates', () => {
......
...@@ -36,7 +36,7 @@ const V2_SWAP_HOP_GAS_ESTIMATE = 50_000 ...@@ -36,7 +36,7 @@ const V2_SWAP_HOP_GAS_ESTIMATE = 50_000
* https://github.com/Uniswap/smart-order-router/blob/main/src/routers/alpha-router/gas-models/v2/v2-heuristic-gas-model.ts * https://github.com/Uniswap/smart-order-router/blob/main/src/routers/alpha-router/gas-models/v2/v2-heuristic-gas-model.ts
*/ */
function guesstimateGas(trade: Trade<Currency, Currency, TradeType> | undefined): number | undefined { function guesstimateGas(trade: Trade<Currency, Currency, TradeType> | undefined): number | undefined {
if (!!trade) { if (trade) {
let gas = 0 let gas = 0
for (const { route } of trade.swaps) { for (const { route } of trade.swaps) {
if (route.protocol === Protocol.V2) { if (route.protocol === Protocol.V2) {
......
import * as Sentry from '@sentry/react'
import { Token } from '@uniswap/sdk-core' import { Token } from '@uniswap/sdk-core'
import { SupportedChainId } from 'constants/chains' import { SupportedChainId } from 'constants/chains'
import uriToHttp from 'lib/utils/uriToHttp' import uriToHttp from 'lib/utils/uriToHttp'
...@@ -37,7 +38,9 @@ async function getColorFromToken(token: Token): Promise<string | null> { ...@@ -37,7 +38,9 @@ async function getColorFromToken(token: Token): Promise<string | null> {
try { try {
logoURI = URIForEthToken(address) logoURI = URIForEthToken(address)
return await getColorFromUriPath(logoURI) return await getColorFromUriPath(logoURI)
} catch (e) {} } catch (error) {
Sentry.captureMessage(error.toString())
}
} }
return null return null
......
...@@ -19,7 +19,7 @@ export default function useIsWindowVisible(): boolean { ...@@ -19,7 +19,7 @@ export default function useIsWindowVisible(): boolean {
useEffect(() => { useEffect(() => {
if (!isVisibilityStateSupported()) return undefined if (!isVisibilityStateSupported()) return undefined
setFocused((focused) => isWindowVisible()) setFocused(() => isWindowVisible())
document.addEventListener('visibilitychange', listener) document.addEventListener('visibilitychange', listener)
return () => { return () => {
......
...@@ -9,7 +9,7 @@ import usePrevious from './usePrevious' ...@@ -9,7 +9,7 @@ import usePrevious from './usePrevious'
import useSelectChain from './useSelectChain' import useSelectChain from './useSelectChain'
function getChainIdFromName(name: string) { function getChainIdFromName(name: string) {
const entry = Object.entries(CHAIN_IDS_TO_NAMES).find(([_, n]) => n === name) const entry = Object.entries(CHAIN_IDS_TO_NAMES).find(([, n]) => n === name)
const chainId = entry?.[0] const chainId = entry?.[0]
return chainId ? parseInt(chainId) : undefined return chainId ? parseInt(chainId) : undefined
} }
......
...@@ -29,7 +29,7 @@ import UserUpdater from './state/user/updater' ...@@ -29,7 +29,7 @@ import UserUpdater from './state/user/updater'
import ThemeProvider, { ThemedGlobalStyle } from './theme' import ThemeProvider, { ThemedGlobalStyle } from './theme'
import RadialGradientByChainUpdater from './theme/components/RadialGradientByChainUpdater' import RadialGradientByChainUpdater from './theme/components/RadialGradientByChainUpdater'
if (!!window.ethereum) { if (window.ethereum) {
window.ethereum.autoRefreshOnNetworkChange = false window.ethereum.autoRefreshOnNetworkChange = false
} }
......
...@@ -11,30 +11,30 @@ describe('useInterval', () => { ...@@ -11,30 +11,30 @@ describe('useInterval', () => {
}) })
describe('with a synchronous function', () => { describe('with a synchronous function', () => {
it('it runs on an interval', () => { it('runs on an interval', () => {
jest.useFakeTimers() jest.useFakeTimers()
renderHook(() => useInterval(spy, 100)) renderHook(() => useInterval(spy, 100))
expect(spy).toHaveBeenCalledTimes(1) expect(spy).toHaveBeenCalledTimes(1)
jest.runTimersToTime(100) jest.advanceTimersByTime(100)
expect(spy).toHaveBeenCalledTimes(2) expect(spy).toHaveBeenCalledTimes(2)
}) })
}) })
describe('with an async funtion', () => { describe('with an async funtion', () => {
it('it runs on an interval exclusive of fn resolving', async () => { it('runs on an interval exclusive of fn resolving', async () => {
jest.useFakeTimers() jest.useFakeTimers()
spy.mockImplementation(() => Promise.resolve(undefined)) spy.mockImplementation(() => Promise.resolve(undefined))
renderHook(() => useInterval(spy, 100)) renderHook(() => useInterval(spy, 100))
expect(spy).toHaveBeenCalledTimes(1) expect(spy).toHaveBeenCalledTimes(1)
jest.runTimersToTime(100) jest.advanceTimersByTime(100)
expect(spy).toHaveBeenCalledTimes(1) expect(spy).toHaveBeenCalledTimes(1)
await spy.mock.results[0].value await spy.mock.results[0].value
jest.runTimersToTime(100) jest.advanceTimersByTime(100)
expect(spy).toHaveBeenCalledTimes(2) expect(spy).toHaveBeenCalledTimes(2)
}) })
}) })
......
...@@ -5,7 +5,7 @@ describe.skip('fetchTokenList', () => { ...@@ -5,7 +5,7 @@ describe.skip('fetchTokenList', () => {
it('throws on an invalid list url', async () => { it('throws on an invalid list url', async () => {
const url = 'https://example.com' const url = 'https://example.com'
await expect(fetchTokenList(url, resolver)).rejects.toThrowError(`failed to fetch list: ${url}`) await expect(fetchTokenList(url, resolver)).rejects.toThrow(`failed to fetch list: ${url}`)
expect(resolver).not.toHaveBeenCalled() expect(resolver).not.toHaveBeenCalled()
}) })
......
import { i18n } from '@lingui/core' import { i18n } from '@lingui/core'
import { I18nProvider } from '@lingui/react' import { I18nProvider } from '@lingui/react'
import * as Sentry from '@sentry/react'
import { DEFAULT_LOCALE, SupportedLocale } from 'constants/locales' import { DEFAULT_LOCALE, SupportedLocale } from 'constants/locales'
import { import {
af, af,
...@@ -82,7 +83,9 @@ export async function dynamicActivate(locale: SupportedLocale) { ...@@ -82,7 +83,9 @@ export async function dynamicActivate(locale: SupportedLocale) {
const catalog = await import(`locales/${locale}.js`) const catalog = await import(`locales/${locale}.js`)
// Bundlers will either export it as default or as a named export named default. // Bundlers will either export it as default or as a named export named default.
i18n.load(locale, catalog.messages || catalog.default.messages) i18n.load(locale, catalog.messages || catalog.default.messages)
} catch {} } catch (error) {
Sentry.captureMessage(error.toString())
}
i18n.activate(locale) i18n.activate(locale)
} }
......
...@@ -15,6 +15,7 @@ function expectedOutput(l: SupportedLocale): string { ...@@ -15,6 +15,7 @@ function expectedOutput(l: SupportedLocale): string {
case 'zh-TW': case 'zh-TW':
return `4,000,000.123` return `4,000,000.123`
case 'fr-FR': case 'fr-FR':
// eslint-disable-next-line no-irregular-whitespace
return `4 000 000,123` return `4 000 000,123`
case 'ar-SA': case 'ar-SA':
return `٤٬٠٠٠٬٠٠٠٫١٢٣` return `٤٬٠٠٠٬٠٠٠٫١٢٣`
...@@ -28,6 +29,7 @@ function expectedOutput(l: SupportedLocale): string { ...@@ -28,6 +29,7 @@ function expectedOutput(l: SupportedLocale): string {
case 'ru-RU': case 'ru-RU':
case 'sv-SE': case 'sv-SE':
case 'uk-UA': case 'uk-UA':
// eslint-disable-next-line no-irregular-whitespace
return `4 000 000,123` return `4 000 000,123`
case 'ca-ES': case 'ca-ES':
case 'da-DK': case 'da-DK':
......
...@@ -11,15 +11,18 @@ export default function uriToHttp(uri: string): string[] { ...@@ -11,15 +11,18 @@ export default function uriToHttp(uri: string): string[] {
return [uri] return [uri]
case 'http': case 'http':
return ['https' + uri.substr(4), uri] return ['https' + uri.substr(4), uri]
case 'ipfs': case 'ipfs': {
const hash = uri.match(/^ipfs:(\/\/)?(.*)$/i)?.[2] const hash = uri.match(/^ipfs:(\/\/)?(.*)$/i)?.[2]
return [`https://cloudflare-ipfs.com/ipfs/${hash}/`, `https://ipfs.io/ipfs/${hash}/`] return [`https://cloudflare-ipfs.com/ipfs/${hash}/`, `https://ipfs.io/ipfs/${hash}/`]
case 'ipns': }
case 'ipns': {
const name = uri.match(/^ipns:(\/\/)?(.*)$/i)?.[2] const name = uri.match(/^ipns:(\/\/)?(.*)$/i)?.[2]
return [`https://cloudflare-ipfs.com/ipns/${name}/`, `https://ipfs.io/ipns/${name}/`] return [`https://cloudflare-ipfs.com/ipns/${name}/`, `https://ipfs.io/ipns/${name}/`]
case 'ar': }
case 'ar': {
const tx = uri.match(/^ar:(\/\/)?(.*)$/i)?.[2] const tx = uri.match(/^ar:(\/\/)?(.*)$/i)?.[2]
return [`https://arweave.net/${tx}`] return [`https://arweave.net/${tx}`]
}
default: default:
return [] return []
} }
......
...@@ -186,7 +186,7 @@ export const ListingButton = ({ onClick, buttonText, showWarningOverride = false ...@@ -186,7 +186,7 @@ export const ListingButton = ({ onClick, buttonText, showWarningOverride = false
<Box marginLeft="4" marginRight="8"> <Box marginLeft="4" marginRight="8">
{warningMessage} {warningMessage}
</Box> </Box>
{!!disableListButton ? ( {disableListButton ? (
<Box paddingTop="6"> <Box paddingTop="6">
<XMarkIcon fill={themeVars.colors.textSecondary} height="20" width="20" /> <XMarkIcon fill={themeVars.colors.textSecondary} height="20" width="20" />
</Box> </Box>
......
...@@ -316,7 +316,7 @@ interface RankingProps { ...@@ -316,7 +316,7 @@ interface RankingProps {
details?: boolean details?: boolean
} }
const Ranking = ({ details, rarity, collectionName, rarityVerified }: RankingProps) => { const Ranking = ({ rarity, collectionName, rarityVerified }: RankingProps) => {
const rarityProviderLogo = getRarityProviderLogo(rarity.source) const rarityProviderLogo = getRarityProviderLogo(rarity.source)
return ( return (
......
...@@ -577,7 +577,7 @@ interface ProfileNftDetailsProps { ...@@ -577,7 +577,7 @@ interface ProfileNftDetailsProps {
const ProfileNftDetails = ({ asset, hideDetails }: ProfileNftDetailsProps) => { const ProfileNftDetails = ({ asset, hideDetails }: ProfileNftDetailsProps) => {
const assetName = () => { const assetName = () => {
if (!asset.name && !asset.tokenId) return if (!asset.name && !asset.tokenId) return
return !!asset.name ? asset.name : `#${asset.tokenId}` return asset.name ? asset.name : `#${asset.tokenId}`
} }
const shouldShowUserListedPrice = !asset.notForSale && asset.asset_contract.tokenType !== NftStandard.Erc1155 const shouldShowUserListedPrice = !asset.notForSale && asset.asset_contract.tokenType !== NftStandard.Erc1155
......
...@@ -387,12 +387,11 @@ const StatsRow = ({ stats, isMobile, ...props }: { stats: GenieCollection; isMob ...@@ -387,12 +387,11 @@ const StatsRow = ({ stats, isMobile, ...props }: { stats: GenieCollection; isMob
{totalSupplyStr} {totalSupplyStr}
</StatsItem> </StatsItem>
) : null} ) : null}
{Boolean(uniqueOwnersPercentage && stats.standard !== TokenType.ERC1155) ? ( {uniqueOwnersPercentage && stats.standard !== TokenType.ERC1155 ? (
<StatsItem label="Unique owners" shouldHide={isSmallContainer ?? false}> <StatsItem label="Unique owners" shouldHide={isSmallContainer ?? false}>
{uniqueOwnersPercentage}% {uniqueOwnersPercentage}%
</StatsItem> </StatsItem>
) : null} ) : null}
{stats.stats?.total_listings && stats.standard !== TokenType.ERC1155 ? ( {stats.stats?.total_listings && stats.standard !== TokenType.ERC1155 ? (
<StatsItem label="Listed" shouldHide={isSmallContainer ?? false}> <StatsItem label="Listed" shouldHide={isSmallContainer ?? false}>
{listedPercentageStr}% {listedPercentageStr}%
......
...@@ -263,7 +263,7 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => { ...@@ -263,7 +263,7 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
return MediaType.Audio return MediaType.Audio
} else if (isVideo(asset.animationUrl ?? '')) { } else if (isVideo(asset.animationUrl ?? '')) {
return MediaType.Video return MediaType.Video
} else if (!!asset.animationUrl) { } else if (asset.animationUrl) {
return MediaType.Embed return MediaType.Embed
} }
return MediaType.Image return MediaType.Image
......
...@@ -3,7 +3,8 @@ import styled, { useTheme } from 'styled-components/macro' ...@@ -3,7 +3,8 @@ import styled, { useTheme } from 'styled-components/macro'
import { themeVars, vars } from '../css/sprinkles.css' import { themeVars, vars } from '../css/sprinkles.css'
type SVGProps = React.SVGProps<SVGSVGElement> // ESLint reports `fill` is missing, whereas it exists on an SVGProps type
type SVGProps = React.SVGProps<SVGSVGElement> & { fill?: string }
export const UniIcon = (props: SVGProps) => ( export const UniIcon = (props: SVGProps) => (
<svg {...props} fill="none" xmlns="http://www.w3.org/2000/svg"> <svg {...props} fill="none" xmlns="http://www.w3.org/2000/svg">
...@@ -599,7 +600,7 @@ export const ActivityTransferIcon = (props: SVGProps) => ( ...@@ -599,7 +600,7 @@ export const ActivityTransferIcon = (props: SVGProps) => (
</svg> </svg>
) )
export const ActivityExternalLinkIcon = (_props: SVGProps) => ( export const ActivityExternalLinkIcon = () => (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-outside-1_3799_46574" maskUnits="userSpaceOnUse" x="2" y="2" width="15" height="15" fill="black"> <mask id="path-1-outside-1_3799_46574" maskUnits="userSpaceOnUse" x="2" y="2" width="15" height="15" fill="black">
<rect fill="white" x="2" y="2" width="15" height="15" /> <rect fill="white" x="2" y="2" width="15" height="15" />
......
...@@ -70,7 +70,7 @@ export const SetDurationModal = () => { ...@@ -70,7 +70,7 @@ export const SetDurationModal = () => {
const [errorState, setErrorState] = useState(ErrorState.valid) const [errorState, setErrorState] = useState(ErrorState.valid)
const setGlobalExpiration = useSellAsset((state) => state.setGlobalExpiration) const setGlobalExpiration = useSellAsset((state) => state.setGlobalExpiration)
const setCustomExpiration = (event: React.ChangeEvent<HTMLInputElement>) => { const setCustomExpiration = (event: React.ChangeEvent<HTMLInputElement>) => {
setAmount(!!event.target.value.length ? event.target.value : '') setAmount(event.target.value.length ? event.target.value : '')
setDuration(displayDuration) setDuration(displayDuration)
} }
const selectDuration = (duration: Duration) => { const selectDuration = (duration: Duration) => {
......
...@@ -90,7 +90,7 @@ export const ProfilePage = () => { ...@@ -90,7 +90,7 @@ export const ProfilePage = () => {
isFetchingNextPage, isFetchingNextPage,
isSuccess, isSuccess,
} = useInfiniteQuery(['ownerCollections', { address }], getOwnerCollections, { } = useInfiniteQuery(['ownerCollections', { address }], getOwnerCollections, {
getNextPageParam: (lastGroup, _allGroups) => (lastGroup.data.length === 0 ? undefined : lastGroup.nextPage), getNextPageParam: (lastGroup) => (lastGroup.data.length === 0 ? undefined : lastGroup.nextPage),
refetchInterval: 15000, refetchInterval: 15000,
refetchIntervalInBackground: false, refetchIntervalInBackground: false,
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
......
...@@ -25,7 +25,7 @@ export const useNFTSelect = create<SelectNFTState>()( ...@@ -25,7 +25,7 @@ export const useNFTSelect = create<SelectNFTState>()(
selectNFT: (nft) => selectNFT: (nft) =>
set(({ selectedNFTs }) => { set(({ selectedNFTs }) => {
if (selectedNFTs.length === 0) return { selectedNFTs: [nft] } if (selectedNFTs.length === 0) return { selectedNFTs: [nft] }
else if (!!selectedNFTs.find((x) => x.id === nft.id)) else if (selectedNFTs.some((x) => x.id === nft.id))
return { selectedNFTs: selectedNFTs.filter((n) => n.id !== nft.id) } return { selectedNFTs: selectedNFTs.filter((n) => n.id !== nft.id) }
else return { selectedNFTs: [...selectedNFTs, nft] } else return { selectedNFTs: [...selectedNFTs, nft] }
}), }),
......
...@@ -39,7 +39,7 @@ export const useWalletCollections = create<WalletCollectionState>()( ...@@ -39,7 +39,7 @@ export const useWalletCollections = create<WalletCollectionState>()(
setCollectionFilters: (address) => setCollectionFilters: (address) =>
set(({ collectionFilters }) => { set(({ collectionFilters }) => {
if (collectionFilters.length === 0) return { collectionFilters: [address] } if (collectionFilters.length === 0) return { collectionFilters: [address] }
else if (!!collectionFilters.find((x) => x === address)) else if (collectionFilters.some((x) => x === address))
return { collectionFilters: collectionFilters.filter((n) => n !== address) } return { collectionFilters: collectionFilters.filter((n) => n !== address) }
else return { collectionFilters: [...collectionFilters, address] } else return { collectionFilters: [...collectionFilters, address] }
}), }),
......
...@@ -49,7 +49,7 @@ const ProfileContent = () => { ...@@ -49,7 +49,7 @@ const ProfileContent = () => {
{/* <Head> TODO: figure out metadata tagging {/* <Head> TODO: figure out metadata tagging
<title>Genie | Sell</title> <title>Genie | Sell</title>
</Head> */} </Head> */}
{!!account ? ( {account ? (
<Box style={{ width: `calc(100% - ${cartExpanded ? SHOPPING_BAG_WIDTH : 0}px)` }}> <Box style={{ width: `calc(100% - ${cartExpanded ? SHOPPING_BAG_WIDTH : 0}px)` }}>
{sellPageState === ProfilePageStateType.VIEWING ? <ProfilePage /> : <ListPage />} {sellPageState === ProfilePageStateType.VIEWING ? <ProfilePage /> : <ListPage />}
</Box> </Box>
......
...@@ -168,7 +168,7 @@ export async function signListing( ...@@ -168,7 +168,7 @@ export async function signListing(
else setStatus(ListingStatus.FAILED) else setStatus(ListingStatus.FAILED)
return false return false
} }
case 'LooksRare': case 'LooksRare': {
const addresses = addressesByNetwork[SupportedChainId.MAINNET] const addresses = addressesByNetwork[SupportedChainId.MAINNET]
const currentTime = Math.round(Date.now() / 1000) const currentTime = Math.round(Date.now() / 1000)
const makerOrder: MakerOrder = { const makerOrder: MakerOrder = {
...@@ -235,8 +235,8 @@ export async function signListing( ...@@ -235,8 +235,8 @@ export async function signListing(
else setStatus(ListingStatus.FAILED) else setStatus(ListingStatus.FAILED)
return false return false
} }
}
case 'X2Y2': case 'X2Y2': {
const orderItem: OfferItem = { const orderItem: OfferItem = {
price: parseEther(listingPrice.toString()), price: parseEther(listingPrice.toString()),
tokens: [ tokens: [
...@@ -269,7 +269,7 @@ export async function signListing( ...@@ -269,7 +269,7 @@ export async function signListing(
else setStatus(ListingStatus.FAILED) else setStatus(ListingStatus.FAILED)
return false return false
} }
}
default: default:
return false return false
} }
......
...@@ -137,12 +137,11 @@ export const syncLocalFiltersWithURL = (state: CollectionFilters) => { ...@@ -137,12 +137,11 @@ export const syncLocalFiltersWithURL = (state: CollectionFilters) => {
const query: Record<string, any> = {} const query: Record<string, any> = {}
urlFilterItems.forEach((key) => { urlFilterItems.forEach((key) => {
switch (key) { switch (key) {
case 'traits': case 'traits': {
const traits = state.traits.map(({ trait_type, trait_value }) => `("${trait_type}","${trait_value}")`) const traits = state.traits.map(({ trait_type, trait_value }) => `("${trait_type}","${trait_value}")`)
query['traits'] = traits query['traits'] = traits
break break
}
case 'all': case 'all':
query['all'] = !state.buyNow query['all'] = !state.buyNow
break break
......
...@@ -511,7 +511,7 @@ export function PositionPage() { ...@@ -511,7 +511,7 @@ export function PositionPage() {
provider, provider,
]) ])
const owner = useSingleCallResult(!!tokenId ? positionManager : null, 'ownerOf', [tokenId]).result?.[0] const owner = useSingleCallResult(tokenId ? positionManager : null, 'ownerOf', [tokenId]).result?.[0]
const ownsNFT = owner === account || positionDetails?.operator === account const ownsNFT = owner === account || positionDetails?.operator === account
const feeValueUpper = inverted ? feeValue0 : feeValue1 const feeValueUpper = inverted ? feeValue0 : feeValue1
......
...@@ -172,7 +172,7 @@ export default function Swap({ className }: { className?: string }) { ...@@ -172,7 +172,7 @@ export default function Swap({ className }: { className?: string }) {
urlLoadedTokens && urlLoadedTokens &&
urlLoadedTokens urlLoadedTokens
.filter((token: Token) => { .filter((token: Token) => {
return !Boolean(token.address in defaultTokens) return !(token.address in defaultTokens)
}) })
.filter((token: Token) => { .filter((token: Token) => {
// Any token addresses that are loaded from the shorthands map do not need to show the import URL // Any token addresses that are loaded from the shorthands map do not need to show the import URL
......
...@@ -106,15 +106,15 @@ describe('document', () => { ...@@ -106,15 +106,15 @@ describe('document', () => {
let fetched: Response let fetched: Response
const FETCHED_ETAGS = 'fetched' const FETCHED_ETAGS = 'fetched'
const expectFetchToHaveBeenCalledWithRequestUrl = () => {
expect(fetch).toHaveBeenCalledWith(requestUrl, expect.anything())
}
beforeEach(() => { beforeEach(() => {
fetched = new Response('test_body', { headers: { etag: FETCHED_ETAGS } }) fetched = new Response('test_body', { headers: { etag: FETCHED_ETAGS } })
fetch.mockReturnValueOnce(fetched) fetch.mockReturnValueOnce(fetched)
}) })
afterEach(() => {
expect(fetch).toHaveBeenCalledWith(requestUrl, expect.anything())
})
describe('with a cached response', () => { describe('with a cached response', () => {
let cached: Response let cached: Response
...@@ -132,6 +132,7 @@ describe('document', () => { ...@@ -132,6 +132,7 @@ describe('document', () => {
await handleDocument(options) await handleDocument(options)
const abortSignal = fetch.mock.calls[0][1].signal const abortSignal = fetch.mock.calls[0][1].signal
expect(abortSignal.aborted).toBeTruthy() expect(abortSignal.aborted).toBeTruthy()
expectFetchToHaveBeenCalledWithRequestUrl()
}) })
it('returns the cached response', async () => { it('returns the cached response', async () => {
...@@ -141,18 +142,21 @@ describe('document', () => { ...@@ -141,18 +142,21 @@ describe('document', () => {
expect(await response.text()).toBe( expect(await response.text()).toBe(
'<html><head></head><body><script>window.__isDocumentCached=true</script>mock</body></html>' '<html><head></head><body><script>window.__isDocumentCached=true</script>mock</body></html>'
) )
expectFetchToHaveBeenCalledWithRequestUrl()
}) })
}) })
it(`returns the fetched response with mismatched etags`, async () => { it(`returns the fetched response with mismatched etags`, async () => {
const response = await handleDocument(options) const response = await handleDocument(options)
expect(response.body).toBe(fetched.body) expect(response.body).toBe(fetched.body)
expectFetchToHaveBeenCalledWithRequestUrl()
}) })
}) })
it(`returns the fetched response with no cached response`, async () => { it(`returns the fetched response with no cached response`, async () => {
const response = await handleDocument(options) const response = await handleDocument(options)
expect(response.body).toBe(fetched.body) expect(response.body).toBe(fetched.body)
expectFetchToHaveBeenCalledWithRequestUrl()
}) })
}) })
}) })
......
...@@ -49,7 +49,7 @@ type HandlerContext = { ...@@ -49,7 +49,7 @@ type HandlerContext = {
* *
* In addition, this handler may serve an offline document if there is no internet connection. * In addition, this handler may serve an offline document if there is no internet connection.
*/ */
export async function handleDocument(this: HandlerContext, { event, request }: RouteHandlerCallbackOptions) { export async function handleDocument(this: HandlerContext, { request }: RouteHandlerCallbackOptions) {
// If we are offline, serve the offline document. // If we are offline, serve the offline document.
if ('onLine' in navigator && !navigator.onLine) return this?.offlineDocument?.clone() || fetch(request) if ('onLine' in navigator && !navigator.onLine) return this?.offlineDocument?.clone() || fetch(request)
......
...@@ -64,8 +64,7 @@ export function useFiatOnrampAvailability(shouldCheck: boolean, callback?: () => ...@@ -64,8 +64,7 @@ export function useFiatOnrampAvailability(shouldCheck: boolean, callback?: () =>
setError('Error, try again later.') setError('Error, try again later.')
dispatch(setFiatOnrampAvailability(false)) dispatch(setFiatOnrampAvailability(false))
} finally { } finally {
if (stale) return if (!stale) setLoading(false)
setLoading(false)
} }
} }
...@@ -88,7 +87,7 @@ export function useToggleModal(modal: ApplicationModal): () => void { ...@@ -88,7 +87,7 @@ export function useToggleModal(modal: ApplicationModal): () => void {
return useCallback(() => dispatch(setOpenModal(isOpen ? null : modal)), [dispatch, modal, isOpen]) return useCallback(() => dispatch(setOpenModal(isOpen ? null : modal)), [dispatch, modal, isOpen])
} }
export function useCloseModal(_modal: ApplicationModal): () => void { export function useCloseModal(): () => void {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
return useCallback(() => dispatch(setOpenModal(null)), [dispatch]) return useCallback(() => dispatch(setOpenModal(null)), [dispatch])
} }
......
...@@ -6,7 +6,7 @@ import { useAppDispatch } from 'state/hooks' ...@@ -6,7 +6,7 @@ import { useAppDispatch } from 'state/hooks'
import { supportedChainId } from 'utils/supportedChainId' import { supportedChainId } from 'utils/supportedChainId'
import { useCloseModal } from './hooks' import { useCloseModal } from './hooks'
import { ApplicationModal, updateChainId } from './reducer' import { updateChainId } from './reducer'
export default function Updater(): null { export default function Updater(): null {
const { account, chainId, provider } = useWeb3React() const { account, chainId, provider } = useWeb3React()
...@@ -15,7 +15,7 @@ export default function Updater(): null { ...@@ -15,7 +15,7 @@ export default function Updater(): null {
const [activeChainId, setActiveChainId] = useState(chainId) const [activeChainId, setActiveChainId] = useState(chainId)
const closeModal = useCloseModal(ApplicationModal.WALLET_DROPDOWN) const closeModal = useCloseModal()
const previousAccountValue = useRef(account) const previousAccountValue = useRef(account)
useEffect(() => { useEffect(() => {
if (account && account !== previousAccountValue.current) { if (account && account !== previousAccountValue.current) {
......
...@@ -386,18 +386,19 @@ describe('list reducer', () => { ...@@ -386,18 +386,19 @@ describe('list reducer', () => {
it('each of those initialized lists is empty', () => { it('each of those initialized lists is empty', () => {
const byUrl = store.getState().byUrl const byUrl = store.getState().byUrl
// note we don't expect the uniswap default list to be prepopulated Object.entries(byUrl)
// this is ok. // We don't expect the Uniswap default list to be prepopulated
Object.keys(byUrl).forEach((url) => { .filter(
if (url !== 'https://unpkg.com/@uniswap/default-token-list@latest/uniswap-default.tokenlist.json') { ([url]) => url !== 'https://unpkg.com/@uniswap/default-token-list@latest/uniswap-default.tokenlist.json'
expect(byUrl[url]).toEqual({ )
.forEach(([, state]) => {
expect(state).toEqual({
error: null, error: null,
current: null, current: null,
loadingRequestId: null, loadingRequestId: null,
pendingUpdate: null, pendingUpdate: null,
}) })
} })
})
}) })
it('sets initialized lists', () => { it('sets initialized lists', () => {
......
...@@ -60,7 +60,7 @@ export default function Updater(): null { ...@@ -60,7 +60,7 @@ export default function Updater(): null {
case VersionUpgrade.NONE: case VersionUpgrade.NONE:
throw new Error('unexpected no version bump') throw new Error('unexpected no version bump')
case VersionUpgrade.PATCH: case VersionUpgrade.PATCH:
case VersionUpgrade.MINOR: case VersionUpgrade.MINOR: {
const min = minVersionBump(list.current.tokens, list.pendingUpdate.tokens) const min = minVersionBump(list.current.tokens, list.pendingUpdate.tokens)
// automatically update minor/patch as long as bump matches the min update // automatically update minor/patch as long as bump matches the min update
if (bump >= min) { if (bump >= min) {
...@@ -71,7 +71,7 @@ export default function Updater(): null { ...@@ -71,7 +71,7 @@ export default function Updater(): null {
) )
} }
break break
}
// update any active or inactive lists // update any active or inactive lists
case VersionUpgrade.MAJOR: case VersionUpgrade.MAJOR:
dispatch(acceptListUpdate(listUrl)) dispatch(acceptListUpdate(listUrl))
......
...@@ -11,8 +11,8 @@ interface TagInfo extends TagDetails { ...@@ -11,8 +11,8 @@ interface TagInfo extends TagDetails {
* Token instances created from token info on a token list. * Token instances created from token info on a token list.
*/ */
export class WrappedTokenInfo implements Token { export class WrappedTokenInfo implements Token {
public readonly isNative: false = false public readonly isNative = false as const
public readonly isToken: true = true public readonly isToken = true as const
public readonly list?: TokenList public readonly list?: TokenList
public readonly tokenInfo: TokenInfo public readonly tokenInfo: TokenInfo
......
...@@ -92,13 +92,13 @@ export interface ExactOutputSwapTransactionInfo extends BaseSwapTransactionInfo ...@@ -92,13 +92,13 @@ export interface ExactOutputSwapTransactionInfo extends BaseSwapTransactionInfo
maximumInputCurrencyAmountRaw: string maximumInputCurrencyAmountRaw: string
} }
export interface DepositLiquidityStakingTransactionInfo { interface DepositLiquidityStakingTransactionInfo {
type: TransactionType.DEPOSIT_LIQUIDITY_STAKING type: TransactionType.DEPOSIT_LIQUIDITY_STAKING
token0Address: string token0Address: string
token1Address: string token1Address: string
} }
export interface WithdrawLiquidityStakingTransactionInfo { interface WithdrawLiquidityStakingTransactionInfo {
type: TransactionType.WITHDRAW_LIQUIDITY_STAKING type: TransactionType.WITHDRAW_LIQUIDITY_STAKING
token0Address: string token0Address: string
token1Address: string token1Address: string
...@@ -164,7 +164,7 @@ export interface RemoveLiquidityV3TransactionInfo { ...@@ -164,7 +164,7 @@ export interface RemoveLiquidityV3TransactionInfo {
expectedAmountQuoteRaw: string expectedAmountQuoteRaw: string
} }
export interface SubmitProposalTransactionInfo { interface SubmitProposalTransactionInfo {
type: TransactionType.SUBMIT_PROPOSAL type: TransactionType.SUBMIT_PROPOSAL
} }
......
...@@ -48,7 +48,7 @@ export default function RadialGradientByChainUpdater(): null { ...@@ -48,7 +48,7 @@ export default function RadialGradientByChainUpdater(): null {
switch (chainId) { switch (chainId) {
case SupportedChainId.ARBITRUM_ONE: case SupportedChainId.ARBITRUM_ONE:
case SupportedChainId.ARBITRUM_RINKEBY: case SupportedChainId.ARBITRUM_RINKEBY: {
setBackground(backgroundResetStyles) setBackground(backgroundResetStyles)
const arbitrumLightGradient = const arbitrumLightGradient =
'radial-gradient(100% 100% at 50% 0%, rgba(205, 232, 251, 0.7) 0%, rgba(252, 243, 249, 0.6536) 49.48%, rgba(255, 255, 255, 0) 100%), #FFFFFF' 'radial-gradient(100% 100% at 50% 0%, rgba(205, 232, 251, 0.7) 0%, rgba(252, 243, 249, 0.6536) 49.48%, rgba(255, 255, 255, 0) 100%), #FFFFFF'
...@@ -56,8 +56,9 @@ export default function RadialGradientByChainUpdater(): null { ...@@ -56,8 +56,9 @@ export default function RadialGradientByChainUpdater(): null {
'radial-gradient(100% 100% at 50% 0%, rgba(10, 41, 75, 0.7) 0%, rgba(34, 30, 48, 0.6536) 49.48%, rgba(31, 33, 40, 0) 100%), #0D0E0E' 'radial-gradient(100% 100% at 50% 0%, rgba(10, 41, 75, 0.7) 0%, rgba(34, 30, 48, 0.6536) 49.48%, rgba(31, 33, 40, 0) 100%), #0D0E0E'
backgroundRadialGradientElement.style.background = darkMode ? arbitrumDarkGradient : arbitrumLightGradient backgroundRadialGradientElement.style.background = darkMode ? arbitrumDarkGradient : arbitrumLightGradient
break break
}
case SupportedChainId.OPTIMISM: case SupportedChainId.OPTIMISM:
case SupportedChainId.OPTIMISM_GOERLI: case SupportedChainId.OPTIMISM_GOERLI: {
setBackground(backgroundResetStyles) setBackground(backgroundResetStyles)
const optimismLightGradient = const optimismLightGradient =
'radial-gradient(100% 100% at 50% 0%, rgba(255, 251, 242, 0.8) 0%, rgba(255, 244, 249, 0.6958) 50.52%, rgba(255, 255, 255, 0) 100%), #FFFFFF' 'radial-gradient(100% 100% at 50% 0%, rgba(255, 251, 242, 0.8) 0%, rgba(255, 244, 249, 0.6958) 50.52%, rgba(255, 255, 255, 0) 100%), #FFFFFF'
...@@ -65,8 +66,9 @@ export default function RadialGradientByChainUpdater(): null { ...@@ -65,8 +66,9 @@ export default function RadialGradientByChainUpdater(): null {
'radial-gradient(100% 100% at 50% 0%, rgba(62, 46, 56, 0.8) 0%, rgba(44, 31, 45, 0.6958) 50.52%, rgba(31, 33, 40, 0) 100%), #0D0E0E' 'radial-gradient(100% 100% at 50% 0%, rgba(62, 46, 56, 0.8) 0%, rgba(44, 31, 45, 0.6958) 50.52%, rgba(31, 33, 40, 0) 100%), #0D0E0E'
backgroundRadialGradientElement.style.background = darkMode ? optimismDarkGradient : optimismLightGradient backgroundRadialGradientElement.style.background = darkMode ? optimismDarkGradient : optimismLightGradient
break break
}
case SupportedChainId.POLYGON: case SupportedChainId.POLYGON:
case SupportedChainId.POLYGON_MUMBAI: case SupportedChainId.POLYGON_MUMBAI: {
setBackground(backgroundResetStyles) setBackground(backgroundResetStyles)
const polygonLightGradient = const polygonLightGradient =
'radial-gradient(100% 100% at 50% 0%, rgba(130, 71, 229, 0.2) 0%, rgba(200, 168, 255, 0.05) 52.6%, rgba(0, 0, 0, 0) 100%), #FFFFFF' 'radial-gradient(100% 100% at 50% 0%, rgba(130, 71, 229, 0.2) 0%, rgba(200, 168, 255, 0.05) 52.6%, rgba(0, 0, 0, 0) 100%), #FFFFFF'
...@@ -74,8 +76,9 @@ export default function RadialGradientByChainUpdater(): null { ...@@ -74,8 +76,9 @@ export default function RadialGradientByChainUpdater(): null {
'radial-gradient(100% 100% at 50% 0%, rgba(130, 71, 229, 0.2) 0%, rgba(200, 168, 255, 0.05) 52.6%, rgba(0, 0, 0, 0) 100%), #0D0E0E' 'radial-gradient(100% 100% at 50% 0%, rgba(130, 71, 229, 0.2) 0%, rgba(200, 168, 255, 0.05) 52.6%, rgba(0, 0, 0, 0) 100%), #0D0E0E'
backgroundRadialGradientElement.style.background = darkMode ? polygonDarkGradient : polygonLightGradient backgroundRadialGradientElement.style.background = darkMode ? polygonDarkGradient : polygonLightGradient
break break
}
case SupportedChainId.CELO: case SupportedChainId.CELO:
case SupportedChainId.CELO_ALFAJORES: case SupportedChainId.CELO_ALFAJORES: {
setBackground(backgroundResetStyles) setBackground(backgroundResetStyles)
const celoLightGradient = const celoLightGradient =
'radial-gradient(100% 100% at 50% 0%, rgba(186, 228, 210, 0.7) 0%, rgba(252, 243, 249, 0.6536) 49.48%, rgba(255, 255, 255, 0) 100%), #FFFFFF' 'radial-gradient(100% 100% at 50% 0%, rgba(186, 228, 210, 0.7) 0%, rgba(252, 243, 249, 0.6536) 49.48%, rgba(255, 255, 255, 0) 100%), #FFFFFF'
...@@ -83,12 +86,14 @@ export default function RadialGradientByChainUpdater(): null { ...@@ -83,12 +86,14 @@ export default function RadialGradientByChainUpdater(): null {
'radial-gradient(100% 100% at 50% 0%, rgba(20, 49, 37, 0.29) 0%, rgba(12, 31, 23, 0.6536) 49.48%, rgba(31, 33, 40, 0) 100%, rgba(31, 33, 40, 0) 100%), #0D0E0E' 'radial-gradient(100% 100% at 50% 0%, rgba(20, 49, 37, 0.29) 0%, rgba(12, 31, 23, 0.6536) 49.48%, rgba(31, 33, 40, 0) 100%, rgba(31, 33, 40, 0) 100%), #0D0E0E'
backgroundRadialGradientElement.style.background = darkMode ? celoDarkGradient : celoLightGradient backgroundRadialGradientElement.style.background = darkMode ? celoDarkGradient : celoLightGradient
break break
default: }
default: {
setBackground(initialStyles) setBackground(initialStyles)
const defaultLightGradient = const defaultLightGradient =
'radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF' 'radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF'
const defaultDarkGradient = 'linear-gradient(180deg, #202738 0%, #070816 100%)' const defaultDarkGradient = 'linear-gradient(180deg, #202738 0%, #070816 100%)'
backgroundRadialGradientElement.style.background = darkMode ? defaultDarkGradient : defaultLightGradient backgroundRadialGradientElement.style.background = darkMode ? defaultDarkGradient : defaultLightGradient
}
} }
}, [darkMode, chainId, isNftPage]) }, [darkMode, chainId, isNftPage])
return null return null
......
...@@ -17,6 +17,17 @@ export const MEDIA_WIDTHS = { ...@@ -17,6 +17,17 @@ export const MEDIA_WIDTHS = {
deprecated_upToLarge: 1280, deprecated_upToLarge: 1280,
} }
const deprecated_mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys(
MEDIA_WIDTHS
).reduce((acc, size) => {
acc[size] = (a: any, b: any, c: any) => css`
@media (max-width: ${(MEDIA_WIDTHS as any)[size]}px) {
${css(a, b, c)}
}
`
return acc
}, {} as any)
export const BREAKPOINTS = { export const BREAKPOINTS = {
xs: 396, xs: 396,
sm: 640, sm: 640,
...@@ -53,17 +64,6 @@ const fonts = { ...@@ -53,17 +64,6 @@ const fonts = {
code: 'courier, courier new, serif', code: 'courier, courier new, serif',
} }
const deprecated_mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys(
MEDIA_WIDTHS
).reduce((accumulator, size) => {
;(accumulator as any)[size] = (a: any, b: any, c: any) => css`
@media (max-width: ${(MEDIA_WIDTHS as any)[size]}px) {
${css(a, b, c)}
}
`
return accumulator
}, {}) as any
function getSettings(darkMode: boolean) { function getSettings(darkMode: boolean) {
return { return {
grids: { grids: {
......
...@@ -7,15 +7,10 @@ describe('#anonymizeLink', () => { ...@@ -7,15 +7,10 @@ describe('#anonymizeLink', () => {
it('anonymizes any addresses in etherscan urls', () => { it('anonymizes any addresses in etherscan urls', () => {
expect(anonymizeLink('https://etherscan.io/address/0xabcd')).toEqual('https://etherscan.io/address/***') expect(anonymizeLink('https://etherscan.io/address/0xabcd')).toEqual('https://etherscan.io/address/***')
}) })
it('anonymizes any addresses in etherscan urls', () => {
expect(anonymizeLink('https://etherscan.io/address/0xabcd')).toEqual('https://etherscan.io/address/***')
})
it('anonymizes any addresses in testnet etherscan urls', () => { it('anonymizes any addresses in testnet etherscan urls', () => {
expect(anonymizeLink('https://goerli.etherscan.io/address/0xabcd')).toEqual( expect(anonymizeLink('https://goerli.etherscan.io/address/0xabcd')).toEqual(
'https://goerli.etherscan.io/address/***' 'https://goerli.etherscan.io/address/***'
) )
})
it('anonymizes any addresses in testnet etherscan urls', () => {
expect(anonymizeLink('https://ropsten.etherscan.io/address/0xabcd')).toEqual( expect(anonymizeLink('https://ropsten.etherscan.io/address/0xabcd')).toEqual(
'https://ropsten.etherscan.io/address/***' 'https://ropsten.etherscan.io/address/***'
) )
......
...@@ -88,6 +88,7 @@ describe('formatTransactionAmount', () => { ...@@ -88,6 +88,7 @@ describe('formatTransactionAmount', () => {
expect(formatTransactionAmount(1234567.8901)).toEqual('1,234,567.89') expect(formatTransactionAmount(1234567.8901)).toEqual('1,234,567.89')
}) })
it('Number ≥ 1M extra long', () => { it('Number ≥ 1M extra long', () => {
// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
expect(formatTransactionAmount(1234567890123456.789)).toEqual('1.234568e+15') expect(formatTransactionAmount(1234567890123456.789)).toEqual('1.234568e+15')
}) })
}) })
......
...@@ -10,7 +10,7 @@ function waitRandom(min: number, max: number): Promise<void> { ...@@ -10,7 +10,7 @@ function waitRandom(min: number, max: number): Promise<void> {
* This error is thrown if the function is cancelled before completing * This error is thrown if the function is cancelled before completing
*/ */
class CancelledError extends Error { class CancelledError extends Error {
public isCancelledError: true = true public isCancelledError = true as const
constructor() { constructor() {
super('Cancelled') super('Cancelled')
} }
...@@ -20,7 +20,7 @@ class CancelledError extends Error { ...@@ -20,7 +20,7 @@ class CancelledError extends Error {
* Throw this error if the function should retry * Throw this error if the function should retry
*/ */
export class RetryableError extends Error { export class RetryableError extends Error {
public isRetryableError: true = true public isRetryableError = true as const
} }
export interface RetryOptions { export interface RetryOptions {
...@@ -42,8 +42,10 @@ export function retry<T>( ...@@ -42,8 +42,10 @@ export function retry<T>(
): { promise: Promise<T>; cancel: () => void } { ): { promise: Promise<T>; cancel: () => void } {
let completed = false let completed = false
let rejectCancelled: (error: Error) => void let rejectCancelled: (error: Error) => void
// eslint-disable-next-line no-async-promise-executor
const promise = new Promise<T>(async (resolve, reject) => { const promise = new Promise<T>(async (resolve, reject) => {
rejectCancelled = reject rejectCancelled = reject
// eslint-disable-next-line no-constant-condition
while (true) { while (true) {
let result: T let result: T
try { try {
......
...@@ -15,7 +15,7 @@ export function swapErrorToUserReadableMessage(error: any): string { ...@@ -15,7 +15,7 @@ export function swapErrorToUserReadableMessage(error: any): string {
} }
} }
while (Boolean(error)) { while (error) {
reason = error.reason ?? error.message ?? reason reason = error.reason ?? error.message ?? reason
error = error.error ?? error.data?.originalError error = error.error ?? error.data?.originalError
} }
......
This diff is collapsed.
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