diff --git a/.eslintignore b/.eslintignore index 2770e2f..32b2fa8 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,5 @@ node_modules +src/vendor dist webpack.config.js jest.config.js diff --git a/.gitignore b/.gitignore index 1437c53..0d18882 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ /.pnp .pnp.js +# Typescript build +tsconfig.tsbuildinfo + # testing /coverage diff --git a/next.config.js b/next.config.js index 75d08e3..64ace7c 100644 --- a/next.config.js +++ b/next.config.js @@ -1,5 +1,33 @@ const { version } = require('./package.json') +const isDev = process.env.NODE_ENV !== 'production' + +const securityHeaders = [ + { + key: 'X-XSS-Protection', + value: '1; mode=block', + }, + { + key: 'X-Frame-Options', + value: 'DENY', + }, + { + key: 'X-Content-Type-Options', + value: 'nosniff', + }, + { + key: 'Referrer-Policy', + value: 'strict-origin-when-cross-origin', + }, + // TODO remove imgsrc githubcontent when use-ck updated with celo terminal icon + { + key: 'Content-Security-Policy', + value: `default-src 'self'; script-src 'self'${ + isDev ? " 'unsafe-eval'" : '' + }; connect-src 'self' https://*.celo.org https://*.celo-testnet.org wss://walletconnect.celo.org wss://relay.walletconnect.org; img-src 'self' data: https://raw.githubusercontent.com; style-src 'self' 'unsafe-inline'; font-src 'self' data:; base-uri 'self'; form-action 'self'`, + }, +] + module.exports = { webpack: (config, { webpack }) => { config.resolve.fallback = { @@ -9,7 +37,7 @@ module.exports = { child_process: false, readline: false, } - config.plugins.push(new webpack.IgnorePlugin(/^electron$/)) + config.plugins.push(new webpack.IgnorePlugin({ resourceRegExp: /^electron$/ })) return config }, @@ -22,6 +50,15 @@ module.exports = { ] }, + async headers() { + return [ + { + source: '/(.*)', + headers: securityHeaders, + }, + ] + }, + env: { NEXT_PUBLIC_VERSION: version, }, diff --git a/package.json b/package.json index dcf5df4..46838cd 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "@celo-tools/mento-fi", - "version": "1.0.0", + "version": "1.1.0", "description": "A simple DApp for Celo Mento exchanges", "keywords": [ "Celo", "Mento", + "Granda", "Exchange" ], "author": "J M Rossy", @@ -12,7 +13,7 @@ "type": "git", "url": "https://github.com/celo-tools/mento-fi" }, - "homepage": "TODO", + "homepage": "https://mento.finance", "license": "Apache-2.0", "scripts": { "dev": "next", @@ -26,43 +27,45 @@ "dependencies": { "@celo-tools/use-contractkit": "^1.3.0", "@ethersproject/address": "^5.5.0", + "@metamask/inpage-provider": "6.0.1", "@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28", "@reduxjs/toolkit": "^1.6.2", "bignumber.js": "^9.0.1", "formik": "^2.2.9", "frappe-charts": "^1.6.2", - "next": "11.1.2", + "next": "12.0.3", "next-persist": "^1.2.4", + "post-message-stream": "3.0.0", "react": "^17.0.2", "react-accessible-dropdown-menu-hook": "^3.1.0", "react-dom": "^17.0.2", - "react-redux": "^7.2.5", + "react-redux": "^7.2.6", "react-select": "4.3.1", - "react-toastify": "^8.0.3" + "react-toastify": "^8.1.0" }, "devDependencies": { - "@testing-library/jest-dom": "^5.0.0", + "@testing-library/jest-dom": "^5.15.0", "@testing-library/react": "^12.1.2", - "@testing-library/user-event": "^13.4.1", + "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.0.2", - "@types/node": "16.11.1", - "@types/react": "17.0.30", - "@types/react-dom": "17.0.9", - "@types/react-redux": "7.1.19", + "@types/node": "16.11.7", + "@types/react": "17.0.34", + "@types/react-dom": "17.0.11", + "@types/react-redux": "7.1.20", "@types/react-select": "4.0.18", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", - "autoprefixer": "^10.3.7", + "autoprefixer": "^10.4.0", "eslint": "7.32.0", - "eslint-config-next": "^11.1.2", + "eslint-config-next": "^12.0.3", "eslint-config-prettier": "^8.3.0", - "jest": "^27.2.5", + "jest": "^27.3.1", "jest-css-modules-transform": "^4.3.0", - "postcss": "^8.3.9", + "postcss": "^8.3.11", "prettier": "^2.4.1", - "tailwindcss": "^2.2.17", + "tailwindcss": "^2.2.19", "ts-jest": "^27.0.7", - "ts-node": "^10.3.0", + "ts-node": "^10.4.0", "typescript": "4.4.4" }, "resolutions": { diff --git a/src/app/store.ts b/src/app/store.ts index 9df3581..8033bd2 100644 --- a/src/app/store.ts +++ b/src/app/store.ts @@ -3,15 +3,17 @@ import { config } from 'src/config/config' import { accountReducer } from 'src/features/accounts/accountSlice' import { blockReducer } from 'src/features/blocks/blockSlice' import { tokenPriceReducer } from 'src/features/chart/tokenPriceSlice' +import { grandaReducer } from 'src/features/granda/grandaSlice' import { swapReducer } from 'src/features/swap/swapSlice' export function createStore() { return configureStore({ reducer: { account: accountReducer, + block: blockReducer, + granda: grandaReducer, swap: swapReducer, tokenPrice: tokenPriceReducer, - block: blockReducer, }, devTools: config.debug, }) diff --git a/src/components/Chevron.tsx b/src/components/Chevron.tsx new file mode 100644 index 0000000..5a4c0cb --- /dev/null +++ b/src/components/Chevron.tsx @@ -0,0 +1,51 @@ +import { memo } from 'react' + +interface Props { + width?: string | number + height?: string | number + direction: 'n' | 'e' | 's' | 'w' + color?: string + classes?: string +} + +function _ChevronIcon({ width, height, direction, color, classes }: Props) { + let className: string + switch (direction) { + case 'n': + className = 'rotate-180' + break + case 'e': + className = 'rotate-270' + break + case 's': + className = '' + break + case 'w': + className = 'rotate-90' + break + default: + throw new Error(`Invalid chevron direction ${direction}`) + } + + return ( + + + + ) +} + +export const ChevronIcon = memo(_ChevronIcon) diff --git a/src/components/TxSuccessToast.tsx b/src/components/TxSuccessToast.tsx new file mode 100644 index 0000000..1a3d327 --- /dev/null +++ b/src/components/TxSuccessToast.tsx @@ -0,0 +1,27 @@ +import { toast } from 'react-toastify' +import { TextLink } from 'src/components/buttons/TextLink' + +export function toastToYourSuccess(msg: string, txHash: string, blockscoutUrl: string) { + toast.success(, { + autoClose: 15000, + }) +} + +export function TxSuccessToast({ + msg, + txHash, + blockscoutUrl, +}: { + msg: string + txHash: string + blockscoutUrl: string +}) { + return ( +
+ {msg + ' '} + + See Details + +
+ ) +} diff --git a/src/components/animation/Spinner.module.css b/src/components/animation/Spinner.module.css new file mode 100644 index 0000000..83ca1ab --- /dev/null +++ b/src/components/animation/Spinner.module.css @@ -0,0 +1,39 @@ +.spinner { + display: inline-block; + position: relative; + width: 80px; + height: 80px; + opacity: 0.8; +} + +.spinner div { + box-sizing: border-box; + display: block; + position: absolute; + width: 64px; + height: 64px; + margin: 8px; + border: 8px solid #2e3338; + border-radius: 50%; + animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; + border-color: #2e3338 transparent transparent transparent; +} + +.spinner div:nth-of-type(1) { + animation-delay: -0.45s; +} +.spinner div:nth-of-type(2) { + animation-delay: -0.3s; +} +.spinner div:nth-of-type(3) { + animation-delay: -0.15s; +} + +@keyframes lds-ring { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/src/components/animation/Spinner.tsx b/src/components/animation/Spinner.tsx new file mode 100644 index 0000000..b0b527d --- /dev/null +++ b/src/components/animation/Spinner.tsx @@ -0,0 +1,16 @@ +import { memo } from 'react' +import styles from 'src/components/animation/Spinner.module.css' + +// From https://loading.io/css/ +function _Spinner() { + return ( +
+
+
+
+
+
+ ) +} + +export const Spinner = memo(_Spinner) diff --git a/src/components/buttons/BackButton.tsx b/src/components/buttons/BackButton.tsx new file mode 100644 index 0000000..aa17b8f --- /dev/null +++ b/src/components/buttons/BackButton.tsx @@ -0,0 +1,6 @@ +import { IconButton, IconButtonProps } from 'src/components/buttons/IconButton' +import LeftArrow from 'src/images/icons/arrow-left-circle.svg' + +export function BackButton(props: IconButtonProps) { + return +} diff --git a/src/components/buttons/IconButton.tsx b/src/components/buttons/IconButton.tsx index df71ac7..26dd84e 100644 --- a/src/components/buttons/IconButton.tsx +++ b/src/components/buttons/IconButton.tsx @@ -1,7 +1,7 @@ import Image from 'next/image' import { PropsWithChildren } from 'react' -interface ButtonProps { +export interface IconButtonProps { width: number height: number classes?: string @@ -12,7 +12,7 @@ interface ButtonProps { passThruProps?: any } -export function IconButton(props: PropsWithChildren) { +export function IconButton(props: PropsWithChildren) { const { width, height, classes, onClick, imgSrc, disabled, title, passThruProps } = props const base = 'flex items-center justify-center transition-all' diff --git a/src/components/buttons/RefreshButton.tsx b/src/components/buttons/RefreshButton.tsx new file mode 100644 index 0000000..110f558 --- /dev/null +++ b/src/components/buttons/RefreshButton.tsx @@ -0,0 +1,6 @@ +import { IconButton, IconButtonProps } from 'src/components/buttons/IconButton' +import RepeatArrow from 'src/images/icons/arrow-repeat.svg' + +export function RefreshButton(props: IconButtonProps) { + return +} diff --git a/src/components/buttons/SolidButton.tsx b/src/components/buttons/SolidButton.tsx index 42395c9..d3e8845 100644 --- a/src/components/buttons/SolidButton.tsx +++ b/src/components/buttons/SolidButton.tsx @@ -4,7 +4,7 @@ interface ButtonProps { size?: 'xs' | 's' | 'm' | 'l' | 'xl' type?: 'submit' | 'reset' | 'button' onClick?: () => void - dark?: boolean // defaults to false + color?: 'white' | 'green' | 'red' // defaults to green classes?: string bold?: boolean disabled?: boolean @@ -14,16 +14,39 @@ interface ButtonProps { } export function SolidButton(props: PropsWithChildren) { - const { size, type, onClick, dark, classes, bold, icon, disabled, title, passThruProps } = props + const { + size, + type, + onClick, + color: _color, + classes, + bold, + icon, + disabled, + title, + passThruProps, + } = props + const color = _color ?? 'green' const base = 'flex items-center justify-center rounded-full transition-all duration-300' const sizing = sizeToClasses(size) - const colors = dark ? 'bg-green text-white' : 'bg-white text-black' - const onHover = dark ? 'hover:bg-green-dark' : 'hover:bg-gray-50' - const onDisabled = 'disabled:bg-gray-300 disabled:text-gray-300' - const onActive = dark ? 'active:bg-green-darkest' : 'active:bg-gray-100' + let baseColors, onHover, onActive + if (color === 'green') { + baseColors = 'bg-green text-white' + onHover = 'hover:bg-green-dark' + onActive = 'active:bg-green-darkest' + } else if (color === 'red') { + baseColors = 'bg-red-600 text-white' + onHover = 'hover:bg-red-500' + onActive = 'active:bg-red-400' + } else if (color === 'white') { + baseColors = 'bg-white text-black' + onHover = 'hover:bg-gray-50' + onActive = 'active:bg-gray-100' + } + const onDisabled = 'disabled:bg-gray-300 disabled:text-gray-500' const weight = bold ? 'font-semibold' : '' - const allClasses = `${base} ${sizing} ${colors} ${onHover} ${onDisabled} ${onActive} ${weight} ${classes}` + const allClasses = `${base} ${sizing} ${baseColors} ${onHover} ${onDisabled} ${onActive} ${weight} ${classes}` return ( + setShowNetworkModal(false)} /> + ) } diff --git a/src/components/nav/NetworkModal.tsx b/src/components/nav/NetworkModal.tsx new file mode 100644 index 0000000..ea5db4f --- /dev/null +++ b/src/components/nav/NetworkModal.tsx @@ -0,0 +1,98 @@ +import { Alfajores, Baklava, Mainnet, Network, useContractKit } from '@celo-tools/use-contractkit' +import ReactModal from 'react-modal' +import { toast } from 'react-toastify' +import { useAppDispatch, useAppSelector } from 'src/app/hooks' +import { IconButton } from 'src/components/buttons/IconButton' +import { reset as accountReset } from 'src/features/accounts/accountSlice' +import { reset as blockReset } from 'src/features/blocks/blockSlice' +import { resetTokenPrices } from 'src/features/chart/tokenPriceSlice' +import { reset as grandaReset } from 'src/features/granda/grandaSlice' +import { reset as swapReset } from 'src/features/swap/swapSlice' +import XCircle from 'src/images/icons/x-circle.svg' +import { HrDivider } from 'src/layout/HrDivider' +import { defaultModalStyles } from 'src/styles/modals' +import { logger } from 'src/utils/logger' + +interface Props { + isOpen: boolean + close: () => void +} + +export function NetworkModal({ isOpen, close }: Props) { + const latestBlock = useAppSelector((s) => s.block.latestBlock) + const { network, updateNetwork } = useContractKit() + const allNetworks = [Mainnet, Alfajores, Baklava] + + const dispatch = useAppDispatch() + const switchToNetwork = async (n: Network) => { + try { + logger.debug('Resetting and switching to network', n.name) + await updateNetwork(n) + dispatch(blockReset()) + dispatch(accountReset()) + dispatch(grandaReset()) + dispatch(swapReset()) + dispatch(resetTokenPrices()) + } catch (error) { + // TODO fix use-ck so it throws on update fail (i.e due to metamask) + logger.error('Error updating network', error) + toast.error('Could not switch network, is Metamask using the right network?') + } + } + + return ( + +
+
+
+ +
+

Network Details

+
+
Connected to:
+
{network.name}
+
+
+
Block Number:
+
{latestBlock?.number ?? 'Unknown'}
+
+
+
Node Rpc Url:
+
{shortenUrl(network.rpcUrl) ?? 'Unknown'}
+
+ +

Switch Network

+
+ {allNetworks.map((n) => ( + + ))} +
+
+
+
+ ) +} + +function shortenUrl(url: string) { + try { + if (!url) return null + return new URL(url).hostname + } catch (error) { + logger.error('Error parsing url', error) + return null + } +} diff --git a/src/config/config.ts b/src/config/config.ts index 96fc88b..1fec65a 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -1,20 +1,9 @@ -export enum CeloContract { - Exchange = 'Exchange', - ExchangeEUR = 'ExchangeEUR', - GoldToken = 'GoldToken', - SortedOracles = 'SortedOracles', - StableToken = 'StableToken', - StableTokenEUR = 'StableTokenEUR', -} - interface Config { debug: boolean version: string | null url: string - blockscoutUrl?: string discordUrl: string chainId: number - contractAddresses: Record showPriceChart: boolean } @@ -25,17 +14,8 @@ const configMainnet: Config = { debug: isDevMode, version, url: 'https://mento.finance', - blockscoutUrl: 'https://explorer.celo.org', discordUrl: 'https://discord.gg/E9AqUQnWQE', chainId: 42220, - contractAddresses: { - [CeloContract.Exchange]: '0x67316300f17f063085Ca8bCa4bd3f7a5a3C66275', - [CeloContract.ExchangeEUR]: '0xE383394B913d7302c49F794C7d3243c429d53D1d', - [CeloContract.GoldToken]: '0x471EcE3750Da237f93B8E339c536989b8978a438', - [CeloContract.SortedOracles]: '0xefB84935239dAcdecF7c5bA76d8dE40b077B7b33', - [CeloContract.StableToken]: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - [CeloContract.StableTokenEUR]: '0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', - }, showPriceChart: false, } diff --git a/src/config/consts.ts b/src/config/consts.ts index 556340d..2e777ce 100644 --- a/src/config/consts.ts +++ b/src/config/consts.ts @@ -3,8 +3,9 @@ export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000' export const AVG_BLOCK_TIMES = 5000 // 5 seconds export const STALE_BLOCK_TIME = 25000 // 25 seconds export const EXCHANGE_RATE_STALE_TIME = 5000 // 5 second +export const GRANDA_PROPOSAL_STALE_TIME = 30000 // 30 second export const BALANCE_STALE_TIME = 5000 // 5 seconds -export const STATUS_POLLER_DELAY = 10000 // 10 seconds +export const STATUS_POLLER_DELAY = 5000 // 5 seconds export const SIGN_OPERATION_TIMEOUT = 90000 // 90 seconds export const STALE_TOKEN_PRICE_TIME = 900000 // 15 minutes diff --git a/src/config/tokenMapping.ts b/src/config/tokenMapping.ts new file mode 100644 index 0000000..45d3d48 --- /dev/null +++ b/src/config/tokenMapping.ts @@ -0,0 +1,65 @@ +/** + * Utilities for converting between kit and local token contracts/models + * The kit is a bit of a mess so this DApp uses it's own enums/types + */ + +import type { ContractKit } from '@celo/contractkit' +import { CeloContract, CeloTokenType, StableToken, Token } from '@celo/contractkit' +import { NativeTokenId } from 'src/config/tokens' + +export async function getExchangeContract(kit: ContractKit, tokenId: NativeTokenId) { + switch (tokenId) { + case NativeTokenId.cUSD: + return kit.contracts.getExchange(StableToken.cUSD) + case NativeTokenId.cEUR: + return kit.contracts.getExchange(StableToken.cEUR) + default: + throw new Error(`Could not get contract for token ${tokenId}`) + } +} + +export async function getTokenContract(kit: ContractKit, tokenId: NativeTokenId) { + switch (tokenId) { + case NativeTokenId.cUSD: + return kit.contracts.getStableToken(StableToken.cUSD) + case NativeTokenId.cEUR: + return kit.contracts.getStableToken(StableToken.cEUR) + case NativeTokenId.CELO: + return kit.contracts.getGoldToken() + default: + throw new Error(`Could not get contract for token ${tokenId}`) + } +} + +export function kitContractToNativeToken(name: CeloContract): NativeTokenId { + if (name === CeloContract.StableToken) return NativeTokenId.cUSD + if (name === CeloContract.StableTokenEUR) return NativeTokenId.cEUR + if (name === CeloContract.GoldToken) return NativeTokenId.CELO + throw new Error(`Unsupported token contract name ${name}`) +} + +export function kitTokenToNativeToken(tokenId: CeloTokenType): NativeTokenId { + switch (tokenId) { + case StableToken.cUSD: + return NativeTokenId.cUSD + case StableToken.cEUR: + return NativeTokenId.cEUR + case Token.CELO: + return NativeTokenId.CELO + default: + throw new Error(`Unsupported token id ${tokenId}`) + } +} + +export function nativeTokenToKitToken(tokenId: NativeTokenId): CeloTokenType { + switch (tokenId) { + case NativeTokenId.cUSD: + return StableToken.cUSD + case NativeTokenId.cEUR: + return StableToken.cEUR + case NativeTokenId.CELO: + return Token.CELO + default: + throw new Error(`Unsupported token id ${tokenId}`) + } +} diff --git a/src/config/tokens.ts b/src/config/tokens.ts index 8e2ef04..9b6e092 100644 --- a/src/config/tokens.ts +++ b/src/config/tokens.ts @@ -1,5 +1,4 @@ import { config } from 'src/config/config' -import { NULL_ADDRESS } from 'src/config/consts' import { Color } from 'src/styles/Color' export interface Token { @@ -7,7 +6,6 @@ export interface Token { symbol: string // usually the same as id name: string color: string - address: string // contract address decimals: number chainId: number } @@ -36,7 +34,6 @@ export const NativeTokens: INativeTokens = { symbol: NativeTokenId.CELO, name: 'Celo Native', color: Color.celoGold, - address: config.contractAddresses.GoldToken, decimals: 18, chainId: config.chainId, }, @@ -45,7 +42,6 @@ export const NativeTokens: INativeTokens = { symbol: NativeTokenId.cUSD, name: 'Celo Dollar', color: Color.celoGreen, - address: config.contractAddresses.StableToken, decimals: 18, chainId: config.chainId, }, @@ -54,7 +50,6 @@ export const NativeTokens: INativeTokens = { symbol: NativeTokenId.cEUR, name: 'Celo Euro', color: Color.celoGreen, - address: config.contractAddresses.StableTokenEUR, decimals: 18, chainId: config.chainId, }, @@ -67,16 +62,6 @@ export const CELO = NativeTokens.CELO export const cUSD = NativeTokens.cUSD export const cEUR = NativeTokens.cEUR -export const UnknownToken: Token = { - id: 'unknown', - symbol: 'unknown', - name: 'Unknown Token', - color: '#818181', - address: NULL_ADDRESS, - decimals: 18, - chainId: config.chainId, -} - export function isNativeToken(tokenId: string) { return Object.keys(NativeTokens).includes(tokenId) } diff --git a/src/features/granda/ProposalConfirm.tsx b/src/features/granda/ProposalConfirm.tsx new file mode 100644 index 0000000..c697f0e --- /dev/null +++ b/src/features/granda/ProposalConfirm.tsx @@ -0,0 +1,172 @@ +import { useContractKit } from '@celo-tools/use-contractkit' +import { ContractKit, StableToken } from '@celo/contractkit' +import Image from 'next/image' +import { useEffect } from 'react' +import { toast } from 'react-toastify' +import { useAppDispatch, useAppSelector } from 'src/app/hooks' +import { BackButton } from 'src/components/buttons/BackButton' +import { RefreshButton } from 'src/components/buttons/RefreshButton' +import { SolidButton } from 'src/components/buttons/SolidButton' +import { toastToYourSuccess } from 'src/components/TxSuccessToast' +import { MAX_EXCHANGE_RATE, MIN_EXCHANGE_RATE, SIGN_OPERATION_TIMEOUT } from 'src/config/consts' +import { getTokenContract, nativeTokenToKitToken } from 'src/config/tokenMapping' +import { NativeTokenId } from 'src/config/tokens' +import { fetchBalances } from 'src/features/accounts/fetchBalances' +import { fetchOracleRates } from 'src/features/granda/fetchOracleRates' +import { fetchProposals } from 'src/features/granda/fetchProposals' +import { setFormValues } from 'src/features/granda/grandaSlice' +import { getExchangeValues } from 'src/features/granda/utils' +import { SwapConfirmSummary } from 'src/features/swap/SwapConfirm' +import InfoCircle from 'src/images/icons/info-circle.svg' +import { FloatingBox } from 'src/layout/FloatingBox' +import { getAdjustedAmount } from 'src/utils/amount' +import { logger } from 'src/utils/logger' +import { asyncTimeout, PROMISE_TIMEOUT } from 'src/utils/timeout' + +export function ProposalConfirm() { + const dispatch = useAppDispatch() + const balances = useAppSelector((s) => s.account.balances) + const { config, oracleRates, formValues } = useAppSelector((s) => s.granda) + const { fromAmount, fromTokenId, toTokenId } = formValues || {} + const { address, kit, initialised, network, performActions } = useContractKit() + + // Ensure invariants are met, otherwise return to form + const isConfirmValid = + fromAmount && fromTokenId && toTokenId && address && kit && config && oracleRates + useEffect(() => { + if (!isConfirmValid) { + dispatch(setFormValues(null)) + } + }, [isConfirmValid, dispatch]) + if (!isConfirmValid) return null + + const { from, to, rate, stableTokenId } = getExchangeValues( + fromAmount, + fromTokenId, + toTokenId, + config.spread, + oracleRates + ) + const tokenBalance = balances[fromTokenId] + // Check if amount is almost equal to balance max, in which case use max + // Helps handle problems from imprecision in non-wei amount display + const finalFromAmount = getAdjustedAmount(from.weiAmount, tokenBalance) + + const onSubmit = async () => { + if (!address || !kit) { + toast.error('Kit not connected') + return + } + if (rate.value < MIN_EXCHANGE_RATE || rate.value > MAX_EXCHANGE_RATE) { + toast.error('Rate seems incorrect') + return + } + + const approvalOperation = async (k: ContractKit) => { + const tokenContract = await getTokenContract(k, fromTokenId) + const grandaContract = await k.contracts.getGrandaMento() + const approveTx = await tokenContract.increaseAllowance( + grandaContract.address, + finalFromAmount + ) + // Gas price must be set manually because contractkit pre-populate it and + // its helpers for getting gas price are only meant for stable token prices + const gasPrice = await k.web3.eth.getGasPrice() + const approveReceipt = await approveTx.sendAndWaitForReceipt({ gasPrice }) + logger.info(`Tx receipt received for approval: ${approveReceipt.transactionHash}`) + return approveReceipt.transactionHash + } + const approvalOpWithTimeout = asyncTimeout(approvalOperation, SIGN_OPERATION_TIMEOUT) + + const proposeOperation = async (k: ContractKit) => { + const sellCelo = fromTokenId === NativeTokenId.CELO + const grandaContract = await k.contracts.getGrandaMento() + const contractId = k.celoTokens.getContract( + nativeTokenToKitToken(stableTokenId) as StableToken + ) + const proposeTx = await grandaContract.createExchangeProposal( + contractId, + finalFromAmount, + sellCelo + ) + const gasPrice = await k.web3.eth.getGasPrice() + const proposeReceipt = await proposeTx.sendAndWaitForReceipt({ gasPrice }) + logger.info(`Tx receipt received for swap: ${proposeReceipt.transactionHash}`) + await dispatch(fetchBalances({ address, kit: k })) + return proposeReceipt.transactionHash + } + const proposeOpWithTimeout = asyncTimeout(proposeOperation, SIGN_OPERATION_TIMEOUT) + + try { + const txHashes = (await performActions( + approvalOpWithTimeout, + proposeOpWithTimeout + )) as string[] + if (!txHashes || txHashes.length !== 2) throw new Error('Tx hashes not found') + toastToYourSuccess('Proposal Created!', txHashes[1], network.explorer) + await dispatch(fetchProposals({ kit, force: true })) + dispatch(setFormValues(null)) + } catch (err: any) { + if (err.message === PROMISE_TIMEOUT) { + toast.error('Action timed out') + } else { + toast.error('Unable to complete proposal') + } + logger.error('Failed to execute proposal', err) + } + } + + const onClickBack = () => { + dispatch(setFormValues(null)) + } + + const onClickRefresh = () => { + if (!kit || !initialised) return + dispatch(fetchOracleRates({ kit })) + .unwrap() + .catch((err) => { + toast.error('Error retrieving exchange rates') + logger.error('Failed to retrieve exchange rates', err) + }) + } + + return ( + +
+ +

Confirm Proposal

+ +
+ + +
+
+
Spread:
+
{`${config.spread}%`}
+
+
+
Veto Period:
+
{(config.vetoPeriodSeconds / 86400).toFixed(2) + ' days'}
+
+
+
+ + Propose + +
+
+ ) +} + +function InfoTip() { + return ( +
+
+ info +
+ Funds will be transferred out of your account until the exchange is executed or cancelled. +
+
+
+ ) +} diff --git a/src/features/granda/ProposalForm.tsx b/src/features/granda/ProposalForm.tsx new file mode 100644 index 0000000..415c314 --- /dev/null +++ b/src/features/granda/ProposalForm.tsx @@ -0,0 +1,79 @@ +import Image from 'next/image' +import { useAppDispatch, useAppSelector } from 'src/app/hooks' +import { TextLink } from 'src/components/buttons/TextLink' +import { setFormValues, setSubpage } from 'src/features/granda/grandaSlice' +import { GrandaSubpage } from 'src/features/granda/types' +import { getExchangeValues } from 'src/features/granda/utils' +import { SwapFormInner } from 'src/features/swap/SwapForm' +import { SwapFormValues } from 'src/features/swap/types' +import { useFormValidator } from 'src/features/swap/useFormValidator' +import { ExchangeValueFormatter } from 'src/features/swap/utils' +import InfoCircle from 'src/images/icons/info-circle.svg' +import { FloatingBox } from 'src/layout/FloatingBox' + +export function ProposalForm() { + const balances = useAppSelector((s) => s.account.balances) + const { config, oracleRates } = useAppSelector((s) => s.granda) + const exchangeLimits = config?.exchangeLimits + const spread = config?.spread + + const dispatch = useAppDispatch() + const onSubmit = (values: SwapFormValues) => { + dispatch(setFormValues(values)) + } + + const validateForm = useFormValidator(balances, exchangeLimits) + + const valueFormatter: ExchangeValueFormatter = (fromAmount, fromTokenId, toTokenId) => + getExchangeValues(fromAmount, fromTokenId, toTokenId, spread, oracleRates) + + // const onClickBack = () => { + // dispatch(setSubpage(GrandaSubpage.List)) + // } + + return ( + +
+ {/* */} +

Propose Granda Exchange

+ {/*
*/} +
+ + +
+ ) +} + +function InfoTip() { + const dispatch = useAppDispatch() + const onClickSeeHistory = () => { + dispatch(setSubpage(GrandaSubpage.List)) + } + return ( +
+
+ info +
+ Granda Mento is a{' '} + + special process + {' '} + for very large exchanges (cUSD 500,000+). See{' '} + + . +
+
+
+ ) +} diff --git a/src/features/granda/ProposalList.tsx b/src/features/granda/ProposalList.tsx new file mode 100644 index 0000000..d65e0b4 --- /dev/null +++ b/src/features/granda/ProposalList.tsx @@ -0,0 +1,138 @@ +import BigNumber from 'bignumber.js' +import Image from 'next/image' +import { useAppDispatch, useAppSelector } from 'src/app/hooks' +import { Spinner } from 'src/components/animation/Spinner' +import { BackButton } from 'src/components/buttons/BackButton' +import { IconButton } from 'src/components/buttons/IconButton' +import { SolidButton } from 'src/components/buttons/SolidButton' +import { CELO, NativeTokens } from 'src/config/tokens' +import { setSubpage, viewProposal } from 'src/features/granda/grandaSlice' +import { GrandaProposal, GrandaProposalState, GrandaSubpage } from 'src/features/granda/types' +import ArrowRight from 'src/images/icons/arrow-right-short.svg' +import PlusCircle from 'src/images/icons/plus-circle-fill.svg' +import { TokenIcon } from 'src/images/tokens/TokenIcon' +import { FloatingBox } from 'src/layout/FloatingBox' +import { fromWei } from 'src/utils/amount' + +export function ProposalList() { + const { proposals, proposalsLastUpdated } = useAppSelector((s) => s.granda) + const numProposals = Object.keys(proposals).length + + const dispatch = useAppDispatch() + const onClickCreate = () => { + dispatch(setSubpage(GrandaSubpage.Form)) + } + + return ( + +
+ +

Granda Mento Proposals

+ +
+ {!numProposals && !proposalsLastUpdated && ( +
+ +
+ )} + {!numProposals && !!proposalsLastUpdated && } + {!!numProposals && !!proposalsLastUpdated && ( + + )} +
+ ) +} + +function EmptyList({ onClickCreate }: { onClickCreate: () => void }) { + return ( +
+

+ There are no Granda Mento proposals on this network yet. +

+ + Create New Proposal + +
+ ) +} + +function ProposalRows({ proposals }: { proposals: GrandaProposal[] }) { + const sorted = proposals.sort((a, b) => new BigNumber(b.id).minus(a.id).toNumber()) + + const dispatch = useAppDispatch() + const onRowClick = (id: string) => { + dispatch(viewProposal(id)) + } + return ( +
+ {sorted.map((p) => { + const stableToken = NativeTokens[p.stableTokenId] + const fromToken = p.sellCelo ? CELO : stableToken + const toToken = p.sellCelo ? stableToken : CELO + const stateColor = proposalStateToColor(p.state) + return ( + + ) + })} +
+ ) +} + +function NewButton({ onClickCreate }: { onClickCreate: () => void }) { + return ( +
+ +
+ ) +} + +function proposalStateToColor(state: GrandaProposalState) { + switch (state) { + case GrandaProposalState.Proposed: + return 'border-yellow-500 text-yellow-500' + case GrandaProposalState.Approved: + case GrandaProposalState.Executed: + return 'border-green-darkest text-green-darkest' + case GrandaProposalState.Cancelled: + return 'border-red-600 text-red-600' + default: + return 'border-gray-500 text-grey-500' + } +} diff --git a/src/features/granda/ProposalView.tsx b/src/features/granda/ProposalView.tsx new file mode 100644 index 0000000..6420e7a --- /dev/null +++ b/src/features/granda/ProposalView.tsx @@ -0,0 +1,232 @@ +import { useContractKit } from '@celo-tools/use-contractkit' +import type { ContractKit } from '@celo/contractkit' +import BigNumber from 'bignumber.js' +import { useEffect, useState } from 'react' +import ReactModal from 'react-modal' +import { toast } from 'react-toastify' +import { useAppDispatch, useAppSelector } from 'src/app/hooks' +import { BackButton } from 'src/components/buttons/BackButton' +import { IconButton } from 'src/components/buttons/IconButton' +import { RefreshButton } from 'src/components/buttons/RefreshButton' +import { SolidButton } from 'src/components/buttons/SolidButton' +import { TextLink } from 'src/components/buttons/TextLink' +import { toastToYourSuccess } from 'src/components/TxSuccessToast' +import { SIGN_OPERATION_TIMEOUT, WEI_PER_UNIT } from 'src/config/consts' +import { NativeTokenId } from 'src/config/tokens' +import { fetchProposals } from 'src/features/granda/fetchProposals' +import { clearProposal } from 'src/features/granda/grandaSlice' +import { GrandaProposal, GrandaProposalState } from 'src/features/granda/types' +import { SwapConfirmSummary } from 'src/features/swap/SwapConfirm' +import XCircle from 'src/images/icons/x-circle.svg' +import { FloatingBox } from 'src/layout/FloatingBox' +import { defaultModalStyles } from 'src/styles/modals' +import { areAddressesEqual, shortenAddress } from 'src/utils/addresses' +import { fromWei } from 'src/utils/amount' +import { logger } from 'src/utils/logger' +import { asyncTimeout, PROMISE_TIMEOUT } from 'src/utils/timeout' + +export function ProposalView() { + const dispatch = useAppDispatch() + const { viewProposalId: proposalId, proposals } = useAppSelector((s) => s.granda) + const proposal = proposalId ? proposals[proposalId] : null + + useEffect(() => { + if (!proposal) dispatch(clearProposal()) + }, [proposal, dispatch]) + + const onClickBack = () => { + dispatch(clearProposal()) + } + + const { address, kit, initialised, network, performActions } = useContractKit() + + const onClickRefresh = () => { + if (!kit || !initialised) return + dispatch(fetchProposals({ kit, force: true })) + .unwrap() + .catch((err) => { + toast.warn('Error retrieving Granda proposals') + logger.error('Failed to retrieve granda proposals', err) + }) + } + + const [showConfModal, setShowConfModal] = useState(false) + + if (!proposal) return null + + const isCancellable = + kit && + initialised && + address && + areAddressesEqual(address, proposal.exchanger) && + proposal.state === GrandaProposalState.Proposed + + const onClickCancel = () => { + setShowConfModal(true) + } + + const onSubmitCancel = async () => { + setShowConfModal(false) + if (!address || !kit) { + toast.error('Kit not connected') + return + } + const cancelOperation = async (k: ContractKit) => { + const grandaContract = await k.contracts.getGrandaMento() + const cancelTx = await grandaContract.cancelExchangeProposal(proposal.id) + // Gas price must be set manually because contractkit pre-populate it and + // its helpers for getting gas price are only meant for stable token prices + const gasPrice = await k.web3.eth.getGasPrice() + const cancelReceipt = await cancelTx.sendAndWaitForReceipt({ gasPrice }) + logger.info(`Tx receipt received for approval: ${cancelReceipt.transactionHash}`) + return cancelReceipt.transactionHash + } + const cancelOpWithTimeout = asyncTimeout(cancelOperation, SIGN_OPERATION_TIMEOUT) + try { + const txHashes = (await performActions(cancelOpWithTimeout)) as string[] + if (!txHashes || txHashes.length !== 1) throw new Error('Tx hashes not found') + toastToYourSuccess('Proposal cancelled', txHashes[1], network.explorer) + await dispatch(fetchProposals({ kit, force: true })) + } catch (err: any) { + if (err.message === PROMISE_TIMEOUT) { + toast.error('Action timed out') + } else { + toast.error('Unable to cancel proposal') + } + logger.error('Failed to cancel proposal', err) + } + } + + return ( + +
+ +

{`Granda Proposal ${proposalId}`}

+ +
+ +
+ + Cancel + +
+ setShowConfModal(false)} + submit={onSubmitCancel} + /> +
+ ) +} + +function SwapDetails({ + proposal: p, + blockscoutUrl, +}: { + proposal: GrandaProposal + blockscoutUrl: string +}) { + const fromToken = p.sellCelo ? NativeTokenId.CELO : p.stableTokenId + const toToken = p.sellCelo ? p.stableTokenId : NativeTokenId.CELO + const fromAmount = new BigNumber(fromWei(p.sellAmount)).integerValue().toFixed(2) + const toAmount = new BigNumber(fromWei(p.buyAmount)).integerValue().toFixed(2) + const effectiveRate = new BigNumber(p.buyAmount).div(p.sellAmount).toNumber() + const fromCeloRate = p.sellCelo ? effectiveRate : 1 / effectiveRate + + const from = { + amount: fromAmount, + weiAmount: p.sellAmount, + token: fromToken, + } + const to = { + amount: toAmount, + weiAmount: p.buyAmount, + token: toToken, + } + const rate = { + value: effectiveRate, + weiValue: '', // Not needed here + fromCeloValue: fromCeloRate.toFixed(2), + fromCeloWeiValue: '', // Not needed here + weiBasis: WEI_PER_UNIT, + lastUpdated: Date.now(), + isReady: true, + } + + return ( + <> + +
+
+
Proposer:
+
+ + {shortenAddress(p.exchanger, true)} + +
+
+ {!!p.approvalTimestamp && ( +
+
Approval time:
+
{new Date(p.approvalTimestamp * 1000).toLocaleString()}
+
+ )} + {!p.approvalTimestamp && ( +
+
Veto Period:
+
{(p.vetoPeriodSeconds / 86400).toFixed(2) + ' days'}
+
+ )} +
+
Proposal Status:
+
{p.state.toUpperCase()}
+
+
+ + ) +} + +interface Props { + isOpen: boolean + close: () => void + submit: () => void +} + +function CancelConfirmationModal({ isOpen, close, submit }: Props) { + return ( + +
+
+
+ +
+

Confirm Cancellation

+

+ Are you sure you want to cancel this proposal? +

+

This will refund your exchange amount.

+
+ + Cancel Proposal + +
+
+
+
+ ) +} diff --git a/src/features/granda/fetchConfig.ts b/src/features/granda/fetchConfig.ts new file mode 100644 index 0000000..2191e8b --- /dev/null +++ b/src/features/granda/fetchConfig.ts @@ -0,0 +1,68 @@ +import type { ContractKit } from '@celo/contractkit' +import { createAsyncThunk } from '@reduxjs/toolkit' +import type { AppDispatch, AppState } from 'src/app/store' +import { kitContractToNativeToken } from 'src/config/tokenMapping' +import { GrandaConfig, SizeLimits } from 'src/features/granda/types' +import { isValidAddress } from 'src/utils/addresses' +import { toWei } from 'src/utils/amount' +import { logger } from 'src/utils/logger' + +interface FetchConfigParams { + kit: ContractKit +} + +export const fetchConfig = createAsyncThunk< + GrandaConfig | null, + FetchConfigParams, + { dispatch: AppDispatch; state: AppState } +>('granda/fetchConfig', async (params, thunkAPI) => { + const { kit } = params + const config = thunkAPI.getState().granda.config + if (!config) { + logger.debug('Fetching granda config') + return _fetchConfig(kit) + } else { + return null + } +}) + +async function _fetchConfig(kit: ContractKit): Promise { + const contract = await kit.contracts.getGrandaMento() + const rawConfig = await contract.getConfig() + + const approver = rawConfig.approver + if (!approver || !isValidAddress(approver)) throw new Error(`Invalid approver: ${approver}`) + + const spread = rawConfig.spread.toNumber() + if (spread > 0.05 || spread < 0) throw new Error(`Invalid spread: ${spread}`) + + const vetoPeriodSeconds = rawConfig.vetoPeriodSeconds.toNumber() + if (vetoPeriodSeconds > 2592000 || vetoPeriodSeconds < 1000) + throw new Error(`Invalid veto period: ${vetoPeriodSeconds}`) + + const maxApprovalExchangeRateChange = rawConfig.maxApprovalExchangeRateChange.toNumber() + if (maxApprovalExchangeRateChange > 0.9 || maxApprovalExchangeRateChange < 0.01) + throw new Error(`Invalid approval exchange rate: ${maxApprovalExchangeRateChange}`) + + const exchangeLimits: SizeLimits = {} + for (const [name, limits] of rawConfig.exchangeLimits.entries()) { + const tokenId = kitContractToNativeToken(name) + const min = limits.minExchangeAmount + const max = limits.maxExchangeAmount + if (min.gt(toWei(1_000_000)) || min.lt(toWei(50_000))) + throw new Error(`Invalid exchange min: ${min}`) + if (max.gt(toWei(100_000_000)) || max.lt(toWei(1_000_000))) + throw new Error(`Invalid exchange max: ${max}`) + exchangeLimits[tokenId] = { + min: min.toFixed(0), + max: max.toFixed(0), + } + } + return { + approver, + spread, + vetoPeriodSeconds, + maxApprovalExchangeRateChange, + exchangeLimits, + } +} diff --git a/src/features/granda/fetchOracleRates.ts b/src/features/granda/fetchOracleRates.ts new file mode 100644 index 0000000..97bd66b --- /dev/null +++ b/src/features/granda/fetchOracleRates.ts @@ -0,0 +1,50 @@ +import type { ContractKit } from '@celo/contractkit' +import { createAsyncThunk } from '@reduxjs/toolkit' +import type { AppDispatch, AppState } from 'src/app/store' +import { MAX_EXCHANGE_RATE, MIN_EXCHANGE_RATE } from 'src/config/consts' +import { nativeTokenToKitToken } from 'src/config/tokenMapping' +import { NativeTokenId, StableTokenIds } from 'src/config/tokens' +import { OracleRates } from 'src/features/granda/types' +import { SimpleExchangeRate } from 'src/features/swap/types' +import { logger } from 'src/utils/logger' +import { areRatesStale } from 'src/utils/time' + +interface FetchOracleRatesParams { + kit: ContractKit +} + +export const fetchOracleRates = createAsyncThunk< + OracleRates | null, + FetchOracleRatesParams, + { dispatch: AppDispatch; state: AppState } +>('granda/fetchOracleRates', async (params, thunkAPI) => { + const { kit } = params + const oracleRates = thunkAPI.getState().granda.oracleRates + if (areRatesStale(oracleRates)) { + logger.debug('Fetching oracle rates') + const newRates: OracleRates = {} + for (const tokenId of StableTokenIds) { + const rate = await _fetchOracleRates(kit, tokenId) + newRates[tokenId] = rate + } + return newRates + } else { + return null + } +}) + +async function _fetchOracleRates( + kit: ContractKit, + tokenId: NativeTokenId +): Promise { + const token = nativeTokenToKitToken(tokenId) + const stableTokenAddress = await kit.celoTokens.getAddress(token) + const sortedOracles = await kit.contracts.getSortedOracles() + const { rate } = await sortedOracles.medianRate(stableTokenAddress) + if (!rate || rate.lt(MIN_EXCHANGE_RATE) || rate.gt(MAX_EXCHANGE_RATE)) + throw new Error(`Invalid oracle rate ${rate}`) + return { + rate: rate.toNumber(), + lastUpdated: Date.now(), + } +} diff --git a/src/features/granda/fetchProposals.ts b/src/features/granda/fetchProposals.ts new file mode 100644 index 0000000..ee492b0 --- /dev/null +++ b/src/features/granda/fetchProposals.ts @@ -0,0 +1,114 @@ +import type { CeloTokenType, ContractKit } from '@celo/contractkit' +import { ExchangeProposalState } from '@celo/contractkit/lib/wrappers/GrandaMento' +import { createAsyncThunk } from '@reduxjs/toolkit' +import type { AppDispatch, AppState } from 'src/app/store' +import { GRANDA_PROPOSAL_STALE_TIME } from 'src/config/consts' +import { kitTokenToNativeToken } from 'src/config/tokenMapping' +import { isStableToken, NativeTokenId } from 'src/config/tokens' +import { GrandaProposal, GrandaProposalState } from 'src/features/granda/types' +import { areAddressesEqual, isValidAddress } from 'src/utils/addresses' +import { logger } from 'src/utils/logger' +import { isStale } from 'src/utils/time' + +interface FetchProposalsParams { + kit: ContractKit + force?: boolean +} + +export const fetchProposals = createAsyncThunk< + Record | null, + FetchProposalsParams, + { dispatch: AppDispatch; state: AppState } +>('granda/fetchProposals', async (params, thunkAPI) => { + const { kit, force } = params + const proposalsLastUpdated = thunkAPI.getState().granda.proposalsLastUpdated + if (isStale(proposalsLastUpdated, GRANDA_PROPOSAL_STALE_TIME) || force) { + logger.debug('Fetching granda proposals') + return _fetchProposals(kit) + } else { + return null + } +}) + +async function _fetchProposals(kit: ContractKit): Promise> { + const contract = await kit.contracts.getGrandaMento() + const propCountBN = await contract.exchangeProposalCount() + const propCount = propCountBN.toNumber() + const proposals: Record = {} + if (propCount <= 0) return proposals + + // Get token addresses to map addr in proposal to token id + const tokenToAddr = await kit.celoTokens.getAddresses() + + // Validate and transform the proposal data to local models + for (let i = 1; i <= propCount; i++) { + const proposal = await contract.getExchangeProposal(i) + const id = proposal.id.toString() + const { + state, + exchanger, + stableToken: stableTokenAddr, + sellAmount, + buyAmount, + sellCelo, + vetoPeriodSeconds, + approvalTimestamp, + } = proposal + if (!isValidAddress(exchanger)) throw new Error(`Invalid proposal exchanger ${exchanger}`) + if (!isValidAddress(stableTokenAddr)) + throw new Error(`Invalid proposal stableToken ${stableTokenAddr}`) + if (!sellAmount || sellAmount.lte(0)) throw new Error(`Invalid sell amount ${sellAmount}`) + if (!buyAmount || buyAmount.lte(0)) throw new Error(`Invalid buy amount ${buyAmount}`) + if (!vetoPeriodSeconds || vetoPeriodSeconds.lte(0)) + throw new Error(`Invalid veto period ${vetoPeriodSeconds}`) + if (!approvalTimestamp || approvalTimestamp.lt(0)) + throw new Error(`Invalid approval time ${approvalTimestamp}`) + + const tokenId = findNativeTokenId(stableTokenAddr, tokenToAddr) + if (!isStableToken(tokenId)) throw new Error('Token is not known stabletoken') + + proposals[id] = { + id, + state: toGrandaProposalState(state), + exchanger, + stableTokenId: tokenId, + sellAmount: sellAmount.toFixed(0), + buyAmount: buyAmount.toFixed(0), + sellCelo, + vetoPeriodSeconds: vetoPeriodSeconds.toNumber(), + approvalTimestamp: approvalTimestamp.toNumber(), + } + } + return proposals +} + +function toGrandaProposalState(state: ExchangeProposalState) { + switch (state) { + case ExchangeProposalState.Proposed: + return GrandaProposalState.Proposed + case ExchangeProposalState.Approved: + return GrandaProposalState.Approved + case ExchangeProposalState.Executed: + return GrandaProposalState.Executed + case ExchangeProposalState.Cancelled: + return GrandaProposalState.Cancelled + default: + throw new Error(`Invalid proposal state: ${state}`) + } +} + +function findNativeTokenId( + stableTokenAddr: string, + tokenToAddr: { + [key in CeloTokenType]?: string + } +): NativeTokenId { + for (const _token of Object.keys(tokenToAddr)) { + const token = _token as CeloTokenType + const addr = tokenToAddr[token] + if (addr && areAddressesEqual(stableTokenAddr, addr)) { + return kitTokenToNativeToken(token) + } + } + throw new Error(`No token found for addr ${stableTokenAddr}`) +} diff --git a/src/features/granda/grandaSlice.ts b/src/features/granda/grandaSlice.ts new file mode 100644 index 0000000..d3c325f --- /dev/null +++ b/src/features/granda/grandaSlice.ts @@ -0,0 +1,87 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import { fetchConfig } from 'src/features/granda/fetchConfig' +import { fetchOracleRates } from 'src/features/granda/fetchOracleRates' +import { fetchProposals } from 'src/features/granda/fetchProposals' +import { + GrandaConfig, + GrandaFormValues, + GrandaProposal, + GrandaSubpage, + OracleRates, +} from 'src/features/granda/types' + +export interface GrandaState { + isActive: boolean // tracks if granda page was loaded to lazily fetch granda info + subpage: GrandaSubpage + viewProposalId: string | null // id of proposal for details view subpage + formValues: GrandaFormValues | null + proposals: Record + proposalsLastUpdated: number | null + oracleRates: OracleRates + config: GrandaConfig | null +} + +const initialState: GrandaState = { + isActive: false, + subpage: GrandaSubpage.Form, + viewProposalId: null, + formValues: null, + proposals: {}, + proposalsLastUpdated: null, + oracleRates: {}, + config: null, +} + +export const grandaSlice = createSlice({ + name: 'granda', + initialState, + reducers: { + activateGranda: (state) => { + state.isActive = true + }, + setSubpage: (state, action: PayloadAction) => { + state.subpage = action.payload + }, + setFormValues: (state, action: PayloadAction) => { + const values = action.payload + if (values) { + state.subpage = GrandaSubpage.Confirm + state.formValues = values + } else { + state.subpage = GrandaSubpage.Form + state.formValues = null + } + }, + viewProposal: (state, action: PayloadAction) => { + state.viewProposalId = action.payload + state.subpage = GrandaSubpage.View + }, + clearProposal: (state) => { + state.viewProposalId = null + state.subpage = GrandaSubpage.List + }, + reset: (state) => ({ ...initialState, isActive: state.isActive }), + }, + extraReducers: (builder) => { + builder.addCase(fetchProposals.fulfilled, (state, action) => { + const proposals = action.payload + if (!proposals) return + state.proposals = proposals + state.proposalsLastUpdated = Date.now() + }) + builder.addCase(fetchOracleRates.fulfilled, (state, action) => { + const rates = action.payload + if (!rates) return + state.oracleRates = rates + }) + builder.addCase(fetchConfig.fulfilled, (state, action) => { + const config = action.payload + if (!config) return + state.config = config + }) + }, +}) + +export const { activateGranda, setFormValues, setSubpage, viewProposal, clearProposal, reset } = + grandaSlice.actions +export const grandaReducer = grandaSlice.reducer diff --git a/src/features/granda/types.ts b/src/features/granda/types.ts new file mode 100644 index 0000000..aaeb4ee --- /dev/null +++ b/src/features/granda/types.ts @@ -0,0 +1,46 @@ +import { NativeTokenId } from 'src/config/tokens' +import { SimpleExchangeRate } from 'src/features/swap/types' + +export enum GrandaSubpage { + List = 'list', + View = 'view', + Form = 'form', + Confirm = 'confirm', +} + +export interface GrandaFormValues { + fromTokenId: NativeTokenId + toTokenId: NativeTokenId + fromAmount: number | string +} + +export enum GrandaProposalState { + Proposed = 'Proposed', + Approved = 'Approved', + Executed = 'Executed', + Cancelled = 'Cancelled', +} + +export interface GrandaProposal { + id: string + state: GrandaProposalState + exchanger: string + stableTokenId: NativeTokenId + sellAmount: string + buyAmount: string + sellCelo: boolean + vetoPeriodSeconds: number + approvalTimestamp: number +} + +export type OracleRates = Partial> + +export interface GrandaConfig { + approver: string + spread: number + vetoPeriodSeconds: number + maxApprovalExchangeRateChange: number + exchangeLimits: SizeLimits +} + +export type SizeLimits = Partial> diff --git a/src/features/granda/utils.ts b/src/features/granda/utils.ts new file mode 100644 index 0000000..fb5964e --- /dev/null +++ b/src/features/granda/utils.ts @@ -0,0 +1,77 @@ +import BigNumber from 'bignumber.js' +import { WEI_PER_UNIT } from 'src/config/consts' +import { NativeTokenId } from 'src/config/tokens' +import { OracleRates } from 'src/features/granda/types' +import { + ExchangeValues, + getDefaultExchangeValues, + parseInputExchangeAmount, +} from 'src/features/swap/utils' +import { fromWei, fromWeiRounded, NumberT, toWei } from 'src/utils/amount' +import { logger } from 'src/utils/logger' + +// Takes raw input and rates info and computes/formats to convenient form +export function getExchangeValues( + fromAmount: NumberT | null | undefined, + fromTokenId: NativeTokenId | null | undefined, + toTokenId: NativeTokenId | null | undefined, + spread: BigNumber.Value | null | undefined, + oracleRates: OracleRates +): ExchangeValues { + try { + // Return some defaults when values are missing + if (!fromTokenId || !toTokenId || !oracleRates || spread === null || spread === undefined) + return getDefaultExchangeValues() + + const sellCelo = fromTokenId === NativeTokenId.CELO + const stableTokenId = sellCelo ? toTokenId : fromTokenId + const fromCeloRate = oracleRates[stableTokenId] + if (!fromCeloRate) return getDefaultExchangeValues(fromTokenId, toTokenId) + + const { rate: fromCeloRateNum } = fromCeloRate + const fromCeloRateWei = toWei(fromCeloRateNum) + const toCeloRateNum = 1 / fromCeloRateNum + const toCeloRateWei = toWei(toCeloRateNum) + const exchangeRateNum = sellCelo ? fromCeloRateNum : toCeloRateNum + const exchangeRateWei = sellCelo ? fromCeloRateWei : toCeloRateWei + + const fromAmountWei = parseInputExchangeAmount(fromAmount, false) + const toAmountWei = getBuyAmount(fromAmountWei, exchangeRateNum, spread) + + return { + from: { + amount: fromWei(fromAmountWei).toFixed(2), + weiAmount: fromAmountWei.toString(), + token: fromTokenId, + }, + to: { + amount: fromWei(toAmountWei).toFixed(2), + weiAmount: toAmountWei.toString(), + token: toTokenId, + }, + rate: { + value: exchangeRateNum, + weiValue: exchangeRateWei.toString(), + fromCeloValue: fromWeiRounded(fromCeloRateWei, true), + fromCeloWeiValue: fromCeloRateWei.toString(), + weiBasis: WEI_PER_UNIT, + lastUpdated: fromCeloRate.lastUpdated, + isReady: true, + }, + stableTokenId, + } + } catch (error) { + logger.warn('Error computing exchange values', error) + return getDefaultExchangeValues() + } +} + +// Should match GetBuyAmount in https://github.com/celo-org/celo-monorepo/blob/master/packages/protocol/contracts/stability/GrandaMento.sol +function getBuyAmount( + amountInWei: BigNumber.Value, + exchangeRate: BigNumber.Value, + spread: BigNumber.Value +): BigNumber { + const adjustedAmount = new BigNumber(amountInWei).times(new BigNumber(1).minus(spread)) + return adjustedAmount.times(exchangeRate) +} diff --git a/src/features/polling/PollingWorker.tsx b/src/features/polling/PollingWorker.tsx index 531d6ac..31ac758 100644 --- a/src/features/polling/PollingWorker.tsx +++ b/src/features/polling/PollingWorker.tsx @@ -1,37 +1,69 @@ import { useContractKit } from '@celo-tools/use-contractkit' import { useEffect } from 'react' import { toast } from 'react-toastify' -import { useAppDispatch } from 'src/app/hooks' +import { useAppDispatch, useAppSelector } from 'src/app/hooks' import { STATUS_POLLER_DELAY } from 'src/config/consts' import { fetchBalances } from 'src/features/accounts/fetchBalances' import { fetchLatestBlock } from 'src/features/blocks/fetchLatestBlock' +import { fetchConfig } from 'src/features/granda/fetchConfig' +import { fetchOracleRates } from 'src/features/granda/fetchOracleRates' +import { fetchProposals } from 'src/features/granda/fetchProposals' import { fetchExchangeRates } from 'src/features/swap/fetchExchangeRates' import { logger } from 'src/utils/logger' import { useInterval } from 'src/utils/timeout' export function PollingWorker() { + const isGrandaActive = useAppSelector((s) => s.granda.isActive) const dispatch = useAppDispatch() const { address, kit, initialised } = useContractKit() + // TODO debounce toast errors + const onPoll = () => { if (!kit || !initialised) return - dispatch(fetchExchangeRates({ kit })).catch((err) => { - toast.error('Error retrieving exchange rates') - logger.error('Failed to retrieve exchange rates', err) - }) - dispatch(fetchLatestBlock({ kit })).catch((err) => { - toast.warn('Error retrieving latest block') - logger.error('Failed to retrieve latest block', err) - }) - if (address) { - dispatch(fetchBalances({ address, kit })).catch((err) => { - toast.error('Error retrieving account balances') - logger.error('Failed to retrieve balances', err) + dispatch(fetchExchangeRates({ kit })) + .unwrap() + .catch((err) => { + toast.warn('Error retrieving exchange rates') + logger.error('Failed to retrieve exchange rates', err) + }) + dispatch(fetchLatestBlock({ kit })) + .unwrap() + .catch((err) => { + toast.warn('Error retrieving latest block') + logger.error('Failed to retrieve latest block', err) }) + if (isGrandaActive) { + dispatch(fetchProposals({ kit })) + .unwrap() + .catch((err) => { + toast.warn('Error retrieving Granda proposals') + logger.error('Failed to retrieve granda proposals', err) + }) + dispatch(fetchConfig({ kit })) + .unwrap() + .catch((err) => { + toast.warn('Error retrieving Granda config') + logger.error('Failed to retrieve granda config', err) + }) + dispatch(fetchOracleRates({ kit })) + .unwrap() + .catch((err) => { + toast.warn('Error retrieving oracle rates') + logger.error('Failed to retrieve oracle rates', err) + }) + } + if (address) { + dispatch(fetchBalances({ address, kit })) + .unwrap() + .catch((err) => { + toast.warn('Error retrieving account balances') + logger.error('Failed to retrieve balances', err) + }) } } - useEffect(onPoll, [address, kit, initialised, dispatch]) + useEffect(onPoll, [isGrandaActive, address, kit, initialised, dispatch]) useInterval(onPoll, STATUS_POLLER_DELAY) diff --git a/src/features/swap/SettingsMenu.tsx b/src/features/swap/SettingsMenu.tsx new file mode 100644 index 0000000..ede19c0 --- /dev/null +++ b/src/features/swap/SettingsMenu.tsx @@ -0,0 +1,37 @@ +import useDropdownMenu from 'react-accessible-dropdown-menu-hook' +import { useAppDispatch, useAppSelector } from 'src/app/hooks' +import { IconButton } from 'src/components/buttons/IconButton' +import { SwitchButton } from 'src/components/buttons/SwitchButton' +import { setShowSlippage } from 'src/features/swap/swapSlice' +import Sliders from 'src/images/icons/sliders.svg' + +export function SettingsMenu() { + const showSlippage = useAppSelector((s) => s.swap.showSlippage) + const dispatch = useAppDispatch() + const onToggleSlippage = (checked: boolean) => { + dispatch(setShowSlippage(checked)) + } + + const { buttonProps, itemProps, isOpen } = useDropdownMenu(1) + + return ( + + ) +} diff --git a/src/features/swap/SwapConfirm.tsx b/src/features/swap/SwapConfirm.tsx index 9289cde..942d8bf 100644 --- a/src/features/swap/SwapConfirm.tsx +++ b/src/features/swap/SwapConfirm.tsx @@ -1,32 +1,30 @@ import { useContractKit } from '@celo-tools/use-contractkit' -import { ContractKit } from '@celo/contractkit' +import type { ContractKit } from '@celo/contractkit' import BigNumber from 'bignumber.js' import { useEffect } from 'react' import { toast } from 'react-toastify' import { useAppDispatch, useAppSelector } from 'src/app/hooks' -import { IconButton } from 'src/components/buttons/IconButton' +import { BackButton } from 'src/components/buttons/BackButton' +import { RefreshButton } from 'src/components/buttons/RefreshButton' import { SolidButton } from 'src/components/buttons/SolidButton' -import { TextLink } from 'src/components/buttons/TextLink' -import { config } from 'src/config/config' +import { toastToYourSuccess } from 'src/components/TxSuccessToast' import { MAX_EXCHANGE_RATE, MAX_EXCHANGE_TOKEN_SIZE, MIN_EXCHANGE_RATE, SIGN_OPERATION_TIMEOUT, } from 'src/config/consts' +import { getExchangeContract, getTokenContract } from 'src/config/tokenMapping' import { NativeTokenId, NativeTokens } from 'src/config/tokens' import { fetchBalances } from 'src/features/accounts/fetchBalances' -import { getExchangeContract, getTokenContract } from 'src/features/swap/contracts' import { fetchExchangeRates } from 'src/features/swap/fetchExchangeRates' import { setFormValues } from 'src/features/swap/swapSlice' import { SwapFormValues } from 'src/features/swap/types' -import { getMinBuyAmount, useExchangeValues } from 'src/features/swap/utils' -import LeftArrow from 'src/images/icons/arrow-left-circle.svg' -import RepeatArrow from 'src/images/icons/arrow-repeat.svg' +import { ExchangeValues, getExchangeValues, getMinBuyAmount } from 'src/features/swap/utils' import { TokenIcon } from 'src/images/tokens/TokenIcon' import { FloatingBox } from 'src/layout/FloatingBox' import { Color } from 'src/styles/Color' -import { fromWeiRounded } from 'src/utils/amount' +import { fromWeiRounded, getAdjustedAmount } from 'src/utils/amount' import { logger } from 'src/utils/logger' import { asyncTimeout, PROMISE_TIMEOUT } from 'src/utils/timeout' @@ -37,8 +35,9 @@ interface Props { export function SwapConfirm(props: Props) { const { fromAmount, fromTokenId, toTokenId, slippage } = props.formValues const toCeloRates = useAppSelector((s) => s.swap.toCeloRates) + const balances = useAppSelector((s) => s.account.balances) const dispatch = useAppDispatch() - const { address, kit, initialised, performActions } = useContractKit() + const { address, kit, initialised, network, performActions } = useContractKit() // Ensure invariants are met, otherwise return to swap form const isConfirmValid = fromAmount && fromTokenId && toTokenId && address && kit @@ -48,24 +47,26 @@ export function SwapConfirm(props: Props) { } }, [isConfirmValid, dispatch]) - const { from, to, rate, stableTokenId } = useExchangeValues( + const { from, to, rate, stableTokenId } = getExchangeValues( fromAmount, fromTokenId, toTokenId, toCeloRates ) - const minBuyAmountWei = getMinBuyAmount(from.weiAmount, slippage, rate.value) + const tokenBalance = balances[fromTokenId] + // Check if amount is almost equal to balance max, in which case use max + // Helps handle problems from imprecision in non-wei amount display + const finalFromAmount = getAdjustedAmount(from.weiAmount, tokenBalance) + const minBuyAmountWei = getMinBuyAmount(finalFromAmount, slippage, rate.value) const minBuyAmount = fromWeiRounded(minBuyAmountWei, true) - const fromToken = NativeTokens[fromTokenId] - const toToken = NativeTokens[toTokenId] const onSubmit = async () => { if (!address || !kit) { toast.error('Kit not connected') return } - if (new BigNumber(from.weiAmount).gt(MAX_EXCHANGE_TOKEN_SIZE)) { - toast.error('Amount too large') + if (new BigNumber(finalFromAmount).gt(MAX_EXCHANGE_TOKEN_SIZE)) { + toast.error('Amount seems too large') return } if (rate.value < MIN_EXCHANGE_RATE || rate.value > MAX_EXCHANGE_RATE) { @@ -73,15 +74,13 @@ export function SwapConfirm(props: Props) { return } - // TODO get adjusted amount? - const approvalOperation = async (k: ContractKit) => { const stableTokenId = fromTokenId === NativeTokenId.CELO ? toTokenId : fromTokenId const tokenContract = await getTokenContract(k, fromTokenId) const exchangeContract = await getExchangeContract(k, stableTokenId) const approveTx = await tokenContract.increaseAllowance( exchangeContract.address, - from.weiAmount + finalFromAmount ) // Gas price must be set manually because contractkit pre-populate it and // its helpers for getting gas price are only meant for stable token prices @@ -95,7 +94,7 @@ export function SwapConfirm(props: Props) { const exchangeOperation = async (k: ContractKit) => { const sellGold = fromTokenId === NativeTokenId.CELO const exchangeContract = await getExchangeContract(k, stableTokenId) - const exchangeTx = await exchangeContract.sell(from.weiAmount, minBuyAmountWei, sellGold) + const exchangeTx = await exchangeContract.sell(finalFromAmount, minBuyAmountWei, sellGold) const gasPrice = await k.web3.eth.getGasPrice() const exchangeReceipt = await exchangeTx.sendAndWaitForReceipt({ gasPrice }) logger.info(`Tx receipt received for swap: ${exchangeReceipt.transactionHash}`) @@ -110,7 +109,7 @@ export function SwapConfirm(props: Props) { exchangeOpWithTimeout )) as string[] if (!txHashes || txHashes.length !== 2) throw new Error('Tx hashes not found') - toast.success(, { autoClose: 15000 }) + toastToYourSuccess('Swap Complete!', txHashes[1], network.explorer) dispatch(setFormValues(null)) } catch (err: any) { if (err.message === PROMISE_TIMEOUT) { @@ -128,60 +127,22 @@ export function SwapConfirm(props: Props) { const onClickRefresh = () => { if (!kit || !initialised) return - dispatch(fetchExchangeRates({ kit })).catch((err) => { - toast.error('Error retrieving exchange rates') - logger.error('Failed to retrieve exchange rates', err) - }) + dispatch(fetchExchangeRates({ kit })) + .unwrap() + .catch((err) => { + toast.error('Error retrieving exchange rates') + logger.error('Failed to retrieve exchange rates', err) + }) } return (
- +

Confirm Swap

- -
-
-
-
- -
-
{fromToken.symbol}
-
{from.amount}
-
-
-
-
-
{toToken.symbol}
-
{to.amount}
-
- -
-
- -
-
-
-
- {rate.isReady ? `${rate.fromCeloValue} ${stableTokenId} : 1 CELO` : 'Loading...'} -
-
+
+
Max Slippage:
@@ -193,7 +154,7 @@ export function SwapConfirm(props: Props) {
- + Swap
@@ -201,6 +162,51 @@ export function SwapConfirm(props: Props) { ) } +interface SwapConfirmSummaryProps { + from: ExchangeValues['from'] + to: ExchangeValues['to'] + rate: ExchangeValues['rate'] + stableTokenId: NativeTokenId + mt?: string +} + +export function SwapConfirmSummary({ from, to, rate, stableTokenId, mt }: SwapConfirmSummaryProps) { + const fromToken = NativeTokens[from.token] + const toToken = NativeTokens[to.token] + + return ( +
+
+
+ +
+
{fromToken.symbol}
+
{from.amount}
+
+
+
+
+
{toToken.symbol}
+
{to.amount}
+
+ +
+
+ +
+
+
+
+ {rate.isReady ? `${rate.fromCeloValue} ${stableTokenId} : 1 CELO` : 'Loading...'} +
+
+
+ ) +} + function RightCircleArrow() { return (
@@ -219,14 +225,3 @@ function RightCircleArrow() {
) } - -function SuccessToast({ txHash }: { txHash: string }) { - return ( -
- Swap Complete!{' '} - - See Details - -
- ) -} diff --git a/src/features/swap/SwapForm.tsx b/src/features/swap/SwapForm.tsx index 1cce25b..b14b7e3 100644 --- a/src/features/swap/SwapForm.tsx +++ b/src/features/swap/SwapForm.tsx @@ -1,24 +1,22 @@ import { Connector, useContractKit } from '@celo-tools/use-contractkit' import { Field, Form, Formik, FormikErrors, useFormikContext } from 'formik' import { useCallback } from 'react' -import useDropdownMenu from 'react-accessible-dropdown-menu-hook' import { toast } from 'react-toastify' import { useAppDispatch, useAppSelector } from 'src/app/hooks' import { IconButton } from 'src/components/buttons/IconButton' import { SolidButton } from 'src/components/buttons/SolidButton' -import { SwitchButton } from 'src/components/buttons/SwitchButton' import { RadioInput } from 'src/components/input/RadioInput' import TokenSelectField, { TokenOption } from 'src/components/input/TokenSelectField' -import { MIN_ROUNDED_VALUE } from 'src/config/consts' import { CELO, cEUR, cUSD, isStableToken, NativeTokenId } from 'src/config/tokens' import { AccountBalances } from 'src/features/accounts/fetchBalances' -import { setFormValues, setShowSlippage } from 'src/features/swap/swapSlice' -import { SwapFormValues, ToCeloRates } from 'src/features/swap/types' -import { useExchangeValues } from 'src/features/swap/utils' +import { SettingsMenu } from 'src/features/swap/SettingsMenu' +import { setFormValues } from 'src/features/swap/swapSlice' +import { SwapFormValues } from 'src/features/swap/types' +import { useFormValidator } from 'src/features/swap/useFormValidator' +import { ExchangeValueFormatter, getExchangeValues } from 'src/features/swap/utils' import DownArrow from 'src/images/icons/arrow-down-short.svg' -import Sliders from 'src/images/icons/sliders.svg' import { FloatingBox } from 'src/layout/FloatingBox' -import { areAmountsNearlyEqual, fromWeiRounded, parseAmount, toWei } from 'src/utils/amount' +import { fromWeiRounded } from 'src/utils/amount' import { useTimeout } from 'src/utils/timeout' const initialValues: SwapFormValues = { @@ -35,8 +33,6 @@ const tokens = [ ] export function SwapForm() { - const { connect, address } = useContractKit() - const balances = useAppSelector((s) => s.account.balances) const { toCeloRates, showSlippage } = useAppSelector((s) => s.swap) @@ -44,62 +40,82 @@ export function SwapForm() { const onSubmit = (values: SwapFormValues) => { dispatch(setFormValues(values)) } + const validateForm = useFormValidator(balances) - const validateForm = (values?: SwapFormValues): FormikErrors => { - if (!values || !values.fromAmount) return { fromAmount: 'Amount Required' } - const parsedAmount = parseAmount(values.fromAmount) - if (!parsedAmount) return { fromAmount: 'Amount is Invalid' } - if (parsedAmount.lt(0)) return { fromAmount: 'Amount cannot be negative' } - if (parsedAmount.lt(MIN_ROUNDED_VALUE)) return { fromAmount: 'Amount too small' } - const tokenId = values.fromTokenId - const tokenBalance = balances[tokenId] - const weiAmount = toWei(parsedAmount) - if (weiAmount.gt(tokenBalance) && !areAmountsNearlyEqual(weiAmount, tokenBalance)) { - return { fromAmount: 'Amount exceeds balance' } - } - return {} - } + const valueFormatter: ExchangeValueFormatter = (fromAmount, fromTokenId, toTokenId) => + getExchangeValues(fromAmount, fromTokenId, toTokenId, toCeloRates) return ( -
+

Swap

- - initialValues={initialValues} + -
- - {showSlippage && } -
- -
- - + validateForm={validateForm} + /> ) } +interface SwapFormInnerProps { + balances: AccountBalances + showSlippage: boolean + onSubmit: (values: SwapFormValues) => void + validateForm: (values?: SwapFormValues) => FormikErrors + valueFormatter: ExchangeValueFormatter +} + +export function SwapFormInner({ + balances, + showSlippage, + onSubmit, + validateForm, + valueFormatter, +}: SwapFormInnerProps) { + const { connect, address } = useContractKit() + + return ( + + initialValues={initialValues} + onSubmit={onSubmit} + validate={validateForm} + validateOnChange={false} + validateOnBlur={false} + > +
+ + {showSlippage && } +
+ +
+ + + ) +} + interface FormInputProps { balances: AccountBalances - toCeloRates: ToCeloRates isConnected: boolean + valueFormatter: ExchangeValueFormatter } function SwapFormInputs(props: FormInputProps) { - const { balances, toCeloRates, isConnected } = props + const { balances, isConnected, valueFormatter } = props const { values, setFieldValue } = useFormikContext() - const { to, rate, stableTokenId } = useExchangeValues( + const { to, rate, stableTokenId } = valueFormatter( values.fromAmount, values.fromTokenId, - values.toTokenId, - toCeloRates + values.toTokenId ) const roundedBalance = fromWeiRounded(balances[values.fromTokenId]) @@ -128,7 +144,7 @@ function SwapFormInputs(props: FormInputProps) { return (
-
+
+ {text} ) } - -function SettingsMenu() { - const showSlippage = useAppSelector((s) => s.swap.showSlippage) - const dispatch = useAppDispatch() - const onToggleSlippage = (checked: boolean) => { - dispatch(setShowSlippage(checked)) - } - - const { buttonProps, itemProps, isOpen } = useDropdownMenu(1) - - return ( - - ) -} diff --git a/src/features/swap/contracts.ts b/src/features/swap/contracts.ts deleted file mode 100644 index fb6d5b2..0000000 --- a/src/features/swap/contracts.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { ContractKit } from '@celo/contractkit' -import { StableToken } from '@celo/contractkit' -import { NativeTokenId } from 'src/config/tokens' - -export async function getExchangeContract(kit: ContractKit, tokenId: NativeTokenId) { - switch (tokenId) { - case NativeTokenId.cUSD: - return kit.contracts.getExchange(StableToken.cUSD) - case NativeTokenId.cEUR: - return kit.contracts.getExchange(StableToken.cEUR) - default: - throw new Error(`Could not get contract for token ${tokenId}`) - } -} - -export async function getTokenContract(kit: ContractKit, tokenId: NativeTokenId) { - switch (tokenId) { - case NativeTokenId.cUSD: - return kit.contracts.getStableToken(StableToken.cUSD) - case NativeTokenId.cEUR: - return kit.contracts.getStableToken(StableToken.cEUR) - case NativeTokenId.CELO: - return kit.contracts.getGoldToken() - default: - throw new Error(`Could not get contract for token ${tokenId}`) - } -} diff --git a/src/features/swap/fetchExchangeRates.ts b/src/features/swap/fetchExchangeRates.ts index 3563eb6..b68e82b 100644 --- a/src/features/swap/fetchExchangeRates.ts +++ b/src/features/swap/fetchExchangeRates.ts @@ -1,11 +1,12 @@ import type { ContractKit } from '@celo/contractkit' import { createAsyncThunk } from '@reduxjs/toolkit' import type { AppDispatch, AppState } from 'src/app/store' -import { EXCHANGE_RATE_STALE_TIME, MAX_EXCHANGE_SPREAD } from 'src/config/consts' +import { MAX_EXCHANGE_SPREAD } from 'src/config/consts' +import { getExchangeContract } from 'src/config/tokenMapping' import { NativeTokenId, StableTokenIds } from 'src/config/tokens' -import { getExchangeContract } from 'src/features/swap/contracts' import { ExchangeRate, ToCeloRates } from 'src/features/swap/types' -import { isStale } from 'src/utils/time' +import { logger } from 'src/utils/logger' +import { areRatesStale } from 'src/utils/time' interface FetchExchangeRatesParams { kit: ContractKit @@ -21,12 +22,13 @@ export const fetchExchangeRates = createAsyncThunk< const { kit } = params const toCeloRates = thunkAPI.getState().swap.toCeloRates if (areRatesStale(toCeloRates)) { - const newToCeloRates: ToCeloRates = {} + logger.debug('Fetching exchange rates') + const newRates: ToCeloRates = {} for (const tokenId of StableTokenIds) { const rate = await _fetchExchangeRates(kit, tokenId) - newToCeloRates[tokenId] = rate + newRates[tokenId] = rate } - return newToCeloRates + return newRates } else { return null } @@ -53,11 +55,3 @@ async function _fetchExchangeRates( lastUpdated: Date.now(), } } - -function areRatesStale(rates: ToCeloRates) { - return ( - !rates || - !Object.keys(rates).length || - Object.values(rates).some((r) => isStale(r.lastUpdated, EXCHANGE_RATE_STALE_TIME)) - ) -} diff --git a/src/features/swap/types.ts b/src/features/swap/types.ts index 69c674f..ae50342 100644 --- a/src/features/swap/types.ts +++ b/src/features/swap/types.ts @@ -7,7 +7,7 @@ export interface SwapFormValues { slippage: string } -export type ToCeloRates = Record // token id to token<->CELO rate +export type ToCeloRates = Partial> // Raw Mento chain data from an Exchange contract export interface ExchangeRate { diff --git a/src/features/swap/useFormValidator.ts b/src/features/swap/useFormValidator.ts new file mode 100644 index 0000000..0a17a17 --- /dev/null +++ b/src/features/swap/useFormValidator.ts @@ -0,0 +1,36 @@ +import { FormikErrors } from 'formik' +import { useCallback } from 'react' +import { MIN_ROUNDED_VALUE } from 'src/config/consts' +import { NativeTokenId } from 'src/config/tokens' +import { AccountBalances } from 'src/features/accounts/fetchBalances' +import { SizeLimits } from 'src/features/granda/types' +import { SwapFormValues } from 'src/features/swap/types' +import { areAmountsNearlyEqual, parseAmount, toWei } from 'src/utils/amount' + +export function useFormValidator(balances: AccountBalances, sizeLimits?: SizeLimits | null) { + return useCallback( + (values?: SwapFormValues): FormikErrors => { + if (!values || !values.fromAmount) return { fromAmount: 'Amount Required' } + const parsedAmount = parseAmount(values.fromAmount) + if (!parsedAmount) return { fromAmount: 'Amount is Invalid' } + if (parsedAmount.lt(0)) return { fromAmount: 'Amount cannot be negative' } + if (parsedAmount.lt(MIN_ROUNDED_VALUE)) return { fromAmount: 'Amount too small' } + const tokenId = values.fromTokenId + const tokenBalance = balances[tokenId] + const weiAmount = toWei(parsedAmount) + if (weiAmount.gt(tokenBalance) && !areAmountsNearlyEqual(weiAmount, tokenBalance)) { + return { fromAmount: 'Amount exceeds balance' } + } + if (sizeLimits) { + const stableTokenId = + values.fromTokenId === NativeTokenId.CELO ? values.toTokenId : values.fromTokenId + const limits = sizeLimits[stableTokenId] + if (limits?.min && weiAmount.lt(limits?.min)) return { fromAmount: 'Amount below minimum' } + if (limits?.max && weiAmount.gt(limits?.max)) + return { fromAmount: 'Amount exceeds maximum' } + } + return {} + }, + [balances, sizeLimits] + ) +} diff --git a/src/features/swap/utils.ts b/src/features/swap/utils.ts index d1baeb1..ded2ed2 100644 --- a/src/features/swap/utils.ts +++ b/src/features/swap/utils.ts @@ -1,7 +1,3 @@ -// import { WEI_PER_UNIT } from 'src/config/consts' -// import { toWei } from 'src/utils/amount' -// import { logger } from 'src/utils/logger' - import BigNumber from 'bignumber.js' import { WEI_PER_UNIT } from 'src/config/consts' import { NativeTokenId } from 'src/config/tokens' @@ -15,7 +11,13 @@ import { } from 'src/utils/amount' import { logger } from 'src/utils/logger' -interface ExchangeValues { +export type ExchangeValueFormatter = ( + fromAmount: NumberT | null | undefined, + fromTokenId: NativeTokenId | null | undefined, + toTokenId: NativeTokenId | null | undefined +) => ExchangeValues + +export interface ExchangeValues { from: { amount: string weiAmount: string @@ -38,12 +40,12 @@ interface ExchangeValues { stableTokenId: NativeTokenId } -export function useExchangeValues( +// Takes raw input and rates info and computes/formats to convenient form +export function getExchangeValues( fromAmount: NumberT | null | undefined, fromTokenId: NativeTokenId | null | undefined, toTokenId: NativeTokenId | null | undefined, - toCeloRates: ToCeloRates, - isFromAmountWei = false + toCeloRates: ToCeloRates ): ExchangeValues { // Return some defaults when values are missing if (!fromTokenId || !toTokenId || !toCeloRates) return getDefaultExchangeValues() @@ -56,7 +58,7 @@ export function useExchangeValues( const { stableBucket, celoBucket, spread } = toCeloRate const [buyBucket, sellBucket] = sellCelo ? [stableBucket, celoBucket] : [celoBucket, stableBucket] - const fromAmountWei = parseInputExchangeAmount(fromAmount, isFromAmountWei) + const fromAmountWei = parseInputExchangeAmount(fromAmount, false) const { exchangeRateNum, exchangeRateWei, fromCeloRateWei, toAmountWei } = calcSimpleExchangeRate( fromAmountWei, buyBucket, @@ -89,7 +91,7 @@ export function useExchangeValues( } } -function parseInputExchangeAmount(amount: NumberT | null | undefined, isWei: boolean) { +export function parseInputExchangeAmount(amount: NumberT | null | undefined, isWei: boolean) { const parsed = parseAmountWithDefault(amount, 0) const parsedWei = isWei ? parsed : toWei(parsed) return BigNumber.max(parsedWei, 0) @@ -126,12 +128,12 @@ export function calcSimpleExchangeRate( return { exchangeRateNum, exchangeRateWei, fromCeloRateWei, toAmountWei } } catch (error) { - logger.warn('Error computing exchange values') + logger.warn('Error computing exchange values', error) return { exchangeRateNum: 0, exchangeRateWei: '0', fromCeloRateWei: '0', toAmountWei: '0' } } } -function getDefaultExchangeValues( +export function getDefaultExchangeValues( _fromToken?: NativeTokenId | null, _toToken?: NativeTokenId | null ): ExchangeValues { diff --git a/src/images/icons/arrow-right-short.svg b/src/images/icons/arrow-right-short.svg new file mode 100644 index 0000000..e26cfd4 --- /dev/null +++ b/src/images/icons/arrow-right-short.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/images/icons/info-circle.svg b/src/images/icons/info-circle.svg new file mode 100644 index 0000000..0498cef --- /dev/null +++ b/src/images/icons/info-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/images/icons/plus-circle-fill.svg b/src/images/icons/plus-circle-fill.svg new file mode 100644 index 0000000..4908445 --- /dev/null +++ b/src/images/icons/plus-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/images/icons/support-coin.png b/src/images/icons/support-coin.png deleted file mode 100644 index c37f815..0000000 Binary files a/src/images/icons/support-coin.png and /dev/null differ diff --git a/src/images/tokens/TokenIcon.tsx b/src/images/tokens/TokenIcon.tsx index 2e2a9f5..c4bd19c 100644 --- a/src/images/tokens/TokenIcon.tsx +++ b/src/images/tokens/TokenIcon.tsx @@ -7,7 +7,7 @@ import cUSDIcon from 'src/images/tokens/cUSD.svg' interface Props { token?: Token | null - size?: 's' | 'm' | 'l' + size?: 'xs' | 's' | 'm' | 'l' } function _TokenIcon({ token, size = 'm' }: Props) { @@ -19,7 +19,15 @@ function _TokenIcon({ token, size = 'm' }: Props) { const { actualSize, fontSize } = sizeValues[size] if (token && imgSrc) { - return {token.symbol} + return ( + + ) } if (token) { @@ -56,6 +64,10 @@ function _TokenIcon({ token, size = 'm' }: Props) { } const sizeValues = { + xs: { + actualSize: '22px', + fontSize: '13px', + }, s: { actualSize: '30px', fontSize: '15px', diff --git a/src/layout/AppLayout.tsx b/src/layout/AppLayout.tsx index 8c6e554..c4edbd1 100644 --- a/src/layout/AppLayout.tsx +++ b/src/layout/AppLayout.tsx @@ -1,7 +1,9 @@ +import { useContractKit } from '@celo-tools/use-contractkit' import { PropsWithChildren, useEffect } from 'react' import Modal from 'react-modal' import { Footer } from 'src/components/nav/Footer' import { Header } from 'src/components/nav/Header' +import { NULL_ADDRESS } from 'src/config/consts' import { PollingWorker } from 'src/features/polling/PollingWorker' import { HeadMeta } from 'src/layout/HeadMeta' @@ -10,12 +12,21 @@ interface Props { } export function AppLayout({ pathName, children }: PropsWithChildren) { - // Required to prevent react-modal from showing aria related error + // Prevent react-modal from showing aria related error // Note react-modal not used directly, it's part of use-contractkit useEffect(() => { Modal.setAppElement('#__next') }, []) + // Prevent web3 from spamming errors due to missing ENS on Celo + // Error: https://github.com/ChainSafe/web3.js/blob/1.x/packages/web3-eth-ens/src/ENS.js#L526 + // Related: https://github.com/ChainSafe/web3.js/issues/3787 + // Related: https://github.com/ChainSafe/web3.js/issues/3010 + const { kit } = useContractKit() + useEffect(() => { + kit.web3.eth.ens.registryAddress = NULL_ADDRESS + }, [kit]) + return ( <> diff --git a/src/layout/HrDivider.tsx b/src/layout/HrDivider.tsx index fc4aeaf..0873ceb 100644 --- a/src/layout/HrDivider.tsx +++ b/src/layout/HrDivider.tsx @@ -4,5 +4,5 @@ interface Props { export function HrDivider(props: Props) { const { classes } = props - return
+ return
} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 68075f2..ea3b96e 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -12,6 +12,7 @@ import { config } from 'src/config/config' import { AppLayout } from 'src/layout/AppLayout' import 'src/styles/fonts.css' import 'src/styles/globals.css' +import 'src/vendor/inpage-metamask' const nextPersistConfig = { method: 'localStorage', diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 6e03a94..a843716 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -28,12 +28,12 @@ class MyDocument extends Document { - + + - - +
diff --git a/src/pages/about.tsx b/src/pages/about.tsx index 7e0c145..6da4b4e 100644 --- a/src/pages/about.tsx +++ b/src/pages/about.tsx @@ -47,7 +47,9 @@ export default function AboutPage() { .

-

{`Version: ${config.version || 'Unknown'}`}

+

{`Version: ${ + config.version || 'Unknown' + } [BETA]`}

) diff --git a/src/pages/granda.tsx b/src/pages/granda.tsx index a943b8f..92d3750 100644 --- a/src/pages/granda.tsx +++ b/src/pages/granda.tsx @@ -1,23 +1,31 @@ -import Image from 'next/image' -import { TextLink } from 'src/components/buttons/TextLink' -import { config } from 'src/config/config' -import Support from 'src/images/icons/support-coin.png' -import { FloatingBox } from 'src/layout/FloatingBox' +import { useEffect } from 'react' +import { useAppDispatch, useAppSelector } from 'src/app/hooks' +import { activateGranda, setSubpage } from 'src/features/granda/grandaSlice' +import { ProposalConfirm } from 'src/features/granda/ProposalConfirm' +import { ProposalForm } from 'src/features/granda/ProposalForm' +import { ProposalList } from 'src/features/granda/ProposalList' +import { ProposalView } from 'src/features/granda/ProposalView' +import { GrandaSubpage } from 'src/features/granda/types' export default function GrandaPage() { + const subpage = useAppSelector((s) => s.granda.subpage) + const dispatch = useAppDispatch() + + useEffect(() => { + dispatch(activateGranda()) + // Restore subpage to list if users leaves granda + return () => { + dispatch(setSubpage(GrandaSubpage.Form)) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + return (
- -

Granda Mento Coming Soon!

-

- Suport for Granda Mento is in progress! Check back here in a few weeks or join the{' '} - - Discord - {' '} - for updates. -

- Building -
+ {subpage === GrandaSubpage.List && } + {subpage === GrandaSubpage.View && } + {subpage === GrandaSubpage.Form && } + {subpage === GrandaSubpage.Confirm && }
) } diff --git a/src/styles/modals.ts b/src/styles/modals.ts new file mode 100644 index 0000000..2a2aa77 --- /dev/null +++ b/src/styles/modals.ts @@ -0,0 +1,14 @@ +export const defaultModalStyles = { + content: { + top: '50%', + left: '50%', + right: 'auto', + bottom: 'auto', + transform: 'translate(-50%, -50%)', + border: 'unset', + background: 'unset', + padding: 'unset', + borderRadius: 10, + boxShadow: '0px 3px 4px 0px rgba(0, 0, 0, 0.15)', + }, +} diff --git a/src/utils/amount.ts b/src/utils/amount.ts index 7a8a1c1..425fd8a 100644 --- a/src/utils/amount.ts +++ b/src/utils/amount.ts @@ -71,3 +71,18 @@ export function areAmountsNearlyEqual(amountInWei1: BigNumber, amountInWei2: Num // Is difference btwn amount and balance less than min amount shown for token return amountInWei1.minus(amountInWei2).abs().lt(minValueWei) } + +// Get amount that is adjusted when user input is nearly the same as max value +export function getAdjustedAmount( + _amountInWei: BigNumber.Value, + _maxAmount: BigNumber.Value +): BigNumber { + const amountInWei = new BigNumber(_amountInWei) + const maxAmount = new BigNumber(_maxAmount) + if (areAmountsNearlyEqual(amountInWei, maxAmount)) { + return maxAmount + } else { + // Just the amount entered, no adjustment needed + return amountInWei + } +} diff --git a/src/utils/time.ts b/src/utils/time.ts index 6ba2176..20cdcdf 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -1,7 +1,17 @@ +import { EXCHANGE_RATE_STALE_TIME } from 'src/config/consts' + export function isStale(lastUpdated: number | null, staleTime: number) { return !lastUpdated || Date.now() - lastUpdated > staleTime } +export function areRatesStale(rates: Record) { + return ( + !rates || + !Object.keys(rates).length || + Object.values(rates).some((r) => isStale(r.lastUpdated, EXCHANGE_RATE_STALE_TIME)) + ) +} + export function areDatesSameDay(d1: Date, d2: Date) { return ( d1.getDate() === d2.getDate() && diff --git a/src/vendor/inpage-metamask.js b/src/vendor/inpage-metamask.js new file mode 100644 index 0000000..ecc0883 --- /dev/null +++ b/src/vendor/inpage-metamask.js @@ -0,0 +1,27 @@ +// Copied from https://github.com/MyCryptoHQ/MyCrypto/blob/master/src/vendor/inpage-metamask.js +// But updated to use newer packages + +import LocalMessageDuplexStream from 'post-message-stream' +import { initProvider } from '@metamask/inpage-provider' + +// Firefox Metamask Hack +// Due to https://github.com/MetaMask/metamask-extension/issues/3133 +;(() => { + if ( + typeof window !== 'undefined' && + !window.ethereum && + !window.web3 && + navigator.userAgent.includes('Firefox') + ) { + // setup background connection + const metamaskStream = new LocalMessageDuplexStream({ + name: 'inpage', + target: 'contentscript', + }) + + // this will initialize the provider and set it as window.ethereum + initProvider({ + connectionStream: metamaskStream, + }) + } +})() diff --git a/tsconfig.json b/tsconfig.json index b63f99a..25b3828 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "baseUrl": ".", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, + "incremental": true, "isolatedModules": true, "lib": ["dom", "dom.iterable", "esnext"], "module": "esnext", @@ -18,6 +19,6 @@ "strict": true, "target": "es2020" }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "src/vendor/*"], "exclude": ["node_modules"] } diff --git a/yarn.lock b/yarn.lock index a349bae..d75409b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -292,20 +292,27 @@ core-js-pure "^3.15.0" regenerator-runtime "^0.13.4" -"@babel/runtime@7.15.3": - version "7.15.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b" - integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA== +"@babel/runtime@7.15.4": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" + integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.14.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.15.4": + version "7.16.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" + integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.14.5", "@babel/template@^7.3.3": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" @@ -958,27 +965,27 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.2.5": - version "27.2.5" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.2.5.tgz#bddbf8d41c191f17b52bf0c9e6c0d18605e35d6e" - integrity sha512-smtlRF9vNKorRMCUtJ+yllIoiY8oFmfFG7xlzsAE76nKEwXNhjPOJIsc7Dv+AUitVt76t+KjIpUP9m98Crn2LQ== +"@jest/console@^27.3.1": + version "27.3.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.3.1.tgz#e8ea3a475d3f8162f23d69efbfaa9cbe486bee93" + integrity sha512-RkFNWmv0iui+qsOr/29q9dyfKTTT5DCuP31kUwg7rmOKPT/ozLeGLKJKVIiOfbiKyleUZKIrHwhmiZWVe8IMdw== dependencies: "@jest/types" "^27.2.5" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.2.5" - jest-util "^27.2.5" + jest-message-util "^27.3.1" + jest-util "^27.3.1" slash "^3.0.0" -"@jest/core@^27.2.5": - version "27.2.5" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.2.5.tgz#854c314708cee0d892ac4f531b9129f00a21ee69" - integrity sha512-VR7mQ+jykHN4WO3OvusRJMk4xCa2MFLipMS+43fpcRGaYrN1KwMATfVEXif7ccgFKYGy5D1TVXTNE4mGq/KMMA== +"@jest/core@^27.3.1": + version "27.3.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.3.1.tgz#04992ef1b58b17c459afb87ab56d81e63d386925" + integrity sha512-DMNE90RR5QKx0EA+wqe3/TNEwiRpOkhshKNxtLxd4rt3IZpCt+RSL+FoJsGeblRZmqdK4upHA/mKKGPPRAifhg== dependencies: - "@jest/console" "^27.2.5" - "@jest/reporters" "^27.2.5" - "@jest/test-result" "^27.2.5" - "@jest/transform" "^27.2.5" + "@jest/console" "^27.3.1" + "@jest/reporters" "^27.3.1" + "@jest/test-result" "^27.3.1" + "@jest/transform" "^27.3.1" "@jest/types" "^27.2.5" "@types/node" "*" ansi-escapes "^4.2.1" @@ -986,64 +993,64 @@ emittery "^0.8.1" exit "^0.1.2" graceful-fs "^4.2.4" - jest-changed-files "^27.2.5" - jest-config "^27.2.5" - jest-haste-map "^27.2.5" - jest-message-util "^27.2.5" + jest-changed-files "^27.3.0" + jest-config "^27.3.1" + jest-haste-map "^27.3.1" + jest-message-util "^27.3.1" jest-regex-util "^27.0.6" - jest-resolve "^27.2.5" - jest-resolve-dependencies "^27.2.5" - jest-runner "^27.2.5" - jest-runtime "^27.2.5" - jest-snapshot "^27.2.5" - jest-util "^27.2.5" - jest-validate "^27.2.5" - jest-watcher "^27.2.5" + jest-resolve "^27.3.1" + jest-resolve-dependencies "^27.3.1" + jest-runner "^27.3.1" + jest-runtime "^27.3.1" + jest-snapshot "^27.3.1" + jest-util "^27.3.1" + jest-validate "^27.3.1" + jest-watcher "^27.3.1" micromatch "^4.0.4" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.2.5": - version "27.2.5" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.2.5.tgz#b85517ccfcec55690c82c56f5a01a3b30c5e3c84" - integrity sha512-XvUW3q6OUF+54SYFCgbbfCd/BKTwm5b2MGLoc2jINXQLKQDTCS2P2IrpPOtQ08WWZDGzbhAzVhOYta3J2arubg== +"@jest/environment@^27.3.1": + version "27.3.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.3.1.tgz#2182defbce8d385fd51c5e7c7050f510bd4c86b1" + integrity sha512-BCKCj4mOVLme6Tanoyc9k0ultp3pnmuyHw73UHRPeeZxirsU/7E3HC4le/VDb/SMzE1JcPnto+XBKFOcoiJzVw== dependencies: - "@jest/fake-timers" "^27.2.5" + "@jest/fake-timers" "^27.3.1" "@jest/types" "^27.2.5" "@types/node" "*" - jest-mock "^27.2.5" + jest-mock "^27.3.0" -"@jest/fake-timers@^27.2.5": - version "27.2.5" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.2.5.tgz#0c7e5762d7bfe6e269e7b49279b097a52a42f0a0" - integrity sha512-ZGUb6jg7BgwY+nmO0TW10bc7z7Hl2G/UTAvmxEyZ/GgNFoa31tY9/cgXmqcxnnZ7o5Xs7RAOz3G1SKIj8IVDlg== +"@jest/fake-timers@^27.3.1": + version "27.3.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.3.1.tgz#1fad860ee9b13034762cdb94266e95609dfce641" + integrity sha512-M3ZFgwwlqJtWZ+QkBG5NmC23A9w+A6ZxNsO5nJxJsKYt4yguBd3i8TpjQz5NfCX91nEve1KqD9RA2Q+Q1uWqoA== dependencies: "@jest/types" "^27.2.5" "@sinonjs/fake-timers" "^8.0.1" "@types/node" "*" - jest-message-util "^27.2.5" - jest-mock "^27.2.5" - jest-util "^27.2.5" + jest-message-util "^27.3.1" + jest-mock "^27.3.0" + jest-util "^27.3.1" -"@jest/globals@^27.2.5": - version "27.2.5" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.2.5.tgz#4115538f98ed6cee4051a90fdbd0854062902099" - integrity sha512-naRI537GM+enFVJQs6DcwGYPn/0vgJNb06zGVbzXfDfe/epDPV73hP1vqO37PqSKDeOXM2KInr6ymYbL1HTP7g== +"@jest/globals@^27.3.1": + version "27.3.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.3.1.tgz#ce1dfb03d379237a9da6c1b99ecfaca1922a5f9e" + integrity sha512-Q651FWiWQAIFiN+zS51xqhdZ8g9b88nGCobC87argAxA7nMfNQq0Q0i9zTfQYgLa6qFXk2cGANEqfK051CZ8Pg== dependencies: - "@jest/environment" "^27.2.5" + "@jest/environment" "^27.3.1" "@jest/types" "^27.2.5" - expect "^27.2.5" + expect "^27.3.1" -"@jest/reporters@^27.2.5": - version "27.2.5" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.2.5.tgz#65198ed1f3f4449e3f656129764dc6c5bb27ebe3" - integrity sha512-zYuR9fap3Q3mxQ454VWF8I6jYHErh368NwcKHWO2uy2fwByqBzRHkf9j2ekMDM7PaSTWcLBSZyd7NNxR1iHxzQ== +"@jest/reporters@^27.3.1": + version "27.3.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.3.1.tgz#28b5c1f5789481e23788048fa822ed15486430b9" + integrity sha512-m2YxPmL9Qn1emFVgZGEiMwDntDxRRQ2D58tiDQlwYTg5GvbFOKseYCcHtn0WsI8CG4vzPglo3nqbOiT8ySBT/w== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.2.5" - "@jest/test-result" "^27.2.5" - "@jest/transform" "^27.2.5" + "@jest/console" "^27.3.1" + "@jest/test-result" "^27.3.1" + "@jest/transform" "^27.3.1" "@jest/types" "^27.2.5" "@types/node" "*" chalk "^4.0.0" @@ -1056,10 +1063,10 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.0.2" - jest-haste-map "^27.2.5" - jest-resolve "^27.2.5" - jest-util "^27.2.5" - jest-worker "^27.2.5" + jest-haste-map "^27.3.1" + jest-resolve "^27.3.1" + jest-util "^27.3.1" + jest-worker "^27.3.1" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" @@ -1075,30 +1082,30 @@ graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@^27.2.5": - version "27.2.5" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.2.5.tgz#e9f73cf6cd5e2cc6eb3105339248dea211f9320e" - integrity sha512-ub7j3BrddxZ0BdSnM5JCF6cRZJ/7j3wgdX0+Dtwhw2Po+HKsELCiXUTvh+mgS4/89mpnU1CPhZxe2mTvuLPJJg== +"@jest/test-result@^27.3.1": + version "27.3.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.3.1.tgz#89adee8b771877c69b3b8d59f52f29dccc300194" + integrity sha512-mLn6Thm+w2yl0opM8J/QnPTqrfS4FoXsXF2WIWJb2O/GBSyResL71BRuMYbYRsGt7ELwS5JGcEcGb52BNrumgg== dependencies: - "@jest/console" "^27.2.5" + "@jest/console" "^27.3.1" "@jest/types" "^27.2.5" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.2.5": - version "27.2.5" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.2.5.tgz#ed5ae91c00e623fb719111d58e380395e16cefbb" - integrity sha512-8j8fHZRfnjbbdMitMAGFKaBZ6YqvFRFJlMJzcy3v75edTOqc7RY65S9JpMY6wT260zAcL2sTQRga/P4PglCu3Q== +"@jest/test-sequencer@^27.3.1": + version "27.3.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.3.1.tgz#4b3bde2dbb05ee74afdae608cf0768e3354683b1" + integrity sha512-siySLo07IMEdSjA4fqEnxfIX8lB/lWYsBPwNFtkOvsFQvmBrL3yj3k3uFNZv/JDyApTakRpxbKLJ3CT8UGVCrA== dependencies: - "@jest/test-result" "^27.2.5" + "@jest/test-result" "^27.3.1" graceful-fs "^4.2.4" - jest-haste-map "^27.2.5" - jest-runtime "^27.2.5" + jest-haste-map "^27.3.1" + jest-runtime "^27.3.1" -"@jest/transform@^27.2.5": - version "27.2.5" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.2.5.tgz#02b08862a56dbedddf0ba3c2eae41e049a250e29" - integrity sha512-29lRtAHHYGALbZOx343v0zKmdOg4Sb0rsA1uSv0818bvwRhs3TyElOmTVXlrw0v1ZTqXJCAH/cmoDXimBhQOJQ== +"@jest/transform@^27.3.1": + version "27.3.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.3.1.tgz#ff80eafbeabe811e9025e4b6f452126718455220" + integrity sha512-3fSvQ02kuvjOI1C1ssqMVBKJpZf6nwoCiSu00zAKh5nrp3SptNtZy/8s5deayHnqxhjD9CWDJ+yqQwuQ0ZafXQ== dependencies: "@babel/core" "^7.1.0" "@jest/types" "^27.2.5" @@ -1107,9 +1114,9 @@ convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.2.4" - jest-haste-map "^27.2.5" + jest-haste-map "^27.3.1" jest-regex-util "^27.0.6" - jest-util "^27.2.5" + jest-util "^27.3.1" micromatch "^4.0.4" pirates "^4.0.1" slash "^3.0.0" @@ -1216,38 +1223,53 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.50.0.tgz#29c6419e8379d496ab6d0426eadf3c4d100cd186" integrity sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA== +"@metamask/inpage-provider@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@metamask/inpage-provider/-/inpage-provider-6.0.1.tgz#09bf3a3157d604fb511133c5a6e8eb1178d776bc" + integrity sha512-kcc2Tmq6bgjMyYxCgDJczdCAUBmAR4FPo+zmN0np02y/KNgDlzuKLdsrv3Y9B/S5XTxjuW95xYtMaxXZ5drEkQ== + dependencies: + eth-json-rpc-errors "^2.0.2" + fast-deep-equal "^2.0.1" + json-rpc-engine "^5.1.5" + json-rpc-middleware-stream "^2.1.1" + loglevel "^1.6.1" + obj-multiplex "^1.0.0" + obs-store "^4.0.3" + pump "^3.0.0" + safe-event-emitter "^1.0.1" + "@metamask/jazzicon@https://github.com/jmrossy/jazzicon#7a8df28": version "2.1.0" resolved "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6" dependencies: mersenne-twister "^1.1.0" -"@napi-rs/triples@^1.0.3": +"@napi-rs/triples@1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@napi-rs/triples/-/triples-1.0.3.tgz#76d6d0c3f4d16013c61e45dfca5ff1e6c31ae53c" integrity sha512-jDJTpta+P4p1NZTFVLHJ/TLFVYVcOqv6l8xwOeBKNPMgY/zDYH/YH7SJbvrr/h1RcS9GzbPcLKGzpuK9cV56UA== -"@next/env@11.1.2": - version "11.1.2" - resolved "https://registry.yarnpkg.com/@next/env/-/env-11.1.2.tgz#27996efbbc54c5f949f5e8c0a156e3aa48369b99" - integrity sha512-+fteyVdQ7C/OoulfcF6vd1Yk0FEli4453gr8kSFbU8sKseNSizYq6df5MKz/AjwLptsxrUeIkgBdAzbziyJ3mA== +"@next/env@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/env/-/env-12.0.3.tgz#e676b4d1454d8b6be433a348e99f2b8276ab6cd7" + integrity sha512-QcdlpcwIH9dYcVlNAU+gXaqHA/omskbRlb+R3vN7LlB2EgLt+9WQwbokcHOsNyt4pI7kDM67W4tr9l7dWnlGdQ== -"@next/eslint-plugin-next@11.1.2": - version "11.1.2" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-11.1.2.tgz#f26cf90bcb6cd2e4645e2ba253bbc9aaaa43a170" - integrity sha512-cN+ojHRsufr9Yz0rtvjv8WI5En0RPZRJnt0y16Ha7DD+0n473evz8i1ETEJHmOLeR7iPJR0zxRrxeTN/bJMOjg== +"@next/eslint-plugin-next@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.0.3.tgz#3945c251d551bacc3712d4a18d6ca56d2938f175" + integrity sha512-P7i+bMypneQcoRN+CX79xssvvIJCaw7Fndzbe7/lB0+LyRbVvGVyMUsFmLLbSxtZq4hvFMJ1p8wML/gsulMZWQ== dependencies: glob "7.1.7" -"@next/polyfill-module@11.1.2": - version "11.1.2" - resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-11.1.2.tgz#1fe92c364fdc81add775a16c678f5057c6aace98" - integrity sha512-xZmixqADM3xxtqBV0TpAwSFzWJP0MOQzRfzItHXf1LdQHWb0yofHHC+7eOrPFic8+ZGz5y7BdPkkgR1S25OymA== +"@next/polyfill-module@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-12.0.3.tgz#4217e5284762124bf9fe2505622c4de89998f7a2" + integrity sha512-fgjVjdCk0Jq627d/N33oQIJjWrcKtzw6Dfa2PfypoIJ35/xFIKgs6mPyvq8cg3Ao5b7dEn9+Rw45PGjlY5e7JA== -"@next/react-dev-overlay@11.1.2": - version "11.1.2" - resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-11.1.2.tgz#73795dc5454b7af168bac93df7099965ebb603be" - integrity sha512-rDF/mGY2NC69mMg2vDqzVpCOlWqnwPUXB2zkARhvknUHyS6QJphPYv9ozoPJuoT/QBs49JJd9KWaAzVBvq920A== +"@next/react-dev-overlay@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-12.0.3.tgz#d85a609bf7d75eb190940d0fc64eff94c0e4a478" + integrity sha512-gHfDEVHFeTUpQMcyytzvkuOu+5DQXjXbCbQHuavFftYrlHqXfzYFsa+wERff+g4/0IzEvcYVp3F4gdmynWfUog== dependencies: "@babel/code-frame" "7.12.11" anser "1.4.9" @@ -1256,42 +1278,70 @@ css.escape "1.5.1" data-uri-to-buffer "3.0.1" platform "1.3.6" - shell-quote "1.7.2" + shell-quote "1.7.3" source-map "0.8.0-beta.0" stacktrace-parser "0.1.10" - strip-ansi "6.0.0" - -"@next/react-refresh-utils@11.1.2": - version "11.1.2" - resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-11.1.2.tgz#44ea40d8e773e4b77bad85e24f6ac041d5e4b4a5" - integrity sha512-hsoJmPfhVqjZ8w4IFzoo8SyECVnN+8WMnImTbTKrRUHOVJcYMmKLL7xf7T0ft00tWwAl/3f3Q3poWIN2Ueql/Q== - -"@next/swc-darwin-arm64@11.1.2": - version "11.1.2" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-11.1.2.tgz#93226c38db488c4b62b30a53b530e87c969b8251" - integrity sha512-hZuwOlGOwBZADA8EyDYyjx3+4JGIGjSHDHWrmpI7g5rFmQNltjlbaefAbiU5Kk7j3BUSDwt30quJRFv3nyJQ0w== - -"@next/swc-darwin-x64@11.1.2": - version "11.1.2" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-11.1.2.tgz#792003989f560c00677b5daeff360b35b510db83" - integrity sha512-PGOp0E1GisU+EJJlsmJVGE+aPYD0Uh7zqgsrpD3F/Y3766Ptfbe1lEPPWnRDl+OzSSrSrX1lkyM/Jlmh5OwNvA== - -"@next/swc-linux-x64-gnu@11.1.2": - version "11.1.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-11.1.2.tgz#8216b2ae1f21f0112958735c39dd861088108f37" - integrity sha512-YcDHTJjn/8RqvyJVB6pvEKXihDcdrOwga3GfMv/QtVeLphTouY4BIcEUfrG5+26Nf37MP1ywN3RRl1TxpurAsQ== - -"@next/swc-win32-x64-msvc@11.1.2": - version "11.1.2" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-11.1.2.tgz#e15824405df137129918205e43cb5e9339589745" - integrity sha512-e/pIKVdB+tGQYa1cW3sAeHm8gzEri/HYLZHT4WZojrUxgWXqx8pk7S7Xs47uBcFTqBDRvK3EcQpPLf3XdVsDdg== - -"@node-rs/helper@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@node-rs/helper/-/helper-1.2.1.tgz#e079b05f21ff4329d82c4e1f71c0290e4ecdc70c" - integrity sha512-R5wEmm8nbuQU0YGGmYVjEc0OHtYsuXdpRG+Ut/3wZ9XAvQWyThN08bTh2cBJgoZxHQUPtvRfeQuxcAgLuiBISg== - dependencies: - "@napi-rs/triples" "^1.0.3" + strip-ansi "6.0.1" + +"@next/react-refresh-utils@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-12.0.3.tgz#1389b0370e258634432d6dd78f889c09a8328e10" + integrity sha512-YPtlfvkYh/4MvNNm5w3uwo+1KPMg67snzr5CuexbRewsu2ITaF7f0bh0Jcayi20wztk8SgWjNz1bmF8j9qbWIw== + +"@next/swc-android-arm64@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.0.3.tgz#8b99b3e7f13dda1f4c3c6dc83af73d8f40afecd5" + integrity sha512-40sOl9/50aamX0dEMrecqJQcUrRK47D7S9F66ulrZmz+5Ujp0lnP1rBOXngo0PZMecfU1tr7zbNubiAMDxfCxw== + +"@next/swc-darwin-arm64@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.0.3.tgz#a385e610fb4a20c47355520b82a79d08e0f6441e" + integrity sha512-iKSe2hCMB51Ft41cNAxZk6St1rBlqSRtBSl4oO0zJlGu7bCxXCGCJ058/OLvYxcNWgz7ODOApObm3Yjv8XEvxg== + +"@next/swc-darwin-x64@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.0.3.tgz#0405a3838a652b7bb44c5cd5d920c11240194385" + integrity sha512-/BcnfLyhIj4rgU3yVDfD8uXK2TcNYIdflYHKkjFxd3/J1GWOtBN31m0dB8fL0h5LdW11kzaXvVvab3f5ilkEww== + +"@next/swc-linux-arm-gnueabihf@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.0.3.tgz#f5d43be7314044526fd9f1eef34337bb95f02e01" + integrity sha512-2HNPhBJuN9L6JzqqqdYB4TKfFFmaKkpF0X3C1s83Xp61mR2sx8gOthHQtZqWDs4ZLnKZU0j2flGU1uuqpHPCpg== + +"@next/swc-linux-arm64-gnu@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.0.3.tgz#6f1cda1dadabcc4d4f13bd6f5ce23b9879bc6d73" + integrity sha512-NXTON1XK7zi2i+A+bY1PVLi1g5b8cSwgzbnuVR0vAgOtU+3at7FqAKOWfuFIXY7eBEK65uu0Fu5gADhMj0uanQ== + +"@next/swc-linux-arm64-musl@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.0.3.tgz#1eedc1f1fcafc9862ef7e83205ada96bf320a694" + integrity sha512-8D0q22VavhcIl2ZQErEffgh5q6mChaG84uTluAoFfjwrgYtPDZX0M5StqkTZL6T5gA5RLHboNVoscIKGZWMojQ== + +"@next/swc-linux-x64-gnu@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.0.3.tgz#eca85107b01a7571957ae25104d11042e9835a49" + integrity sha512-4mkimH9nMzbuQfLmZ152NYSHdrII9AeqrkrHszexL1Lup2TLMPuxlXj55eVnyyeKFXRLlnqbCu7aOIND68RbOA== + +"@next/swc-linux-x64-musl@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.0.3.tgz#758656b8e36a520c03763d154c366bec889c56b3" + integrity sha512-MXvx+IDYoSsSM7KcwbQAVo9r+ZeklHeDQiUEmyRRzQE1Q4JvkWwMdPu/NfFdyxur+RfKjRoUoWFdPi5MBKTpkw== + +"@next/swc-win32-arm64-msvc@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.0.3.tgz#3f8ab8fa3367d729e49b3072fb24f9d0f8af7c21" + integrity sha512-8GusumFZLp/mtVix+3JZVTGqzqntTsrTIFZ+GpcLMwyVjB3KkBwHiwJaa38WGleUinJSpJvgmhTWgppsiSKW3A== + +"@next/swc-win32-ia32-msvc@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.0.3.tgz#e3df153a4e0c896a5871f1d26c7e176fa1ceec72" + integrity sha512-mF7bkxSZ++QiB+E0HFqay/etvPF+ZFcCuG27lSwFIM00J+TE0IRqMyMx66vJ8g1h6khpwXPI0o2hrwIip/r8cQ== + +"@next/swc-win32-x64-msvc@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.0.3.tgz#c3e4af29cd74190b89461ccc26b932ae4c27f99d" + integrity sha512-eXFwyf46UFFggMQ3k2tJsOmB3SuKjWaSiZJH0tTDUsLw74lyqyzJqMCVA4yY0gWSlEnSjmX5nrCBknVZd3joaA== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1455,10 +1505,10 @@ lz-string "^1.4.4" pretty-format "^27.0.2" -"@testing-library/jest-dom@^5.0.0": - version "5.14.1" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.14.1.tgz#8501e16f1e55a55d675fe73eecee32cdaddb9766" - integrity sha512-dfB7HVIgTNCxH22M1+KU6viG5of2ldoA5ly8Ar8xkezKHKXjRvznCdbMbqjYGgO2xjRbwnR+rR8MLUIqF3kKbQ== +"@testing-library/jest-dom@^5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.15.0.tgz#4f5295dbc476a14aec3b07176434b3d51aae5da7" + integrity sha512-lOMuQidnL1tWHLEWIhL6UvSZC1Qt3OkNe1khvi2h6xFiqpe5O8arYs46OU0qyUGq0cSTbroQyMktYNXu3a7sAA== dependencies: "@babel/runtime" "^7.9.2" "@types/testing-library__jest-dom" "^5.9.1" @@ -1478,10 +1528,10 @@ "@babel/runtime" "^7.12.5" "@testing-library/dom" "^8.0.0" -"@testing-library/user-event@^13.4.1": - version "13.4.1" - resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.4.1.tgz#8d9e73bcc7be09560b4c0ffbb6842ac43bc80ed2" - integrity sha512-WcnVwi96MmFsHLMNvBz03aPMVDU3UOgucXcn85fNXKKdtd7CHi2NAgE3hASt157yTB9krym0ikFVKbqYghKRCg== +"@testing-library/user-event@^13.5.0": + version "13.5.0" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.5.0.tgz#69d77007f1e124d55314a2b73fd204b333b13295" + integrity sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg== dependencies: "@babel/runtime" "^7.12.5" @@ -1689,10 +1739,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== -"@types/node@16.11.1": - version "16.11.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.1.tgz#2e50a649a50fc403433a14f829eface1a3443e97" - integrity sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA== +"@types/node@16.11.7": + version "16.11.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42" + integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw== "@types/node@^10.12.18": version "10.17.60" @@ -1747,10 +1797,10 @@ dependencies: "@types/react" "*" -"@types/react-dom@17.0.9": - version "17.0.9" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.9.tgz#441a981da9d7be117042e1a6fd3dac4b30f55add" - integrity sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg== +"@types/react-dom@17.0.11": + version "17.0.11" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466" + integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q== dependencies: "@types/react" "*" @@ -1761,20 +1811,10 @@ dependencies: "@types/react" "*" -"@types/react-redux@7.1.19": - version "7.1.19" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.19.tgz#477bd0a9b01bae6d6bf809418cdfa7d3c16d4c62" - integrity sha512-L37dSCT0aoJnCgpR8Iuginlbxoh7qhWOXiaDqEsxVMrER1CmVhFD+63NxgJeT4pkmEM28oX0NH4S4f+sXHTZjA== - dependencies: - "@types/hoist-non-react-statics" "^3.3.0" - "@types/react" "*" - hoist-non-react-statics "^3.3.0" - redux "^4.0.0" - -"@types/react-redux@^7.1.16": - version "7.1.18" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.18.tgz#2bf8fd56ebaae679a90ebffe48ff73717c438e04" - integrity sha512-9iwAsPyJ9DLTRH+OFeIrm9cAbIj1i2ANL3sKQFATqnPWRbg+jEFXyZOKHiQK/N86pNRXbb4HRxAxo0SIX1XwzQ== +"@types/react-redux@7.1.20", "@types/react-redux@^7.1.20": + version "7.1.20" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.20.tgz#42f0e61ababb621e12c66c96dda94c58423bd7df" + integrity sha512-q42es4c8iIeTgcnB+yJgRTTzftv3eYYvCZOh1Ckn2eX/3o5TdsQYKUWpLoLuGlcY/p+VAhV9IOEZJcWk/vfkXw== dependencies: "@types/hoist-non-react-statics" "^3.3.0" "@types/react" "*" @@ -1807,10 +1847,10 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@17.0.30": - version "17.0.30" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.30.tgz#2f8e6f5ab6415c091cc5e571942ee9064b17609e" - integrity sha512-3Dt/A8gd3TCXi2aRe84y7cK1K8G+N9CZRDG8kDGguOKa0kf/ZkSwTmVIDPsm/KbQOVMaDJXwhBtuOXxqwdpWVg== +"@types/react@17.0.34": + version "17.0.34" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.34.tgz#797b66d359b692e3f19991b6b07e4b0c706c0102" + integrity sha512-46FEGrMjc2+8XhHXILr+3+/sTe3OfzSPU9YGKILLrUYbQ1CLQC9Daqo1KzENGXAWwrFwiY0l4ZbF20gRvgpWTg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2172,6 +2212,11 @@ acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== +acorn@8.5.0, acorn@^8.4.1: + version "8.5.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" + integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== + acorn@^7.0.0, acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" @@ -2182,11 +2227,6 @@ acorn@^8.2.4: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== -acorn@^8.4.1: - version "8.5.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" - integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== - aes-js@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" @@ -2413,14 +2453,6 @@ assert@2.0.0: object-is "^1.0.1" util "^0.12.0" -assert@^1.1.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== - dependencies: - object-assign "^4.1.1" - util "0.10.3" - assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" @@ -2431,11 +2463,6 @@ ast-types-flow@^0.0.7: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= -ast-types@0.13.2: - version "0.13.2" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" - integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== - astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -2473,16 +2500,16 @@ autoprefixer@^10.2.6: normalize-range "^0.1.2" postcss-value-parser "^4.1.0" -autoprefixer@^10.3.7: - version "10.3.7" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.3.7.tgz#cef2562058406bd378c94aacda36bb46a97b3186" - integrity sha512-EmGpu0nnQVmMhX8ROoJ7Mx8mKYPlcUHuxkwrRYEYMz85lu7H09v8w6R1P0JPdn/hKU32GjpLBFEOuIlDWCRWvg== +autoprefixer@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.0.tgz#c3577eb32a1079a440ec253e404eaf1eb21388c8" + integrity sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA== dependencies: - browserslist "^4.17.3" - caniuse-lite "^1.0.30001264" + browserslist "^4.17.5" + caniuse-lite "^1.0.30001272" fraction.js "^4.1.1" normalize-range "^0.1.2" - picocolors "^0.2.1" + picocolors "^1.0.0" postcss-value-parser "^4.1.0" available-typed-arrays@^1.0.2: @@ -2510,12 +2537,12 @@ axobject-query@^2.2.0: resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== -babel-jest@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.2.5.tgz#6bbbc1bb4200fe0bfd1b1fbcbe02fc62ebed16aa" - integrity sha512-GC9pWCcitBhSuF7H3zl0mftoKizlswaF0E3qi+rPL417wKkCB0d+Sjjb0OfXvxj7gWiBf497ldgRMii68Xz+2g== +babel-jest@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.3.1.tgz#0636a3404c68e07001e434ac4956d82da8a80022" + integrity sha512-SjIF8hh/ir0peae2D6S6ZKRhUy7q/DnpH7k/V6fT4Bgs/LXXUztOpX4G2tCgq8mLo5HA9mN6NmlFMeYtKmIsTQ== dependencies: - "@jest/transform" "^27.2.5" + "@jest/transform" "^27.3.1" "@jest/types" "^27.2.5" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.0.0" @@ -2814,7 +2841,7 @@ browserify-sign@^4.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -browserify-zlib@0.2.0, browserify-zlib@^0.2.0: +browserify-zlib@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== @@ -2843,15 +2870,15 @@ browserslist@^4.17.1: nanocolors "^0.1.5" node-releases "^1.1.76" -browserslist@^4.17.3: - version "4.17.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.4.tgz#72e2508af2a403aec0a49847ef31bd823c57ead4" - integrity sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ== +browserslist@^4.17.5: + version "4.18.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.18.0.tgz#849944d9bbbbe5ff6f418a8b558e3effca433cae" + integrity sha512-ER2M0g5iAR84fS/zjBDqEgU6iO5fS9JI2EkHr5zxDxYEFk3LjhU9Vpp/INb6RMQphxko7PDV1FH38H/qVP5yCA== dependencies: - caniuse-lite "^1.0.30001265" - electron-to-chromium "^1.3.867" + caniuse-lite "^1.0.30001280" + electron-to-chromium "^1.3.896" escalade "^3.1.1" - node-releases "^2.0.0" + node-releases "^2.0.1" picocolors "^1.0.0" bs-logger@0.x: @@ -2912,15 +2939,6 @@ buffer@5.6.0: base64-js "^1.0.2" ieee754 "^1.1.4" -buffer@^4.3.0: - version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -3009,10 +3027,10 @@ caniuse-lite@^1.0.30001260: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001263.tgz#7ce7a6fb482a137585cbc908aaf38e90c53a16a4" integrity sha512-doiV5dft6yzWO1WwU19kt8Qz8R0/8DgEziz6/9n2FxUasteZNwNNYSmJO3GLBH8lCVE73AB1RPDPAeYbcO5Cvw== -caniuse-lite@^1.0.30001264, caniuse-lite@^1.0.30001265: - version "1.0.30001269" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001269.tgz#3a71bee03df627364418f9fd31adfc7aa1cc2d56" - integrity sha512-UOy8okEVs48MyHYgV+RdW1Oiudl1H6KolybD6ZquD0VcrPSgj25omXO1S7rDydjpqaISCwA8Pyx+jUQKZwWO5w== +caniuse-lite@^1.0.30001272, caniuse-lite@^1.0.30001280: + version "1.0.30001280" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001280.tgz#066a506046ba4be34cde5f74a08db7a396718fb7" + integrity sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA== caseless@~0.12.0: version "0.12.0" @@ -3122,7 +3140,7 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== -ci-info@^3.1.1: +ci-info@^3.1.1, ci-info@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== @@ -3273,17 +3291,12 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -console-browserify@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" - integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== - console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -constants-browserify@1.0.0, constants-browserify@^1.0.0: +constants-browserify@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= @@ -3432,7 +3445,7 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypto-browserify@3.12.0, crypto-browserify@^3.11.0: +crypto-browserify@3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== @@ -3799,11 +3812,6 @@ domain-browser@4.19.0: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.19.0.tgz#1093e17c0a17dbd521182fe90d49ac1370054af1" integrity sha512-fRA+BaAWOR/yr/t7T9E9GJztHPeFjj8U35ajyAjCDtAAnTn1Rc1f6W6VGPJrO1tkQv9zWu+JRof7z6oQtiYVFQ== -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -3839,10 +3847,10 @@ electron-to-chromium@^1.3.846: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.850.tgz#c56c72abfeab051b4b328beb894461c5912d0456" integrity sha512-ZzkDcdzePeF4dhoGZQT77V2CyJOpwfTZEOg4h0x6R/jQhGt/rIRpbRyVreWLtD7B/WsVxo91URm2WxMKR9JQZA== -electron-to-chromium@^1.3.867: - version "1.3.871" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.871.tgz#6e87365fd72037a6c898fb46050ad4be3ac9ef62" - integrity sha512-qcLvDUPf8DSIMWarHT2ptgcqrYg62n3vPA7vhrOF24d8UNzbUBaHu2CySiENR3nEDzYgaN60071t0F6KLYMQ7Q== +electron-to-chromium@^1.3.896: + version "1.3.896" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.896.tgz#4a94efe4870b1687eafd5c378198a49da06e8a1b" + integrity sha512-NcGkBVXePiuUrPLV8IxP43n1EOtdg+dudVjrfVEUd/bOqpQUFZ2diL5PPYzbgEhZFEltdXV3AcyKwGnEQ5lhMA== elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" @@ -3894,7 +3902,7 @@ encoding@0.1.13: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.1.0, end-of-stream@^1.4.1: +end-of-stream@^1.1.0, end-of-stream@^1.4.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -4014,12 +4022,12 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-next@^11.1.2: - version "11.1.2" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-11.1.2.tgz#73c918f2fa6120d5f65080bf3fcf6b154905707e" - integrity sha512-dFutecxX2Z5/QVlLwdtKt+gIfmNMP8Qx6/qZh3LM/DFVdGJEAnUKrr4VwGmACB2kx/PQ5bx3R+QxnEg4fDPiTg== +eslint-config-next@^12.0.3: + version "12.0.3" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.0.3.tgz#a85ad423997f098b41b61c279472e0642e200a9e" + integrity sha512-q+mX6jhk3HrCo39G18MLhiC6f8zJnTA00f30RSuVUWsv45SQUm6r62oXVqrbAgMEybe0yx/GDRvfA6LvSolw6Q== dependencies: - "@next/eslint-plugin-next" "11.1.2" + "@next/eslint-plugin-next" "12.0.3" "@rushstack/eslint-patch" "^1.0.6" "@typescript-eslint/parser" "^4.20.0" eslint-import-resolver-node "^0.3.4" @@ -4261,6 +4269,13 @@ eth-ens-namehash@2.0.8: idna-uts46-hx "^2.3.1" js-sha3 "^0.5.7" +eth-json-rpc-errors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-2.0.2.tgz#c1965de0301fe941c058e928bebaba2e1285e3c4" + integrity sha512-uBCRM2w2ewusRHGxN8JhcuOb2RN3ueAOYH/0BhqdFmQkZx5lj5+fLKTz0mIVOzd4FG5/kUksCzCD7eTEim6gaA== + dependencies: + fast-safe-stringify "^2.0.6" + eth-lib@0.2.8, eth-lib@^0.2.8: version "0.2.8" resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" @@ -4282,6 +4297,13 @@ eth-lib@^0.1.26: ws "^3.0.0" xhr-request-promise "^0.1.2" +eth-rpc-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz#d7b22653c70dbf9defd4ef490fd08fe70608ca10" + integrity sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg== + dependencies: + fast-safe-stringify "^2.0.6" + ethereum-bloom-filters@^1.0.6: version "1.0.10" resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a" @@ -4382,7 +4404,7 @@ eventemitter3@4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== -events@^3.0.0, events@^3.1.0, events@^3.3.0: +events@3.3.0, events@^3.0.0, events@^3.1.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -4425,16 +4447,16 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== -expect@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.2.5.tgz#16154aaa60b4d9a5b0adacfea3e4d6178f4b93fd" - integrity sha512-ZrO0w7bo8BgGoP/bLz+HDCI+0Hfei9jUSZs5yI/Wyn9VkG9w8oJ7rHRgYj+MA7yqqFa0IwHA3flJzZtYugShJA== +expect@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.3.1.tgz#d0f170b1f5c8a2009bab0beffd4bb94f043e38e7" + integrity sha512-MrNXV2sL9iDRebWPGOGFdPQRl2eDQNu/uhxIMShjjx74T6kC6jFIkmQ6OqXDtevjGUkyB2IT56RzDBqXf/QPCg== dependencies: "@jest/types" "^27.2.5" ansi-styles "^5.0.0" - jest-get-type "^27.0.6" - jest-matcher-utils "^27.2.5" - jest-message-util "^27.2.5" + jest-get-type "^27.3.1" + jest-matcher-utils "^27.3.1" + jest-message-util "^27.3.1" jest-regex-util "^27.0.6" express@^4.14.0: @@ -4495,6 +4517,11 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -4526,7 +4553,7 @@ fast-redact@^3.0.0: resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.0.2.tgz#c940ba7162dde3aeeefc522926ae8c5231412904" integrity sha512-YN+CYfCVRVMUZOUPeinHNKgytM1wPI/C/UCLEi56EsY2dwwvI00kIJHJoI7pMVqGoMew8SMZ2SSfHKHULHXDsg== -fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.0.8: +fast-safe-stringify@^2.0.6, fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.0.8: version "2.1.1" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== @@ -5161,7 +5188,7 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -https-browserify@1.0.0, https-browserify@^1.0.0: +https-browserify@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= @@ -5275,16 +5302,11 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= - inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -5529,7 +5551,7 @@ is-typedarray@1.0.0, is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -isarray@^1.0.0, isarray@~1.0.0: +isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -5593,84 +5615,84 @@ isurl@^1.0.0-alpha5: has-to-string-tag-x "^1.2.0" is-object "^1.0.1" -jest-changed-files@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.2.5.tgz#9dfd550d158260bcb6fa80aff491f5647f7daeca" - integrity sha512-jfnNJzF89csUKRPKJ4MwZ1SH27wTmX2xiAIHUHrsb/OYd9Jbo4/SXxJ17/nnx6RIifpthk3Y+LEeOk+/dDeGdw== +jest-changed-files@^27.3.0: + version "27.3.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.3.0.tgz#22a02cc2b34583fc66e443171dc271c0529d263c" + integrity sha512-9DJs9garMHv4RhylUMZgbdCJ3+jHSkpL9aaVKp13xtXAD80qLTLrqcDZL1PHA9dYA0bCI86Nv2BhkLpLhrBcPg== dependencies: "@jest/types" "^27.2.5" execa "^5.0.0" throat "^6.0.1" -jest-circus@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.2.5.tgz#573256a6fb6e447ac2fc7e0ade9375013309037f" - integrity sha512-eyL9IcrAxm3Saq3rmajFCwpaxaRMGJ1KJs+7hlTDinXpJmeR3P02bheM3CYohE7UfwOBmrFMJHjgo/WPcLTM+Q== +jest-circus@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.3.1.tgz#1679e74387cbbf0c6a8b42de963250a6469e0797" + integrity sha512-v1dsM9II6gvXokgqq6Yh2jHCpfg7ZqV4jWY66u7npz24JnhP3NHxI0sKT7+ZMQ7IrOWHYAaeEllOySbDbWsiXw== dependencies: - "@jest/environment" "^27.2.5" - "@jest/test-result" "^27.2.5" + "@jest/environment" "^27.3.1" + "@jest/test-result" "^27.3.1" "@jest/types" "^27.2.5" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" - expect "^27.2.5" + expect "^27.3.1" is-generator-fn "^2.0.0" - jest-each "^27.2.5" - jest-matcher-utils "^27.2.5" - jest-message-util "^27.2.5" - jest-runtime "^27.2.5" - jest-snapshot "^27.2.5" - jest-util "^27.2.5" - pretty-format "^27.2.5" + jest-each "^27.3.1" + jest-matcher-utils "^27.3.1" + jest-message-util "^27.3.1" + jest-runtime "^27.3.1" + jest-snapshot "^27.3.1" + jest-util "^27.3.1" + pretty-format "^27.3.1" slash "^3.0.0" stack-utils "^2.0.3" throat "^6.0.1" -jest-cli@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.2.5.tgz#88718c8f05f1c0f209152952ecd61afe4c3311bb" - integrity sha512-XzfcOXi5WQrXqFYsDxq5RDOKY4FNIgBgvgf3ZBz4e/j5/aWep5KnsAYH5OFPMdX/TP/LFsYQMRH7kzJUMh6JKg== +jest-cli@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.3.1.tgz#b576f9d146ba6643ce0a162d782b40152b6b1d16" + integrity sha512-WHnCqpfK+6EvT62me6WVs8NhtbjAS4/6vZJnk7/2+oOr50cwAzG4Wxt6RXX0hu6m1169ZGMlhYYUNeKBXCph/Q== dependencies: - "@jest/core" "^27.2.5" - "@jest/test-result" "^27.2.5" + "@jest/core" "^27.3.1" + "@jest/test-result" "^27.3.1" "@jest/types" "^27.2.5" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" import-local "^3.0.2" - jest-config "^27.2.5" - jest-util "^27.2.5" - jest-validate "^27.2.5" + jest-config "^27.3.1" + jest-util "^27.3.1" + jest-validate "^27.3.1" prompts "^2.0.1" yargs "^16.2.0" -jest-config@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.2.5.tgz#c2e4ec6ea2bf4ffd2cae3d927999fe6159cba207" - integrity sha512-QdENtn9b5rIIYGlbDNEcgY9LDL5kcokJnXrp7x8AGjHob/XFqw1Z6p+gjfna2sUulQsQ3ce2Fvntnv+7fKYDhQ== +jest-config@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.3.1.tgz#cb3b7f6aaa8c0a7daad4f2b9573899ca7e09bbad" + integrity sha512-KY8xOIbIACZ/vdYCKSopL44I0xboxC751IX+DXL2+Wx6DKNycyEfV3rryC3BPm5Uq/BBqDoMrKuqLEUNJmMKKg== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^27.2.5" + "@jest/test-sequencer" "^27.3.1" "@jest/types" "^27.2.5" - babel-jest "^27.2.5" + babel-jest "^27.3.1" chalk "^4.0.0" + ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.1" graceful-fs "^4.2.4" - is-ci "^3.0.0" - jest-circus "^27.2.5" - jest-environment-jsdom "^27.2.5" - jest-environment-node "^27.2.5" - jest-get-type "^27.0.6" - jest-jasmine2 "^27.2.5" + jest-circus "^27.3.1" + jest-environment-jsdom "^27.3.1" + jest-environment-node "^27.3.1" + jest-get-type "^27.3.1" + jest-jasmine2 "^27.3.1" jest-regex-util "^27.0.6" - jest-resolve "^27.2.5" - jest-runner "^27.2.5" - jest-util "^27.2.5" - jest-validate "^27.2.5" + jest-resolve "^27.3.1" + jest-runner "^27.3.1" + jest-util "^27.3.1" + jest-validate "^27.3.1" micromatch "^4.0.4" - pretty-format "^27.2.5" + pretty-format "^27.3.1" jest-css-modules-transform@^4.3.0: version "4.3.0" @@ -5701,15 +5723,15 @@ jest-diff@^27.0.0: jest-get-type "^27.0.6" pretty-format "^27.2.0" -jest-diff@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.2.5.tgz#908f7a6aca5653824516ad30e0a9fd9767e53623" - integrity sha512-7gfwwyYkeslOOVQY4tVq5TaQa92mWfC9COsVYMNVYyJTOYAqbIkoD3twi5A+h+tAPtAelRxkqY6/xu+jwTr0dA== +jest-diff@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.3.1.tgz#d2775fea15411f5f5aeda2a5e02c2f36440f6d55" + integrity sha512-PCeuAH4AWUo2O5+ksW4pL9v5xJAcIKPUPfIhZBcG1RKv/0+dvaWTQK1Nrau8d67dp65fOqbeMdoil+6PedyEPQ== dependencies: chalk "^4.0.0" diff-sequences "^27.0.6" - jest-get-type "^27.0.6" - pretty-format "^27.2.5" + jest-get-type "^27.3.1" + pretty-format "^27.3.1" jest-docblock@^27.0.6: version "27.0.6" @@ -5718,41 +5740,41 @@ jest-docblock@^27.0.6: dependencies: detect-newline "^3.0.0" -jest-each@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.2.5.tgz#378118d516db730b92096a9607b8711165946353" - integrity sha512-HUPWIbJT0bXarRwKu/m7lYzqxR4GM5EhKOsu0z3t0SKtbFN6skQhpAUADM4qFShBXb9zoOuag5lcrR1x/WM+Ag== +jest-each@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.3.1.tgz#14c56bb4f18dd18dc6bdd853919b5f16a17761ff" + integrity sha512-E4SwfzKJWYcvOYCjOxhZcxwL+AY0uFMvdCOwvzgutJiaiodFjkxQQDxHm8FQBeTqDnSmKsQWn7ldMRzTn2zJaQ== dependencies: "@jest/types" "^27.2.5" chalk "^4.0.0" - jest-get-type "^27.0.6" - jest-util "^27.2.5" - pretty-format "^27.2.5" + jest-get-type "^27.3.1" + jest-util "^27.3.1" + pretty-format "^27.3.1" -jest-environment-jsdom@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.2.5.tgz#21de3ad0e89441d961b592ba7561b16241279208" - integrity sha512-QtRpOh/RQKuXniaWcoFE2ElwP6tQcyxHu0hlk32880g0KczdonCs5P1sk5+weu/OVzh5V4Bt1rXuQthI01mBLg== +jest-environment-jsdom@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.3.1.tgz#63ac36d68f7a9303494df783494856222b57f73e" + integrity sha512-3MOy8qMzIkQlfb3W1TfrD7uZHj+xx8Olix5vMENkj5djPmRqndMaXtpnaZkxmxM+Qc3lo+yVzJjzuXbCcZjAlg== dependencies: - "@jest/environment" "^27.2.5" - "@jest/fake-timers" "^27.2.5" + "@jest/environment" "^27.3.1" + "@jest/fake-timers" "^27.3.1" "@jest/types" "^27.2.5" "@types/node" "*" - jest-mock "^27.2.5" - jest-util "^27.2.5" + jest-mock "^27.3.0" + jest-util "^27.3.1" jsdom "^16.6.0" -jest-environment-node@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.2.5.tgz#ffa1afb3604c640ec841f044d526c65912e02cef" - integrity sha512-0o1LT4grm7iwrS8fIoLtwJxb/hoa3GsH7pP10P02Jpj7Mi4BXy65u46m89vEM2WfD1uFJQ2+dfDiWZNA2e6bJg== +jest-environment-node@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.3.1.tgz#af7d0eed04edafb740311b303f3fe7c8c27014bb" + integrity sha512-T89F/FgkE8waqrTSA7/ydMkcc52uYPgZZ6q8OaZgyiZkJb5QNNCF6oPZjH9IfPFfcc9uBWh1574N0kY0pSvTXw== dependencies: - "@jest/environment" "^27.2.5" - "@jest/fake-timers" "^27.2.5" + "@jest/environment" "^27.3.1" + "@jest/fake-timers" "^27.3.1" "@jest/types" "^27.2.5" "@types/node" "*" - jest-mock "^27.2.5" - jest-util "^27.2.5" + jest-mock "^27.3.0" + jest-util "^27.3.1" jest-get-type@^26.3.0: version "26.3.0" @@ -5764,10 +5786,15 @@ jest-get-type@^27.0.6: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg== -jest-haste-map@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.2.5.tgz#0247b7299250643472bbcf5b4ad85c72d5178e2e" - integrity sha512-pzO+Gw2WLponaSi0ilpzYBE0kuVJstoXBX8YWyUebR8VaXuX4tzzn0Zp23c/WaETo7XYTGv2e8KdnpiskAFMhQ== +jest-get-type@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.3.1.tgz#a8a2b0a12b50169773099eee60a0e6dd11423eff" + integrity sha512-+Ilqi8hgHSAdhlQ3s12CAVNd8H96ZkQBfYoXmArzZnOfAtVAJEiPDBirjByEblvG/4LPJmkL+nBqPO3A1YJAEg== + +jest-haste-map@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.3.1.tgz#7656fbd64bf48bda904e759fc9d93e2c807353ee" + integrity sha512-lYfNZIzwPccDJZIyk9Iz5iQMM/MH56NIIcGj7AFU1YyA4ewWFBl8z+YPJuSCRML/ee2cCt2y3W4K3VXPT6Nhzg== dependencies: "@jest/types" "^27.2.5" "@types/graceful-fs" "^4.1.2" @@ -5777,59 +5804,59 @@ jest-haste-map@^27.2.5: graceful-fs "^4.2.4" jest-regex-util "^27.0.6" jest-serializer "^27.0.6" - jest-util "^27.2.5" - jest-worker "^27.2.5" + jest-util "^27.3.1" + jest-worker "^27.3.1" micromatch "^4.0.4" walker "^1.0.7" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.2.5.tgz#baaf96c69913c52bce0100000cf0721027c0fd66" - integrity sha512-hdxY9Cm/CjLqu2tXeAoQHPgA4vcqlweVXYOg1+S9FeFdznB9Rti+eEBKDDkmOy9iqr4Xfbq95OkC4NFbXXPCAQ== +jest-jasmine2@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.3.1.tgz#df6d3d07c7dafc344feb43a0072a6f09458d32b0" + integrity sha512-WK11ZUetDQaC09w4/j7o4FZDUIp+4iYWH/Lik34Pv7ukL+DuXFGdnmmi7dT58J2ZYKFB5r13GyE0z3NPeyJmsg== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^27.2.5" + "@jest/environment" "^27.3.1" "@jest/source-map" "^27.0.6" - "@jest/test-result" "^27.2.5" + "@jest/test-result" "^27.3.1" "@jest/types" "^27.2.5" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^27.2.5" + expect "^27.3.1" is-generator-fn "^2.0.0" - jest-each "^27.2.5" - jest-matcher-utils "^27.2.5" - jest-message-util "^27.2.5" - jest-runtime "^27.2.5" - jest-snapshot "^27.2.5" - jest-util "^27.2.5" - pretty-format "^27.2.5" + jest-each "^27.3.1" + jest-matcher-utils "^27.3.1" + jest-message-util "^27.3.1" + jest-runtime "^27.3.1" + jest-snapshot "^27.3.1" + jest-util "^27.3.1" + pretty-format "^27.3.1" throat "^6.0.1" -jest-leak-detector@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.2.5.tgz#e2edc3b37d38e8d9a527e10e456b403c3151b206" - integrity sha512-HYsi3GUR72bYhOGB5C5saF9sPdxGzSjX7soSQS+BqDRysc7sPeBwPbhbuT8DnOpijnKjgwWQ8JqvbmReYnt3aQ== +jest-leak-detector@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.3.1.tgz#7fb632c2992ef707a1e73286e1e704f9cc1772b2" + integrity sha512-78QstU9tXbaHzwlRlKmTpjP9k4Pvre5l0r8Spo4SbFFVy/4Abg9I6ZjHwjg2QyKEAMg020XcjP+UgLZIY50yEg== dependencies: - jest-get-type "^27.0.6" - pretty-format "^27.2.5" + jest-get-type "^27.3.1" + pretty-format "^27.3.1" -jest-matcher-utils@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.2.5.tgz#4684faaa8eb32bf15e6edaead6834031897e2980" - integrity sha512-qNR/kh6bz0Dyv3m68Ck2g1fLW5KlSOUNcFQh87VXHZwWc/gY6XwnKofx76Qytz3x5LDWT09/2+yXndTkaG4aWg== +jest-matcher-utils@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.3.1.tgz#257ad61e54a6d4044e080d85dbdc4a08811e9c1c" + integrity sha512-hX8N7zXS4k+8bC1Aj0OWpGb7D3gIXxYvPNK1inP5xvE4ztbz3rc4AkI6jGVaerepBnfWB17FL5lWFJT3s7qo8w== dependencies: chalk "^4.0.0" - jest-diff "^27.2.5" - jest-get-type "^27.0.6" - pretty-format "^27.2.5" + jest-diff "^27.3.1" + jest-get-type "^27.3.1" + pretty-format "^27.3.1" -jest-message-util@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.2.5.tgz#ed8b7b0965247bb875a49c1f9b9ab2d1d0820028" - integrity sha512-ggXSLoPfIYcbmZ8glgEJZ8b+e0Msw/iddRmgkoO7lDAr9SmI65IIfv7VnvTnV4FGnIIUIjzM+fHRHO5RBvyAbQ== +jest-message-util@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.3.1.tgz#f7c25688ad3410ab10bcb862bcfe3152345c6436" + integrity sha512-bh3JEmxsTZ/9rTm0jQrPElbY2+y48Rw2t47uMfByNyUVR+OfPh4anuyKsGqsNkXk/TI4JbLRZx+7p7Hdt6q1yg== dependencies: "@babel/code-frame" "^7.12.13" "@jest/types" "^27.2.5" @@ -5837,14 +5864,14 @@ jest-message-util@^27.2.5: chalk "^4.0.0" graceful-fs "^4.2.4" micromatch "^4.0.4" - pretty-format "^27.2.5" + pretty-format "^27.3.1" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.2.5.tgz#0ec38d5ff1e49c4802e7a4a8179e8d7a2fd84de0" - integrity sha512-HiMB3LqE9RzmeMzZARi2Bz3NoymxyP0gCid4y42ca1djffNtYFKgI220aC1VP1mUZ8rbpqZbHZOJ15093bZV/Q== +jest-mock@^27.3.0: + version "27.3.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.3.0.tgz#ddf0ec3cc3e68c8ccd489bef4d1f525571a1b867" + integrity sha512-ziZiLk0elZOQjD08bLkegBzv5hCABu/c8Ytx45nJKkysQwGaonvmTxwjLqEA4qGdasq9o2I8/HtdGMNnVsMTGw== dependencies: "@jest/types" "^27.2.5" "@types/node" "*" @@ -5859,40 +5886,40 @@ jest-regex-util@^27.0.6: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.6.tgz#02e112082935ae949ce5d13b2675db3d8c87d9c5" integrity sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ== -jest-resolve-dependencies@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.2.5.tgz#fcd8eca005b3d11ba32da443045c028164b83be1" - integrity sha512-BSjefped31bcvvCh++/pN9ueqqN1n0+p8/58yScuWfklLm2tbPbS9d251vJhAy0ZI2pL/0IaGhOTJrs9Y4FJlg== +jest-resolve-dependencies@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.3.1.tgz#85b99bdbdfa46e2c81c6228fc4c91076f624f6e2" + integrity sha512-X7iLzY8pCiYOnvYo2YrK3P9oSE8/3N2f4pUZMJ8IUcZnT81vlSonya1KTO9ZfKGuC+svE6FHK/XOb8SsoRUV1A== dependencies: "@jest/types" "^27.2.5" jest-regex-util "^27.0.6" - jest-snapshot "^27.2.5" + jest-snapshot "^27.3.1" -jest-resolve@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.2.5.tgz#04dadbfc1312a2541f5c199c5011945e9cfe5cef" - integrity sha512-q5irwS3oS73SKy3+FM/HL2T7WJftrk9BRzrXF92f7net5HMlS7lJMg/ZwxLB4YohKqjSsdksEw7n/jvMxV7EKg== +jest-resolve@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.3.1.tgz#0e5542172a1aa0270be6f66a65888647bdd74a3e" + integrity sha512-Dfzt25CFSPo3Y3GCbxynRBZzxq9AdyNN+x/v2IqYx6KVT5Z6me2Z/PsSGFSv3cOSUZqJ9pHxilao/I/m9FouLw== dependencies: "@jest/types" "^27.2.5" chalk "^4.0.0" - escalade "^3.1.1" graceful-fs "^4.2.4" - jest-haste-map "^27.2.5" + jest-haste-map "^27.3.1" jest-pnp-resolver "^1.2.2" - jest-util "^27.2.5" - jest-validate "^27.2.5" + jest-util "^27.3.1" + jest-validate "^27.3.1" resolve "^1.20.0" + resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.2.5.tgz#3d9d0626f351480bb2cffcfbbfac240c0097ebd4" - integrity sha512-n41vw9RLg5TKAnEeJK9d6pGOsBOpwE89XBniK+AD1k26oIIy3V7ogM1scbDjSheji8MUPC9pNgCrZ/FHLVDNgg== +jest-runner@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.3.1.tgz#1d594dcbf3bd8600a7e839e790384559eaf96e3e" + integrity sha512-r4W6kBn6sPr3TBwQNmqE94mPlYVn7fLBseeJfo4E2uCTmAyDFm2O5DYAQAFP7Q3YfiA/bMwg8TVsciP7k0xOww== dependencies: - "@jest/console" "^27.2.5" - "@jest/environment" "^27.2.5" - "@jest/test-result" "^27.2.5" - "@jest/transform" "^27.2.5" + "@jest/console" "^27.3.1" + "@jest/environment" "^27.3.1" + "@jest/test-result" "^27.3.1" + "@jest/transform" "^27.3.1" "@jest/types" "^27.2.5" "@types/node" "*" chalk "^4.0.0" @@ -5900,30 +5927,29 @@ jest-runner@^27.2.5: exit "^0.1.2" graceful-fs "^4.2.4" jest-docblock "^27.0.6" - jest-environment-jsdom "^27.2.5" - jest-environment-node "^27.2.5" - jest-haste-map "^27.2.5" - jest-leak-detector "^27.2.5" - jest-message-util "^27.2.5" - jest-resolve "^27.2.5" - jest-runtime "^27.2.5" - jest-util "^27.2.5" - jest-worker "^27.2.5" + jest-environment-jsdom "^27.3.1" + jest-environment-node "^27.3.1" + jest-haste-map "^27.3.1" + jest-leak-detector "^27.3.1" + jest-message-util "^27.3.1" + jest-resolve "^27.3.1" + jest-runtime "^27.3.1" + jest-util "^27.3.1" + jest-worker "^27.3.1" source-map-support "^0.5.6" throat "^6.0.1" -jest-runtime@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.2.5.tgz#d144c3f6889b927aae1e695b63a41a3323b7016b" - integrity sha512-N0WRZ3QszKyZ3Dm27HTBbBuestsSd3Ud5ooVho47XZJ8aSKO/X1Ag8M1dNx9XzfGVRNdB/xCA3lz8MJwIzPLLA== +jest-runtime@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.3.1.tgz#80fa32eb85fe5af575865ddf379874777ee993d7" + integrity sha512-qtO6VxPbS8umqhEDpjA4pqTkKQ1Hy4ZSi9mDVeE9Za7LKBo2LdW2jmT+Iod3XFaJqINikZQsn2wEi0j9wPRbLg== dependencies: - "@jest/console" "^27.2.5" - "@jest/environment" "^27.2.5" - "@jest/fake-timers" "^27.2.5" - "@jest/globals" "^27.2.5" + "@jest/console" "^27.3.1" + "@jest/environment" "^27.3.1" + "@jest/globals" "^27.3.1" "@jest/source-map" "^27.0.6" - "@jest/test-result" "^27.2.5" - "@jest/transform" "^27.2.5" + "@jest/test-result" "^27.3.1" + "@jest/transform" "^27.3.1" "@jest/types" "^27.2.5" "@types/yargs" "^16.0.0" chalk "^4.0.0" @@ -5933,14 +5959,14 @@ jest-runtime@^27.2.5: exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.2.4" - jest-haste-map "^27.2.5" - jest-message-util "^27.2.5" - jest-mock "^27.2.5" + jest-haste-map "^27.3.1" + jest-message-util "^27.3.1" + jest-mock "^27.3.0" jest-regex-util "^27.0.6" - jest-resolve "^27.2.5" - jest-snapshot "^27.2.5" - jest-util "^27.2.5" - jest-validate "^27.2.5" + jest-resolve "^27.3.1" + jest-snapshot "^27.3.1" + jest-util "^27.3.1" + jest-validate "^27.3.1" slash "^3.0.0" strip-bom "^4.0.0" yargs "^16.2.0" @@ -5953,10 +5979,10 @@ jest-serializer@^27.0.6: "@types/node" "*" graceful-fs "^4.2.4" -jest-snapshot@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.2.5.tgz#8a612fe31e2967f58ad364542198dff61f92ef32" - integrity sha512-2/Jkn+VN6Abwz0llBltZaiJMnL8b1j5Bp/gRIxe9YR3FCEh9qp0TXVV0dcpTGZ8AcJV1SZGQkczewkI9LP5yGw== +jest-snapshot@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.3.1.tgz#1da5c0712a252d70917d46c037054f5918c49ee4" + integrity sha512-APZyBvSgQgOT0XumwfFu7X3G5elj6TGhCBLbBdn3R1IzYustPGPE38F51dBWMQ8hRXa9je0vAdeVDtqHLvB6lg== dependencies: "@babel/core" "^7.7.2" "@babel/generator" "^7.7.2" @@ -5964,23 +5990,23 @@ jest-snapshot@^27.2.5: "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/transform" "^27.2.5" + "@jest/transform" "^27.3.1" "@jest/types" "^27.2.5" "@types/babel__traverse" "^7.0.4" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.2.5" + expect "^27.3.1" graceful-fs "^4.2.4" - jest-diff "^27.2.5" - jest-get-type "^27.0.6" - jest-haste-map "^27.2.5" - jest-matcher-utils "^27.2.5" - jest-message-util "^27.2.5" - jest-resolve "^27.2.5" - jest-util "^27.2.5" + jest-diff "^27.3.1" + jest-get-type "^27.3.1" + jest-haste-map "^27.3.1" + jest-matcher-utils "^27.3.1" + jest-message-util "^27.3.1" + jest-resolve "^27.3.1" + jest-util "^27.3.1" natural-compare "^1.4.0" - pretty-format "^27.2.5" + pretty-format "^27.3.1" semver "^7.3.2" jest-util@^27.0.0: @@ -5995,41 +6021,41 @@ jest-util@^27.0.0: is-ci "^3.0.0" picomatch "^2.2.3" -jest-util@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.2.5.tgz#88740c4024d223634a82ce7c2263e8bc6df3b3ba" - integrity sha512-QRhDC6XxISntMzFRd/OQ6TGsjbzA5ONO0tlAj2ElHs155x1aEr0rkYJBEysG6H/gZVH3oGFzCdAB/GA8leh8NQ== +jest-util@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.3.1.tgz#a58cdc7b6c8a560caac9ed6bdfc4e4ff23f80429" + integrity sha512-8fg+ifEH3GDryLQf/eKZck1DEs2YuVPBCMOaHQxVVLmQwl/CDhWzrvChTX4efLZxGrw+AA0mSXv78cyytBt/uw== dependencies: "@jest/types" "^27.2.5" "@types/node" "*" chalk "^4.0.0" + ci-info "^3.2.0" graceful-fs "^4.2.4" - is-ci "^3.0.0" picomatch "^2.2.3" -jest-validate@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.2.5.tgz#2d59bf1627d180f395ba58f24599b0ee0efcfbdf" - integrity sha512-XgYtjS89nhVe+UfkbLgcm+GgXKWgL80t9nTcNeejyO3t0Sj/yHE8BtIJqjZu9NXQksYbGImoQRXmQ1gP+Guffw== +jest-validate@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.3.1.tgz#3a395d61a19cd13ae9054af8cdaf299116ef8a24" + integrity sha512-3H0XCHDFLA9uDII67Bwi1Vy7AqwA5HqEEjyy934lgVhtJ3eisw6ShOF1MDmRPspyikef5MyExvIm0/TuLzZ86Q== dependencies: "@jest/types" "^27.2.5" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.0.6" + jest-get-type "^27.3.1" leven "^3.1.0" - pretty-format "^27.2.5" + pretty-format "^27.3.1" -jest-watcher@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.2.5.tgz#41cd3e64dc5bea8a4327083d71ba7667be400567" - integrity sha512-umV4qGozg2Dn6DTTtqAh9puPw+DGLK9AQas7+mWjiK8t0fWMpxKg8ZXReZw7L4C88DqorsGUiDgwHNZ+jkVrkQ== +jest-watcher@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.3.1.tgz#ba5e0bc6aa843612b54ddb7f009d1cbff7e05f3e" + integrity sha512-9/xbV6chABsGHWh9yPaAGYVVKurWoP3ZMCv6h+O1v9/+pkOroigs6WzZ0e9gLP/njokUwM7yQhr01LKJVMkaZA== dependencies: - "@jest/test-result" "^27.2.5" + "@jest/test-result" "^27.3.1" "@jest/types" "^27.2.5" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.2.5" + jest-util "^27.3.1" string-length "^4.0.1" jest-worker@27.0.0-next.5: @@ -6041,23 +6067,23 @@ jest-worker@27.0.0-next.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.2.5.tgz#ed42865661959488aa020e8a325df010597c36d4" - integrity sha512-HTjEPZtcNKZ4LnhSp02NEH4vE+5OpJ0EsOWYvGQpHgUMLngydESAAMH5Wd/asPf29+XUDQZszxpLg1BkIIA2aw== +jest-worker@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.3.1.tgz#0def7feae5b8042be38479799aeb7b5facac24b2" + integrity sha512-ks3WCzsiZaOPJl/oMsDjaf0TRiSv7ctNgs0FqRr2nARsovz6AWWy4oLElwcquGSz692DzgZQrCLScPNs5YlC4g== dependencies: "@types/node" "*" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.2.5.tgz#7d8a5c8781a160f693beeb7c68e46c16ef948148" - integrity sha512-vDMzXcpQN4Ycaqu+vO7LX8pZwNNoKMhc+gSp6q1D8S6ftRk8gNW8cni3YFxknP95jxzQo23Lul0BI2FrWgnwYQ== +jest@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.3.1.tgz#b5bab64e8f56b6f7e275ba1836898b0d9f1e5c8a" + integrity sha512-U2AX0AgQGd5EzMsiZpYt8HyZ+nSVIh5ujQ9CPp9EQZJMjXIiSZpJNweZl0swatKRoqHWgGKM3zaSwm4Zaz87ng== dependencies: - "@jest/core" "^27.2.5" + "@jest/core" "^27.3.1" import-local "^3.0.2" - jest-cli "^27.2.5" + jest-cli "^27.3.1" jmespath@^0.15.0: version "0.15.0" @@ -6163,6 +6189,22 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-rpc-engine@^5.1.5: + version "5.4.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz#75758609d849e1dba1e09021ae473f3ab63161e5" + integrity sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g== + dependencies: + eth-rpc-errors "^3.0.0" + safe-event-emitter "^1.0.1" + +json-rpc-middleware-stream@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/json-rpc-middleware-stream/-/json-rpc-middleware-stream-2.1.1.tgz#06e5409e201e7ddeae47bef29f7059eafd4d5325" + integrity sha512-WZheufPN+/RKkjXQP3lK5tFYblqG0n+oYv5qpammwwY2vsJRB7mM4Txhr4ajzvYEZi1UkENnplrmaYiqaqafaA== + dependencies: + readable-stream "^2.3.3" + safe-event-emitter "^1.0.1" + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -6427,6 +6469,11 @@ log-symbols@2.2.0: dependencies: chalk "^2.0.1" +loglevel@^1.6.1: + version "1.8.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" + integrity sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -6813,7 +6860,7 @@ nanoid@^3.1.25: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q== -nanoid@^3.1.28: +nanoid@^3.1.30: version "3.1.30" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ== @@ -6823,13 +6870,6 @@ napi-build-utils@^1.0.1: resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== -native-url@0.3.4: - version "0.3.4" - resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.3.4.tgz#29c943172aed86c63cee62c8c04db7f5756661f8" - integrity sha512-6iM8R99ze45ivyH8vybJ7X0yekIcPf5GgLV5K0ENCbmRcaRIDoj37BC8iLEmaaBfqqb8enuZ5p0uhY+lVAbAcA== - dependencies: - querystring "^0.2.0" - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -6853,20 +6893,20 @@ next-tick@~1.0.0: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= -next@11.1.2: - version "11.1.2" - resolved "https://registry.yarnpkg.com/next/-/next-11.1.2.tgz#527475787a9a362f1bc916962b0c0655cc05bc91" - integrity sha512-azEYL0L+wFjv8lstLru3bgvrzPvK0P7/bz6B/4EJ9sYkXeW8r5Bjh78D/Ol7VOg0EIPz0CXoe72hzAlSAXo9hw== +next@12.0.3: + version "12.0.3" + resolved "https://registry.yarnpkg.com/next/-/next-12.0.3.tgz#325732ceb4193306a9a31912815fc570d1a66641" + integrity sha512-GGdhTBcerdMZbitrO67IVetmB+AHa2X69xrkXKClUT8SRu8pEVto/2QMSnfI+uYc5czCUWPsVtVY3aMoMRMaCA== dependencies: - "@babel/runtime" "7.15.3" + "@babel/runtime" "7.15.4" "@hapi/accept" "5.0.2" - "@next/env" "11.1.2" - "@next/polyfill-module" "11.1.2" - "@next/react-dev-overlay" "11.1.2" - "@next/react-refresh-utils" "11.1.2" - "@node-rs/helper" "1.2.1" + "@napi-rs/triples" "1.0.3" + "@next/env" "12.0.3" + "@next/polyfill-module" "12.0.3" + "@next/react-dev-overlay" "12.0.3" + "@next/react-refresh-utils" "12.0.3" + acorn "8.5.0" assert "2.0.0" - ast-types "0.13.2" browserify-zlib "0.2.0" browserslist "4.16.6" buffer "5.6.0" @@ -6879,29 +6919,28 @@ next@11.1.2: domain-browser "4.19.0" encoding "0.1.13" etag "1.8.1" + events "3.3.0" find-cache-dir "3.3.1" get-orientation "1.1.2" https-browserify "1.0.0" image-size "1.0.0" jest-worker "27.0.0-next.5" - native-url "0.3.4" node-fetch "2.6.1" node-html-parser "1.4.9" - node-libs-browser "^2.2.1" os-browserify "0.3.0" p-limit "3.1.0" path-browserify "1.0.1" - pnp-webpack-plugin "1.6.4" postcss "8.2.15" process "0.11.10" querystring-es3 "0.2.1" raw-body "2.4.1" react-is "17.0.2" react-refresh "0.8.3" + regenerator-runtime "0.13.4" stream-browserify "3.0.0" stream-http "3.1.1" string_decoder "1.3.0" - styled-jsx "4.0.1" + styled-jsx "5.0.0-beta.3" timers-browserify "2.0.12" tty-browserify "0.0.1" use-subscription "1.5.1" @@ -6909,10 +6948,17 @@ next@11.1.2: vm-browserify "1.1.2" watchpack "2.1.1" optionalDependencies: - "@next/swc-darwin-arm64" "11.1.2" - "@next/swc-darwin-x64" "11.1.2" - "@next/swc-linux-x64-gnu" "11.1.2" - "@next/swc-win32-x64-msvc" "11.1.2" + "@next/swc-android-arm64" "12.0.3" + "@next/swc-darwin-arm64" "12.0.3" + "@next/swc-darwin-x64" "12.0.3" + "@next/swc-linux-arm-gnueabihf" "12.0.3" + "@next/swc-linux-arm64-gnu" "12.0.3" + "@next/swc-linux-arm64-musl" "12.0.3" + "@next/swc-linux-x64-gnu" "12.0.3" + "@next/swc-linux-x64-musl" "12.0.3" + "@next/swc-win32-arm64-msvc" "12.0.3" + "@next/swc-win32-ia32-msvc" "12.0.3" + "@next/swc-win32-x64-msvc" "12.0.3" node-abi@^2.21.0: version "2.30.1" @@ -6963,35 +7009,6 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-libs-browser@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - node-modules-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" @@ -7007,10 +7024,10 @@ node-releases@^1.1.76: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.76.tgz#df245b062b0cafbd5282ab6792f7dccc2d97f36e" integrity sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA== -node-releases@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.0.tgz#67dc74903100a7deb044037b8a2e5f453bb05400" - integrity sha512-aA87l0flFYMzCHpTM3DERFSYxc6lv/BltdbRTOMZuxZ0cwZCD3mejE5n9vLhSJCN++/eOqr77G1IO5uXxlQYWA== +node-releases@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" + integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== normalize-package-data@^2.3.2: version "2.5.0" @@ -7082,6 +7099,15 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +obj-multiplex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/obj-multiplex/-/obj-multiplex-1.0.0.tgz#2f2ae6bfd4ae11befe742ea9ea5b36636eabffc1" + integrity sha1-Lyrmv9SuEb7+dC6p6ls2Y26r/8E= + dependencies: + end-of-stream "^1.4.0" + once "^1.4.0" + readable-stream "^2.3.3" + object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -7174,6 +7200,16 @@ oboe@2.1.5: dependencies: http-https "^1.0.0" +obs-store@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/obs-store/-/obs-store-4.0.3.tgz#b632ec7814baa604fae084a4c97e87c0b7a6d14c" + integrity sha512-+mm13kCRDv6IcvUDKTw0LIy5+dQhIktYaR/RwwZUFzOTi/fjMaNBnk42Adb94qZqJ00qWkjhQSZH7MXlKnTi8A== + dependencies: + readable-stream "^2.2.2" + safe-event-emitter "^1.0.1" + through2 "^2.0.3" + xtend "^4.0.1" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -7219,7 +7255,7 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -os-browserify@0.3.0, os-browserify@^0.3.0: +os-browserify@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= @@ -7354,11 +7390,6 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - path-browserify@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" @@ -7427,11 +7458,6 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picocolors@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" - integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -7516,12 +7542,12 @@ platform@1.3.6: resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== -pnp-webpack-plugin@1.6.4: - version "1.6.4" - resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" - integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== +post-message-stream@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/post-message-stream/-/post-message-stream-3.0.0.tgz#90d9f54bd209e6b6f5d74795b87588205b547048" + integrity sha1-kNn1S9IJ5rb110eVuHWIIFtUcEg= dependencies: - ts-pnp "^1.1.6" + readable-stream "^2.1.4" postcss-js@^3.0.3: version "3.0.3" @@ -7590,6 +7616,15 @@ postcss@8.2.15: nanoid "^3.1.23" source-map-js "^0.6.2" +postcss@^8.3.11: + version "8.3.11" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.11.tgz#c3beca7ea811cd5e1c4a3ec6d2e7599ef1f8f858" + integrity sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA== + dependencies: + nanoid "^3.1.30" + picocolors "^1.0.0" + source-map-js "^0.6.2" + postcss@^8.3.5: version "8.3.8" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.8.tgz#9ebe2a127396b4b4570ae9f7770e7fb83db2bac1" @@ -7599,15 +7634,6 @@ postcss@^8.3.5: nanoid "^3.1.25" source-map-js "^0.6.2" -postcss@^8.3.9: - version "8.3.9" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.9.tgz#98754caa06c4ee9eb59cc48bd073bb6bd3437c31" - integrity sha512-f/ZFyAKh9Dnqytx5X62jgjhhzttjZS7hMsohcI7HEI5tjELX/HxCy3EFhsRxyzGvrzFF+82XPvCS8T9TFleVJw== - dependencies: - nanoid "^3.1.28" - picocolors "^0.2.1" - source-map-js "^0.6.2" - prebuild-install@^6.0.1: version "6.1.4" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.1.4.tgz#ae3c0142ad611d58570b89af4986088a4937e00f" @@ -7682,10 +7708,10 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^27.2.5: - version "27.2.5" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.2.5.tgz#7cfe2a8e8f01a5b5b29296a0b70f4140df0830c5" - integrity sha512-+nYn2z9GgicO9JiqmY25Xtq8SYfZ/5VCpEU3pppHHNAhd1y+ZXxmNPd1evmNcAd6Hz4iBV2kf0UpGth5A/VJ7g== +pretty-format@^27.3.1: + version "27.3.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.3.1.tgz#7e9486365ccdd4a502061fa761d3ab9ca1b78df5" + integrity sha512-DR/c+pvFc52nLimLROYjnXPtolawm+uWDxr4FjuLDLUn+ktWnSN851KoHwHzzqq6rfCOjkzN8FLgDrSub6UDuA== dependencies: "@jest/types" "^27.2.5" ansi-regex "^5.0.1" @@ -7762,21 +7788,11 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - punycode@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= -punycode@^1.2.4: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -7835,21 +7851,11 @@ query-string@^6.13.5: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" -querystring-es3@0.2.1, querystring-es3@^0.2.0: +querystring-es3@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -querystring@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" - integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -7955,12 +7961,12 @@ react-input-autosize@^3.0.0: dependencies: prop-types "^15.5.8" -react-is@17.0.2, react-is@^17.0.1: +react-is@17.0.2, react-is@^17.0.1, react-is@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -7987,17 +7993,17 @@ react-modal@^3.14.3: react-lifecycles-compat "^3.0.0" warning "^4.0.3" -react-redux@^7.2.5: - version "7.2.5" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.5.tgz#213c1b05aa1187d9c940ddfc0b29450957f6a3b8" - integrity sha512-Dt29bNyBsbQaysp6s/dN0gUodcq+dVKKER8Qv82UrpeygwYeX1raTtil7O/fftw/rFqzaf6gJhDZRkkZnn6bjg== +react-redux@^7.2.6: + version "7.2.6" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa" + integrity sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ== dependencies: - "@babel/runtime" "^7.12.1" - "@types/react-redux" "^7.1.16" + "@babel/runtime" "^7.15.4" + "@types/react-redux" "^7.1.20" hoist-non-react-statics "^3.3.2" loose-envify "^1.4.0" prop-types "^15.7.2" - react-is "^16.13.1" + react-is "^17.0.2" react-refresh@0.8.3: version "0.8.3" @@ -8017,10 +8023,10 @@ react-select@4.3.1: react-input-autosize "^3.0.0" react-transition-group "^4.3.0" -react-toastify@^8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-8.0.3.tgz#7fbf65f69ec357aab8dd03c1496f9177aa92409a" - integrity sha512-rv3koC7f9lKKSkdpYgo/TGzgWlrB/aaiUInF1DyV7BpiM4kyTs+uhu6/r8XDMtBY2FOIHK+FlK3Iv7OzpA/tCA== +react-toastify@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-8.1.0.tgz#acaca4e8c4415c8474562dd84a14e6f390ed7f17" + integrity sha512-M+Q3rTmEw/53Csr7NsV/YnldJe4c7uERcY7Tma9mvLU98QT2VhIkKwjBzzxZkJRk/oBKyUAtkyMjMgO00hx6gQ== dependencies: clsx "^1.1.1" @@ -8059,7 +8065,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.3.3, readable-stream@^2.3.6: +readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -8123,6 +8129,11 @@ redux@^4.0.0, redux@^4.1.0: dependencies: "@babel/runtime" "^7.9.2" +regenerator-runtime@0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz#e96bf612a3362d12bb69f7e8f74ffeab25c7ac91" + integrity sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g== + regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" @@ -8204,6 +8215,11 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve.exports@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" + integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== + resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" @@ -8293,6 +8309,13 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-event-emitter@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" + integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== + dependencies: + events "^3.0.0" + safe-json-utils@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/safe-json-utils/-/safe-json-utils-1.1.1.tgz#0e883874467d95ab914c3f511096b89bfb3e63b1" @@ -8424,10 +8447,10 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" - integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== +shell-quote@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" + integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== side-channel@^1.0.4: version "1.0.4" @@ -8641,14 +8664,6 @@ stream-browserify@3.0.0: inherits "~2.0.4" readable-stream "^3.5.0" -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - stream-http@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.1.1.tgz#0370a8017cf8d050b9a8554afe608f043eaff564" @@ -8659,17 +8674,6 @@ stream-http@3.1.1: readable-stream "^3.6.0" xtend "^4.0.2" -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - stream-parser@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/stream-parser/-/stream-parser-0.3.1.tgz#1618548694420021a1182ff0af1911c129761773" @@ -8774,7 +8778,7 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" -string_decoder@1.3.0, string_decoder@^1.0.0, string_decoder@^1.1.1: +string_decoder@1.3.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -8788,12 +8792,12 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@6.0.0, strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== +strip-ansi@6.0.1, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - ansi-regex "^5.0.0" + ansi-regex "^5.0.1" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" @@ -8816,12 +8820,12 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== dependencies: - ansi-regex "^5.0.1" + ansi-regex "^5.0.0" strip-bom@^3.0.0: version "3.0.0" @@ -8862,10 +8866,10 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -styled-jsx@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-4.0.1.tgz#ae3f716eacc0792f7050389de88add6d5245b9e9" - integrity sha512-Gcb49/dRB1k8B4hdK8vhW27Rlb2zujCk1fISrizCcToIs+55B4vmUM0N9Gi4nnVfFZWe55jRdWpAqH1ldAKWvQ== +styled-jsx@5.0.0-beta.3: + version "5.0.0-beta.3" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0-beta.3.tgz#400d16179b5dff10d5954ab8be27a9a1b7780dd2" + integrity sha512-HtDDGSFPvmjHIqWf9n8Oo54tAoY/DTplvlyOH2+YOtD80Sp31Ap8ffSmxhgk5EkUoJ7xepdXMGT650mSffWuRA== dependencies: "@babel/plugin-syntax-jsx" "7.14.5" "@babel/types" "7.15.0" @@ -8961,10 +8965,10 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" -tailwindcss@^2.2.17: - version "2.2.17" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.17.tgz#c6332731f9ff1b6628ff589c95c38685347775e3" - integrity sha512-WgRpn+Pxn7eWqlruxnxEbL9ByVRWi3iC10z4b6dW0zSdnkPVC4hPMSWLQkkW8GCyBIv/vbJ0bxIi9dVrl4CfoA== +tailwindcss@^2.2.19: + version "2.2.19" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.19.tgz#540e464832cd462bb9649c1484b0a38315c2653c" + integrity sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw== dependencies: arg "^5.0.1" bytes "^3.0.0" @@ -9072,12 +9076,20 @@ throat@^6.0.1: resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== +through2@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + timed-out@^4.0.0, timed-out@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= -timers-browserify@2.0.12, timers-browserify@^2.0.4: +timers-browserify@2.0.12: version "2.0.12" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== @@ -9112,11 +9124,6 @@ tmpl@1.0.x: resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -9184,10 +9191,10 @@ ts-jest@^27.0.7: semver "7.x" yargs-parser "20.x" -ts-node@^10.3.0: - version "10.3.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.3.0.tgz#a797f2ed3ff50c9a5d814ce400437cb0c1c048b4" - integrity sha512-RYIy3i8IgpFH45AX4fQHExrT8BxDeKTdC83QFJkNzkvt8uFB6QJ8XMyhynYiKMLxt9a7yuXaDBZNOYS3XjDcYw== +ts-node@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" + integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== dependencies: "@cspotcode/source-map-support" "0.7.0" "@tsconfig/node10" "^1.0.7" @@ -9213,11 +9220,6 @@ ts-node@^8.4.1: source-map-support "^0.5.17" yn "3.1.1" -ts-pnp@^1.1.6: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" - integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== - tsconfig-paths@^3.9.0: version "3.10.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz#79ae67a68c15289fdf5c51cb74f397522d795ed7" @@ -9239,11 +9241,6 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= - tty-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" @@ -9416,14 +9413,6 @@ url-to-options@^1.0.1: resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= -url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - use-subscription@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" @@ -9448,13 +9437,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= - dependencies: - inherits "2.0.1" - util@0.12.4, util@^0.12.0: version "0.12.4" resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" @@ -9467,13 +9449,6 @@ util@0.12.4, util@^0.12.0: safe-buffer "^5.1.2" which-typed-array "^1.1.2" -util@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== - dependencies: - inherits "2.0.3" - utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -9530,7 +9505,7 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vm-browserify@1.1.2, vm-browserify@^1.0.1: +vm-browserify@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== @@ -10034,7 +10009,7 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.0, xtend@^4.0.2: +xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==