Integrate Beehiiv email verification in onboarding flow #13

Merged
nabarun merged 5 commits from deep-stack/testnet-onboarding-app:sk-beehiiv-integration into main 2024-08-08 10:06:35 +00:00
10 changed files with 118 additions and 197 deletions
Showing only changes of commit 04b2eeb77a - Show all commits

View File

@ -12,7 +12,6 @@
"@mui/material": "^5.15.14", "@mui/material": "^5.15.14",
"@sumsub/websdk": "^2.3.1", "@sumsub/websdk": "^2.3.1",
"@sumsub/websdk-react": "^2.3.1", "@sumsub/websdk-react": "^2.3.1",
"@types/jsonwebtoken": "^9.0.6",
"@walletconnect/encoding": "^1.0.2", "@walletconnect/encoding": "^1.0.2",
"@walletconnect/modal": "^2.6.2", "@walletconnect/modal": "^2.6.2",
"@walletconnect/sign-client": "^2.11.3", "@walletconnect/sign-client": "^2.11.3",
@ -24,7 +23,7 @@
"crypto-browserify": "^3.12.0", "crypto-browserify": "^3.12.0",
"ethers": "5.7.2", "ethers": "5.7.2",
"https-browserify": "^1.0.0", "https-browserify": "^1.0.0",
"jsonwebtoken": "^9.0.2", "jwt-decode": "^4.0.0",
"notistack": "^3.0.1", "notistack": "^3.0.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

View File

@ -12,7 +12,8 @@ import TermsAndConditions from "./pages/TermsAndConditions";
import Header from "./components/Header"; import Header from "./components/Header";
import { WalletConnectProvider } from "./context/WalletConnectContext"; import { WalletConnectProvider } from "./context/WalletConnectContext";
import VerifyEmail from "./pages/VerifyEmail"; import VerifyEmail from "./pages/VerifyEmail";
import RedirectEmail from "./pages/RedirectEmail"; import Email from "./pages/Email";
import Thanks from "./pages/Thanks";
function App() { function App() {
return ( return (
@ -22,8 +23,9 @@ function App() {
<Routes> <Routes>
<Route path="/" element={<TermsAndConditions />} /> <Route path="/" element={<TermsAndConditions />} />
<Route path="/verify-email" element={<VerifyEmail />} /> <Route path="/verify-email" element={<VerifyEmail />} />
<Route path="/email" element={<RedirectEmail/>} /> <Route path="/email" element={<Email/>} />
<Route path="/connect-wallet" element={<ConnectWallet />} /> <Route path="/connect-wallet" element={<ConnectWallet />} />
<Route path="/thanks" element={<Thanks />} />
<Route element={<SignPageLayout />}> <Route element={<SignPageLayout />}>
<Route path="/sign-with-nitro-key" element={<SignWithNitroKey />} /> <Route path="/sign-with-nitro-key" element={<SignWithNitroKey />} />
<Route <Route

View File

@ -12,23 +12,14 @@ const ConnectWallet = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const {kycIdHash} = location.state as {
kycIdHash?: string
}
useEffect(() => { useEffect(() => {
if (!kycIdHash) {
return;
}
if (session) { if (session) {
navigate("/sign-with-nitro-key", { navigate("/sign-with-nitro-key", {
state: { state: location.state
kycIdHash
}
}); });
} }
}, [session, navigate, kycIdHash]); }, [session, navigate, location]);
const handler = async () => { const handler = async () => {
await connect(); await connect();
@ -36,41 +27,7 @@ const ConnectWallet = () => {
return ( return (
<Container maxWidth="lg"> <Container maxWidth="lg">
{kycIdHash ? <> <Box
<Box
display="flex"
flexDirection="column"
alignItems="center"
justifyContent="center"
marginTop={10}
sx={{
border: 1,
borderColor: 'grey.500',
}}
padding={5}
>
<Typography variant="h5" component="h1" gutterBottom color={colors.red[400]}>
Disclaimer
</Typography>
<Typography variant="body1">
{WALLET_DISCLAIMER_MSG}
</Typography>
</Box>
<Box
display="flex"
flexDirection="column"
alignItems="center"
padding={5}
justifyContent="center"
>
<Button
variant="contained"
onClick={handler}
>
Connect Wallet
</Button>
</Box>
</> : <Box
display="flex" display="flex"
flexDirection="column" flexDirection="column"
alignItems="center" alignItems="center"
@ -83,13 +40,26 @@ const ConnectWallet = () => {
padding={5} padding={5}
> >
<Typography variant="h5" component="h1" gutterBottom color={colors.red[400]}> <Typography variant="h5" component="h1" gutterBottom color={colors.red[400]}>
Error Disclaimer
</Typography> </Typography>
<Typography variant="body1"> <Typography variant="body1">
KYC ID not found. Please verify your email and try again {WALLET_DISCLAIMER_MSG}
</Typography> </Typography>
</Box> </Box>
} <Box
display="flex"
flexDirection="column"
alignItems="center"
padding={5}
justifyContent="center"
>
<Button
variant="contained"
onClick={handler}
>
Connect Wallet
</Button>
</Box>
</Container> </Container>
); );
}; };

View File

@ -1,8 +1,10 @@
import { Box, Typography } from '@mui/material'
import React from 'react' import React from 'react'
import { Box, Typography } from '@mui/material'
import { REDIRECT_EMAIL_MSG } from '../constants' import { REDIRECT_EMAIL_MSG } from '../constants'
const RedirectEmail = () => { const Email = () => {
return ( return (
<Box <Box
display="flex" display="flex"
@ -28,4 +30,4 @@ const RedirectEmail = () => {
) )
} }
export default RedirectEmail export default Email

View File

@ -25,14 +25,14 @@ const SignWithCosmos = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const {message: innerMessage, cosmosAddress, receivedEthSig: ethSignature, kycIdHash} = location.state as { const {message: innerMessage, cosmosAddress, receivedEthSig: ethSignature, subscriberIdHash} = location.state as {
message?: { message?: {
msg: string; msg: string;
address: string; address: string;
}; };
cosmosAddress?: string; cosmosAddress?: string;
receivedEthSig?: string; receivedEthSig?: string;
kycIdHash?: string; subscriberIdHash?: string;
}; };
const ethAddress = innerMessage!.address; const ethAddress = innerMessage!.address;
@ -49,11 +49,11 @@ const SignWithCosmos = () => {
participant: cosmosAddress!, participant: cosmosAddress!,
ethPayload: innerMessage, ethPayload: innerMessage,
ethSignature: ethSignature!, ethSignature: ethSignature!,
kycId: kycIdHash!, kycId: subscriberIdHash!,
role role
}, },
}; };
}, [cosmosAddress, innerMessage, ethSignature, kycIdHash, role]); }, [cosmosAddress, innerMessage, ethSignature, subscriberIdHash, role]);
const handleTokenRequest = async () => { const handleTokenRequest = async () => {
try { try {

View File

@ -68,8 +68,12 @@ const SignWithNitroKey = () => {
}, },
}); });
} else { } else {
const {kycIdHash} = location.state as { const state = location.state as {
kycIdHash?: string subscriberIdHash?: string
}
if (!state.subscriberIdHash) {
throw new Error("Subscriber ID not found. Please verify your email and try again")
} }
navigate("/sign-with-cosmos", { navigate("/sign-with-cosmos", {
@ -77,7 +81,7 @@ const SignWithNitroKey = () => {
message, message,
cosmosAddress, cosmosAddress,
receivedEthSig, receivedEthSig,
kycIdHash, subscriberIdHash: state.subscriberIdHash,
}, },
}); });
} }

73
src/pages/Thanks.tsx Normal file
View File

@ -0,0 +1,73 @@
import React, { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { jwtDecode } from "jwt-decode";
import { ethers } from 'ethers';
import { Box, colors, Typography } from '@mui/material';
interface JwtPayload {
subscriber_id: string;
exp: number;
iss: string;
iat: number;
}
const Thanks: React.FC = () => {
const location = useLocation();
const navigate = useNavigate();
const [err, setErr] = useState<string>();
useEffect(() => {
const queryParams = new URLSearchParams(location.search);
const token = queryParams.get('jwt_token');
try {
if(!token){
throw new Error("Invalid JWT Token")
}
const decoded = jwtDecode(token) as JwtPayload;
const currentTime = Math.floor(Date.now() / 1000);
if (!decoded.subscriber_id) {
throw new Error("Subscriber ID not found")
}
if (decoded.exp < currentTime) {
throw new Error("Token has expired");
}
const uuidBuffer = Buffer.from(decoded.subscriber_id.replace(/-/g, ''), 'hex');
const subscriberIdHash = ethers.utils.sha256(uuidBuffer);
navigate('/connect-wallet', {
state:{
subscriberIdHash
}
});
} catch (error) {
setErr(String(error));
}
}, [location.search, navigate]);
return (
<Box
display="flex"
flexDirection="column"
alignItems="center"
justifyContent="center"
marginY={20}
marginX={50}
padding={5}
>
<Typography variant="h4" component="h1" gutterBottom color={colors.red[400]}>
{err ? err : "Loading..."}
</Typography>
</Box>
);
};
export default Thanks;

View File

@ -50,7 +50,7 @@ const UserVerification = () => {
message, message,
cosmosAddress, cosmosAddress,
receivedEthSig, receivedEthSig,
kycIdHash, subscriberIdHash: kycIdHash,
}}) }})
} }
}, [applicationSubmitted, kycId, navigate, cosmosAddress, message, receivedEthSig]); }, [applicationSubmitted, kycId, navigate, cosmosAddress, message, receivedEthSig]);

View File

@ -1,47 +0,0 @@
import React, { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import jwt from 'jsonwebtoken';
import { sha256 } from 'ethers/lib/utils';
interface JwtPayload {
subscriber_id: string;
exp: number;
iss: string;
iat: number;
}
const VerifyJWTToken: React.FC = () => {
const location = useLocation();
const navigate = useNavigate();
const [err, setErr] = useState<string>();
useEffect(() => {
const queryParams = new URLSearchParams(location.search);
const token = queryParams.get('jwt_token');
if (token) {
try {
const decoded = jwt.decode(token) as JwtPayload;
const currentTime = Math.floor(Date.now() / 1000);
if (decoded.exp < currentTime) {
throw new Error("Token has expired");
}
const kycIdHash = sha256(decoded.subscriber_id);
navigate('/connect-wallet', {
state:{
kycIdHash
}
});
} catch (error) {
setErr(String(error));
}
}
}, [location.search, navigate]);
return (<div>{err ? err : "Loading..."}</div>);
};
export default VerifyJWTToken;

View File

@ -3453,13 +3453,6 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/jsonwebtoken@^9.0.6":
version "9.0.6"
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz#d1af3544d99ad992fb6681bbe60676e06b032bd3"
integrity sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==
dependencies:
"@types/node" "*"
"@types/long@^4.0.1": "@types/long@^4.0.1":
version "4.0.2" version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
@ -5047,11 +5040,6 @@ bser@2.1.1:
dependencies: dependencies:
node-int64 "^0.4.0" node-int64 "^0.4.0"
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
buffer-from@^1.0.0: buffer-from@^1.0.0:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
@ -6220,13 +6208,6 @@ eastasianwidth@^0.2.0:
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1: ee-first@1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@ -9036,22 +9017,6 @@ jsonpointer@^5.0.0:
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559"
integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==
jsonwebtoken@^9.0.2:
version "9.0.2"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3"
integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==
dependencies:
jws "^3.2.2"
lodash.includes "^4.3.0"
lodash.isboolean "^3.0.3"
lodash.isinteger "^4.0.4"
lodash.isnumber "^3.0.3"
lodash.isplainobject "^4.0.6"
lodash.isstring "^4.0.1"
lodash.once "^4.0.0"
ms "^2.1.1"
semver "^7.5.4"
"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5:
version "3.3.5" version "3.3.5"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a"
@ -9067,22 +9032,10 @@ junk@3.1.0:
resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1"
integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==
jwa@^1.4.1: jwt-decode@^4.0.0:
version "1.4.1" version "4.0.0"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-4.0.0.tgz#2270352425fd413785b2faf11f6e755c5151bd4b"
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
jws@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
dependencies:
jwa "^1.4.1"
safe-buffer "^5.0.1"
keccak@^3.0.0: keccak@^3.0.0:
version "3.0.4" version "3.0.4"
@ -9302,41 +9255,11 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==
lodash.isboolean@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==
lodash.isequal@4.5.0: lodash.isequal@4.5.0:
version "4.5.0" version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
lodash.isinteger@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==
lodash.isnumber@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==
lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
lodash.isstring@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==
lodash.memoize@^4.1.2: lodash.memoize@^4.1.2:
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@ -9347,11 +9270,6 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==
lodash.sortby@^4.7.0: lodash.sortby@^4.7.0:
version "4.7.0" version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"