From d5c4ee03426f023289002f2a4d50ea15c22733e3 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 22 Nov 2021 10:55:25 -0800 Subject: [PATCH] feat: display an ENS avatar (#2806) * feat: ens avatar resolution * chore: uninstall @davatar/react * fix: add avatar alt * feat: support data uris * feat: support arweave uris * feat: support erc721 avatars * feat: support erc1155 avatars * fix: jazzicon integration * fix: clean usage of status icon * fix: fix jazzicon svg offset * refactor: share status icon component * fix: pass memoized args to multicall --- package.json | 2 +- src/abis/erc1155.json | 49 +++++++++ src/abis/erc721.json | 40 ++++++++ src/components/AccountDetails/index.tsx | 74 ++++--------- src/components/Identicon/StatusIcon.tsx | 25 +++++ src/components/Identicon/index.tsx | 45 +++++--- src/components/Web3Status/index.tsx | 45 ++------ src/hooks/useContract.ts | 12 ++- src/hooks/useENSAvatar.ts | 131 ++++++++++++++++++++++++ src/utils/uriToHttp.ts | 7 +- yarn.lock | 65 ++++++------ 11 files changed, 354 insertions(+), 141 deletions(-) create mode 100644 src/abis/erc1155.json create mode 100644 src/abis/erc721.json create mode 100644 src/components/Identicon/StatusIcon.tsx create mode 100644 src/hooks/useENSAvatar.ts diff --git a/package.json b/package.json index 7234f79145..d350fe1513 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ ], "private": true, "devDependencies": { - "@davatar/react": "1.8.1", "@ethersproject/experimental": "^5.4.0", "@gnosis.pm/safe-apps-web3-react": "^0.6.0", "@graphql-codegen/cli": "1.21.5", @@ -21,6 +20,7 @@ "@lingui/cli": "^3.9.0", "@lingui/macro": "^3.9.0", "@lingui/react": "^3.9.0", + "@metamask/jazzicon": "^2.0.0", "@popperjs/core": "^2.4.4", "@reach/dialog": "^0.10.3", "@reach/portal": "^0.10.3", diff --git a/src/abis/erc1155.json b/src/abis/erc1155.json new file mode 100644 index 0000000000..d14e0e18bc --- /dev/null +++ b/src/abis/erc1155.json @@ -0,0 +1,49 @@ +[ + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "_id", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/abis/erc721.json b/src/abis/erc721.json new file mode 100644 index 0000000000..76339c6a0b --- /dev/null +++ b/src/abis/erc721.json @@ -0,0 +1,40 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/components/AccountDetails/index.tsx b/src/components/AccountDetails/index.tsx index 1404f25961..1c8845a733 100644 --- a/src/components/AccountDetails/index.tsx +++ b/src/components/AccountDetails/index.tsx @@ -1,15 +1,12 @@ import { Trans } from '@lingui/macro' +import { AbstractConnector } from '@web3-react/abstract-connector' import { useCallback, useContext } from 'react' import { ExternalLink as LinkIcon } from 'react-feather' import { useAppDispatch } from 'state/hooks' import styled, { ThemeContext } from 'styled-components/macro' -import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg' -import FortmaticIcon from '../../assets/images/fortmaticIcon.png' -import PortisIcon from '../../assets/images/portisIcon.png' -import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg' import { ReactComponent as Close } from '../../assets/images/x.svg' -import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors' +import { injected, portis, walletlink } from '../../connectors' import { SUPPORTED_WALLETS } from '../../constants/wallet' import { useActiveWeb3React } from '../../hooks/web3' import { clearAllTransactions } from '../../state/transactions/actions' @@ -17,7 +14,7 @@ import { ExternalLink, LinkStyledButton, TYPE } from '../../theme' import { shortenAddress } from '../../utils' import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' import { ButtonSecondary } from '../Button' -import Identicon from '../Identicon' +import StatusIcon from '../Identicon/StatusIcon' import { AutoRow } from '../Row' import Copy from './Copy' import Transaction from './Transaction' @@ -179,6 +176,23 @@ const IconWrapper = styled.div<{ size?: number }>` `}; ` +function WrappedStatusIcon({ connector }: { connector: AbstractConnector }) { + return ( + + + {connector === portis && ( + { + portis.portis.showPortis() + }} + > + Show Portis + + )} + + ) +} + const TransactionListWrapper = styled.div` ${({ theme }) => theme.flexColumnNoWrap}; ` @@ -244,50 +258,6 @@ export default function AccountDetails({ ) } - function getStatusIcon() { - if (connector === injected) { - return ( - - - - ) - } else if (connector === walletconnect) { - return ( - - {'WalletConnect - - ) - } else if (connector === walletlink) { - return ( - - {'Coinbase - - ) - } else if (connector === fortmatic) { - return ( - - {'Fortmatic - - ) - } else if (connector === portis) { - return ( - <> - - {'Portis - { - portis.portis.showPortis() - }} - > - Show Portis - - - - ) - } - return null - } - const clearAllTransactionsCallback = useCallback(() => { if (chainId) dispatch(clearAllTransactions({ chainId })) }, [dispatch, chainId]) @@ -332,14 +302,14 @@ export default function AccountDetails({ {ENSName ? ( <>
- {getStatusIcon()} + {connector && }

{ENSName}

) : ( <>
- {getStatusIcon()} + {connector && }

{account && shortenAddress(account)}

diff --git a/src/components/Identicon/StatusIcon.tsx b/src/components/Identicon/StatusIcon.tsx new file mode 100644 index 0000000000..208031a651 --- /dev/null +++ b/src/components/Identicon/StatusIcon.tsx @@ -0,0 +1,25 @@ +import { AbstractConnector } from '@web3-react/abstract-connector' + +import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg' +import FortmaticIcon from '../../assets/images/fortmaticIcon.png' +import PortisIcon from '../../assets/images/portisIcon.png' +import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg' +import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors' +import Identicon from '../Identicon' + +export default function StatusIcon({ connector }: { connector: AbstractConnector }) { + switch (connector) { + case injected: + return + case walletconnect: + return {'WalletConnect'} + case walletlink: + return {'Coinbase + case fortmatic: + return {'Fortmatic'} + case portis: + return {'Portis'} + default: + return null + } +} diff --git a/src/components/Identicon/index.tsx b/src/components/Identicon/index.tsx index f0015537af..f873068cd7 100644 --- a/src/components/Identicon/index.tsx +++ b/src/components/Identicon/index.tsx @@ -1,32 +1,47 @@ -import Davatar, { Image } from '@davatar/react' -import { useMemo } from 'react' +import jazzicon from '@metamask/jazzicon' +import useENSAvatar from 'hooks/useENSAvatar' +import { useEffect, useRef, useState } from 'react' import styled from 'styled-components/macro' import { useActiveWeb3React } from '../../hooks/web3' -const StyledIdenticonContainer = styled.div` +const StyledIdenticon = styled.div` height: 1rem; width: 1rem; border-radius: 1.125rem; background-color: ${({ theme }) => theme.bg4}; + font-size: initial; +` + +const StyledAvatar = styled.img` + height: inherit; + width: inherit; + border-radius: inherit; ` export default function Identicon() { - const { account, library } = useActiveWeb3React() + const ref = useRef(null) + const { account } = useActiveWeb3React() + const { avatar } = useENSAvatar(account ?? undefined) + const [fetchable, setFetchable] = useState(true) - // restrict usage of Davatar until it stops sending 3p requests - // see https://github.com/metaphor-xyz/davatar-helpers/issues/18 - const supportsENS = useMemo(() => { - return ([1, 3, 4, 5] as Array).includes(library?.network?.chainId) - }, [library]) + useEffect(() => { + if ((!avatar || !fetchable) && account) { + const icon = jazzicon(16, parseInt(account?.slice(2, 10), 16)) + const current = ref.current + current?.appendChild(icon) + return () => { + current?.removeChild(icon) + } + } + return + }, [account, avatar, fetchable]) return ( - - {account && supportsENS ? ( - - ) : ( - + + {avatar && fetchable && ( + setFetchable(false)}> )} - + ) } diff --git a/src/components/Web3Status/index.tsx b/src/components/Web3Status/index.tsx index cb62866b15..9d8900aad6 100644 --- a/src/components/Web3Status/index.tsx +++ b/src/components/Web3Status/index.tsx @@ -7,11 +7,6 @@ import { useMemo } from 'react' import { Activity } from 'react-feather' import styled, { css } from 'styled-components/macro' -import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg' -import FortmaticIcon from '../../assets/images/fortmaticIcon.png' -import PortisIcon from '../../assets/images/portisIcon.png' -import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg' -import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors' import { NetworkContextName } from '../../constants/misc' import useENSName from '../../hooks/useENSName' import { useHasSocks } from '../../hooks/useSocksBalance' @@ -20,7 +15,7 @@ import { isTransactionRecent, useAllTransactions } from '../../state/transaction import { TransactionDetails } from '../../state/transactions/reducer' import { shortenAddress } from '../../utils' import { ButtonSecondary } from '../Button' -import Identicon from '../Identicon' +import StatusIcon from '../Identicon/StatusIcon' import Loader from '../Loader' import { RowBetween } from '../Row' import WalletModal from '../WalletModal' @@ -132,36 +127,12 @@ function Sock() { ) } -// eslint-disable-next-line react/prop-types -function StatusIcon({ connector }: { connector: AbstractConnector }) { - if (connector === injected) { - return - } else if (connector === walletconnect) { - return ( - - {'WalletConnect'} - - ) - } else if (connector === walletlink) { - return ( - - {'CoinbaseWallet'} - - ) - } else if (connector === fortmatic) { - return ( - - {'Fortmatic'} - - ) - } else if (connector === portis) { - return ( - - {'Portis'} - - ) - } - return null +function WrappedStatusIcon({ connector }: { connector: AbstractConnector }) { + return ( + + + + ) } function Web3StatusInner() { @@ -198,7 +169,7 @@ function Web3StatusInner() { {ENSName || shortenAddress(account)} )} - {!hasPendingTransactions && connector && } + {!hasPendingTransactions && connector && } ) } else if (error) { diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts index 44a1675616..0350251325 100644 --- a/src/hooks/useContract.ts +++ b/src/hooks/useContract.ts @@ -15,6 +15,8 @@ import ENS_PUBLIC_RESOLVER_ABI from 'abis/ens-public-resolver.json' import ENS_ABI from 'abis/ens-registrar.json' import ERC20_ABI from 'abis/erc20.json' import ERC20_BYTES32_ABI from 'abis/erc20_bytes32.json' +import ERC721_ABI from 'abis/erc721.json' +import ERC1155_ABI from 'abis/erc1155.json' import GOVERNOR_BRAVO_ABI from 'abis/governor-bravo.json' import WETH_ABI from 'abis/weth.json' import { @@ -35,7 +37,7 @@ import { NonfungiblePositionManager, Quoter, UniswapInterfaceMulticall } from 't import { V3Migrator } from 'types/v3/V3Migrator' import { getContract } from 'utils' -import { ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Erc20, Weth } from '../abis/types' +import { ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Erc20, Erc721, Erc1155, Weth } from '../abis/types' import { UNI, WETH9_EXTENDED } from '../constants/tokens' import { useActiveWeb3React } from './web3' @@ -75,6 +77,14 @@ export function useWETHContract(withSignerIfPossible?: boolean) { return useContract(chainId ? WETH9_EXTENDED[chainId]?.address : undefined, WETH_ABI, withSignerIfPossible) } +export function useERC721Contract(nftAddress?: string) { + return useContract(nftAddress, ERC721_ABI, false) +} + +export function useERC1155Contract(nftAddress?: string) { + return useContract(nftAddress, ERC1155_ABI, false) +} + export function useArgentWalletDetectorContract() { return useContract(ARGENT_WALLET_DETECTOR_ADDRESS, ARGENT_WALLET_DETECTOR_ABI, false) } diff --git a/src/hooks/useENSAvatar.ts b/src/hooks/useENSAvatar.ts new file mode 100644 index 0000000000..8e6d07d16b --- /dev/null +++ b/src/hooks/useENSAvatar.ts @@ -0,0 +1,131 @@ +import { namehash } from '@ethersproject/hash' +import { useEffect, useMemo, useState } from 'react' +import uriToHttp from 'utils/uriToHttp' + +import { useSingleCallResult } from '../state/multicall/hooks' +import { isAddress } from '../utils' +import isZero from '../utils/isZero' +import { useENSRegistrarContract, useENSResolverContract, useERC721Contract, useERC1155Contract } from './useContract' +import useDebounce from './useDebounce' +import useENSName from './useENSName' +import { useActiveWeb3React } from './web3' + +/** + * Returns the ENS avatar URI, if available. + * Spec: https://gist.github.com/Arachnid/9db60bd75277969ee1689c8742b75182. + */ +export default function useENSAvatar( + address?: string, + enforceOwnership = true +): { avatar: string | null; loading: boolean } { + const debouncedAddress = useDebounce(address, 200) + const node = useMemo(() => { + if (!debouncedAddress || !isAddress(debouncedAddress)) return undefined + try { + return debouncedAddress ? namehash(`${debouncedAddress.toLowerCase().substr(2)}.addr.reverse`) : undefined + } catch (error) { + return undefined + } + }, [debouncedAddress]) + + const addressAvatar = useAvatarFromNode(node) + const nameAvatar = useAvatarFromNode(namehash(useENSName(address).ENSName ?? '')) + let avatar = addressAvatar.avatar || nameAvatar.avatar + + const nftAvatar = useAvatarFromNFT(avatar, enforceOwnership) + avatar = nftAvatar.avatar || avatar + + const http = avatar && uriToHttp(avatar)[0] + + const changed = debouncedAddress !== address + return { + avatar: changed ? null : http ?? null, + loading: changed || addressAvatar.loading || nameAvatar.loading || nftAvatar.loading, + } +} + +function useAvatarFromNode(node?: string): { avatar?: string; loading: boolean } { + const nodeArgument = useMemo(() => [node], [node]) + const textArgument = useMemo(() => [node, 'avatar'], [node]) + const registrarContract = useENSRegistrarContract(false) + const resolverAddress = useSingleCallResult(registrarContract, 'resolver', nodeArgument) + const resolverAddressResult = resolverAddress.result?.[0] + const resolverContract = useENSResolverContract( + resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined, + false + ) + const avatar = useSingleCallResult(resolverContract, 'text', textArgument) + + return { + avatar: avatar.result?.[0], + loading: resolverAddress.loading || avatar.loading, + } +} + +function useAvatarFromNFT(nftUri = '', enforceOwnership: boolean): { avatar?: string; loading: boolean } { + const parts = nftUri.toLowerCase().split(':') + const protocol = parts[0] + // ignore the chain from eip155 + // TODO: when we are able, pull only from the specified chain + const [, erc] = parts[1]?.split('/') ?? [] + const [contractAddress, id] = parts[2]?.split('/') ?? [] + const isERC721 = protocol === 'eip155' && erc === 'erc721' + const isERC1155 = protocol === 'eip155' && erc === 'erc1155' + const erc721 = useERC721Uri(isERC721 ? contractAddress : undefined, id, enforceOwnership) + const erc1155 = useERC1155Uri(isERC1155 ? contractAddress : undefined, id, enforceOwnership) + const uri = erc721.uri || erc1155.uri + const http = uri && uriToHttp(uri)[0] + + const [loading, setLoading] = useState(false) + const [avatar, setAvatar] = useState(undefined) + useEffect(() => { + setAvatar(undefined) + if (http) { + setLoading(true) + fetch(http) + .then((res) => res.json()) + .then(({ image }) => { + setAvatar(image) + }) + .catch((e) => console.warn(e)) + .finally(() => { + setLoading(false) + }) + } + }, [http]) + + return { avatar, loading: erc721.loading || erc1155.loading || loading } +} + +function useERC721Uri( + contractAddress: string | undefined, + id: string | undefined, + enforceOwnership: boolean +): { uri?: string; loading: boolean } { + const idArgument = useMemo(() => [id], [id]) + const { account } = useActiveWeb3React() + const contract = useERC721Contract(contractAddress) + const owner = useSingleCallResult(contract, 'ownerOf', idArgument) + const uri = useSingleCallResult(contract, 'tokenURI', idArgument) + return { + uri: !enforceOwnership || account === owner.result?.[0] ? uri.result?.[0] : undefined, + loading: owner.loading || uri.loading, + } +} + +function useERC1155Uri( + contractAddress: string | undefined, + id: string | undefined, + enforceOwnership: boolean +): { uri?: string; loading: boolean } { + const { account } = useActiveWeb3React() + const idArgument = useMemo(() => [id], [id]) + const accountArgument = useMemo(() => [account || '', id], [account, id]) + const contract = useERC1155Contract(contractAddress) + const balance = useSingleCallResult(contract, 'balanceOf', accountArgument) + const uri = useSingleCallResult(contract, 'uri', idArgument) + return { + uri: !enforceOwnership || balance.result?.[0] > 0 ? uri.result?.[0] : undefined, + loading: balance.loading || uri.loading, + } +} diff --git a/src/utils/uriToHttp.ts b/src/utils/uriToHttp.ts index 8d334b0f87..1bff146647 100644 --- a/src/utils/uriToHttp.ts +++ b/src/utils/uriToHttp.ts @@ -1,10 +1,12 @@ /** - * Given a URI that may be ipfs, ipns, http, or https protocol, return the fetch-able http(s) URLs for the same content + * Given a URI that may be ipfs, ipns, http, https, or data protocol, return the fetch-able http(s) URLs for the same content * @param uri to convert to fetch-able http url */ export default function uriToHttp(uri: string): string[] { const protocol = uri.split(':')[0].toLowerCase() switch (protocol) { + case 'data': + return [uri] case 'https': return [uri] case 'http': @@ -15,6 +17,9 @@ export default function uriToHttp(uri: string): string[] { case 'ipns': const name = uri.match(/^ipns:(\/\/)?(.*)$/i)?.[2] return [`https://cloudflare-ipfs.com/ipns/${name}/`, `https://ipfs.io/ipns/${name}/`] + case 'ar': + const tx = uri.match(/^ipns:(\/\/)?(.*)$/i)?.[2] + return [`https://arweave.net/${tx}`] default: return [] } diff --git a/yarn.lock b/yarn.lock index e21a4c6cd6..c1f384bd8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1329,19 +1329,6 @@ debug "^3.1.0" lodash.once "^4.1.1" -"@davatar/react@1.8.1": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@davatar/react/-/react-1.8.1.tgz#2fe3e619422a46092c57025328be56b1e911550e" - integrity sha512-vq9zNwAfnZCoY8W2eAbjWP1GPQutUfdxG+lKG2fAPqNFP2qrzDhIziKyCKtt7jwaXp79P1Cy1Gjzlvs1XkzwOQ== - dependencies: - "@ethersproject/contracts" "^5.4.1" - "@ethersproject/providers" "^5.4.5" - "@types/react-blockies" "^1.4.1" - bn.js "^5.2.0" - color "^3.2.1" - mersenne-twister "^1.1.0" - react-blockies "^1.4.1" - "@emotion/cache@^10.0.27": version "10.0.29" resolved "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz" @@ -1562,7 +1549,7 @@ dependencies: "@ethersproject/bignumber" "^5.4.0" -"@ethersproject/contracts@5.4.1", "@ethersproject/contracts@^5.4.1": +"@ethersproject/contracts@5.4.1": version "5.4.1" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.4.1.tgz#3eb4f35b7fe60a962a75804ada2746494df3e470" integrity sha512-m+z2ZgPy4pyR15Je//dUaymRUZq5MtDajF6GwFbGAVmKz/RF+DNIPwF0k5qEcL3wPGVqUjFg2/krlCRVTU4T5w== @@ -1673,7 +1660,7 @@ dependencies: "@ethersproject/logger" "^5.4.0" -"@ethersproject/providers@5.4.5", "@ethersproject/providers@^5.4.5": +"@ethersproject/providers@5.4.5": version "5.4.5" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.4.5.tgz#eb2ea2a743a8115f79604a8157233a3a2c832928" integrity sha512-1GkrvkiAw3Fj28cwi1Sqm8ED1RtERtpdXmRfwIBGmqBSN5MoeRUHuwHPppMtbPayPgpFcvD7/Gdc9doO5fGYgw== @@ -3034,6 +3021,14 @@ "@babel/runtime" "^7.11.2" "@lingui/core" "^3.9.0" +"@metamask/jazzicon@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@metamask/jazzicon/-/jazzicon-2.0.0.tgz#5615528e91c0fc5c9d79202d1f0954a7922525a0" + integrity sha512-7M+WSZWKcQAo0LEhErKf1z+D3YX0tEDAcGvcKbDyvDg34uvgeKR00mFNIYwAhdAS9t8YXxhxZgsrRBBg6X8UQg== + dependencies: + color "^0.11.3" + mersenne-twister "^1.1.0" + "@metamask/safe-event-emitter@2.0.0", "@metamask/safe-event-emitter@^2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz" @@ -4202,13 +4197,6 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== -"@types/react-blockies@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@types/react-blockies/-/react-blockies-1.4.1.tgz#d5f6fff8ece3e90f2e7708f8f3156c87333312df" - integrity sha512-aDX0g0hwzdodkGLSDNUQr6gXxwclGjnhS8jhsR8uQhAfe/7i3GZD/NDcSlQ2SiQiLhfRxX3NlY+nvBwf5Y0tTg== - dependencies: - "@types/react" "*" - "@types/react-dom@>=16.9.0", "@types/react-dom@^17.0.1": version "17.0.9" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.9.tgz#441a981da9d7be117042e1a6fd3dac4b30f55add" @@ -6746,7 +6734,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.2.0: +bn.js@^5.0.0, bn.js@^5.1.1: version "5.2.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== @@ -7640,7 +7628,7 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.9.0, color-convert@^1.9.3: +color-convert@^1.3.0, color-convert@^1.9.0, color-convert@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -7664,6 +7652,13 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-string@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" + integrity sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE= + dependencies: + color-name "^1.0.0" + color-string@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.6.0.tgz#c3915f61fe267672cb7e1e064c9d692219f6c312" @@ -7672,7 +7667,16 @@ color-string@^1.6.0: color-name "^1.0.0" simple-swizzle "^0.2.2" -color@^3.0.0, color@^3.2.1: +color@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" + integrity sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q= + dependencies: + clone "^1.0.2" + color-convert "^1.3.0" + color-string "^0.3.0" + +color@^3.0.0: version "3.2.1" resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== @@ -14330,7 +14334,7 @@ meros@1.1.4: mersenne-twister@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/mersenne-twister/-/mersenne-twister-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/mersenne-twister/-/mersenne-twister-1.1.0.tgz#f916618ee43d7179efcf641bec4531eb9670978a" integrity sha1-+RZhjuQ9cXnvz2Qb7EUx65Zwl4o= messageformat-parser@^4.1.3: @@ -16884,7 +16888,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -17156,13 +17160,6 @@ react-app-polyfill@^2.0.0: regenerator-runtime "^0.13.7" whatwg-fetch "^3.4.1" -react-blockies@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/react-blockies/-/react-blockies-1.4.1.tgz#d4f0faf95ac197213a297a370a4d7f77ea3d0b08" - integrity sha512-4N015X5oPNnD3xQPsiqolOFzPZSSWyc5mJhJUZShUCHtiGUxVN+1qsWTcglkHMNySux9hUofaispqcw9QkWP5Q== - dependencies: - prop-types "^15.5.10" - react-clientside-effect@^1.2.2: version "1.2.5" resolved "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz"