Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
663eb42a74 | ||
|
|
8ba837b2f4 | ||
|
|
fc1c8df06b | ||
|
|
aa9aed89f2 |
+2
-1
@@ -3,7 +3,8 @@ REACT_APP_ETHEREUM_MAINNET_CHAIN_ID=1
|
||||
REACT_APP_LACONICD_CHAIN_ID=laconic_9000-1
|
||||
REACT_APP_REGISTRY_GQL_ENDPOINT=http://localhost:9473/api
|
||||
REACT_APP_LACONICD_RPC_ENDPOINT=http://localhost:26657
|
||||
REACT_APP_LACONICD_DENOM=alnt
|
||||
REACT_APP_FAUCET_ENDPOINT=http://localhost:4000
|
||||
REACT_APP_WALLET_META_URL=http://localhost:3000
|
||||
REACT_APP_SUMSUB_API_ENDPOINT=
|
||||
REACT_APP_STAKING_AMOUNT=1000000000000000
|
||||
REACT_APP_LACONICD_DENOM=alnt
|
||||
|
||||
+2
-1
@@ -12,6 +12,7 @@
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"indent": ["error", 2, { "SwitchCase": 1 }]
|
||||
"indent": ["error", 2, { "SwitchCase": 1 }],
|
||||
"semi": ["error", "always"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@cerc-io/registry-sdk": "^0.2.5",
|
||||
"@cosmjs/encoding": "^0.32.4",
|
||||
"@cosmjs/proto-signing": "^0.32.4",
|
||||
"@cosmjs/stargate": "^0.32.4",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
|
||||
+13
-3
@@ -8,12 +8,14 @@ import PageNotFound from "./pages/PageNotFound";
|
||||
import OnboardingSuccess from "./pages/OnboardingSuccess";
|
||||
import SignPageLayout from "./layout/SignPageLayout";
|
||||
import UserVerification from "./pages/UserVerification";
|
||||
import TermsAndConditions from "./pages/TermsAndConditions";
|
||||
import LandingPage from "./pages/LandingPage";
|
||||
import Header from "./components/Header";
|
||||
import { WalletConnectProvider } from "./context/WalletConnectContext";
|
||||
import VerifyEmail from "./pages/VerifyEmail";
|
||||
import Email from "./pages/Email";
|
||||
import Thanks from "./pages/Thanks";
|
||||
import Validator from "./pages/Validator";
|
||||
import ValidatorSuccess from "./pages/ValidatorSuccess";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@@ -21,7 +23,7 @@ function App() {
|
||||
<Header />
|
||||
<WalletConnectProvider>
|
||||
<Routes>
|
||||
<Route path="/" element={<TermsAndConditions />} />
|
||||
<Route path="/" element={<LandingPage />} />
|
||||
<Route path="/verify-email" element={<VerifyEmail />} />
|
||||
<Route path="/email" element={<Email/>} />
|
||||
<Route path="/connect-wallet" element={<ConnectWallet />} />
|
||||
@@ -39,7 +41,15 @@ function App() {
|
||||
<Route
|
||||
path="/onboarding-success"
|
||||
element={<OnboardingSuccess />}
|
||||
></Route>
|
||||
/>
|
||||
<Route
|
||||
path="/validator"
|
||||
element={<Validator />}
|
||||
/>
|
||||
<Route
|
||||
path="/validator-success"
|
||||
element={<ValidatorSuccess />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="*" element={<PageNotFound />} />
|
||||
</Routes>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Link, useLocation } from 'react-router-dom';
|
||||
import { AppBar, Toolbar, Avatar, Box, IconButton } from '@mui/material';
|
||||
|
||||
const Header: React.FC = () => {
|
||||
const location = useLocation()
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<AppBar position="static" color="inherit">
|
||||
|
||||
@@ -14,16 +14,16 @@ const SelectRoleCard = ({ handleAccept, handleRoleChange }: { handleAccept: () =
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [isHidden, setIsHidden] = useState(false);
|
||||
|
||||
const [isDialogOpen, setisDialogOpen] = useState(false)
|
||||
const [isDialogOpen, setisDialogOpen] = useState(false);
|
||||
|
||||
const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setChecked(event.target.checked);
|
||||
};
|
||||
|
||||
const handleContinue = () => {
|
||||
handleAccept()
|
||||
setIsHidden(true)
|
||||
}
|
||||
handleAccept();
|
||||
setIsHidden(true);
|
||||
};
|
||||
|
||||
const handleRadioChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSelectedRole(event.target.value as Role);
|
||||
|
||||
@@ -4,13 +4,22 @@ import { Document, Page, pdfjs } from 'react-pdf';
|
||||
import { Typography } from '@mui/material';
|
||||
|
||||
// https://github.com/wojtekmaj/react-pdf?tab=readme-ov-file#copy-worker-to-public-directory
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = process.env.PUBLIC_URL + '/pdf.worker.min.mjs'
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = process.env.PUBLIC_URL + '/pdf.worker.min.mjs';
|
||||
|
||||
const TermsAndConditionsBox = ({height}: {height: string}) => {
|
||||
interface TermsAndConditionsBoxProps {
|
||||
height: string;
|
||||
onLoad?: () => void;
|
||||
}
|
||||
|
||||
const TermsAndConditionsBox = ({ height, onLoad }: TermsAndConditionsBoxProps ) => {
|
||||
const [numPages, setNumPages] = useState<number>();
|
||||
|
||||
function onDocumentLoadSuccess({ numPages }: { numPages: number }): void {
|
||||
setNumPages(numPages);
|
||||
|
||||
if (onLoad){
|
||||
onLoad();
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
+4
-32
@@ -1,35 +1,7 @@
|
||||
export const TNC_GENERIC_CONTENT = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`;
|
||||
export const WALLET_DISCLAIMER_MSG = 'You are connecting to an experimental wallet! It is not secure. Do not use it elsewhere and/or for managing real assets.';
|
||||
|
||||
export const TNC_VALIDATOR_CONTENT = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`;
|
||||
|
||||
export const TNC_PARTICIPANT_CONTENT = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`;
|
||||
|
||||
export const WALLET_DISCLAIMER_MSG = 'You are connecting to an experimental wallet! It is not secure. Do not use it elsewhere and/or for managing real assets.'
|
||||
|
||||
export const REDIRECT_EMAIL_MSG = 'Please check your inbox and click the link to verify your email address.'
|
||||
export const REDIRECT_EMAIL_MSG = 'Close this tab and the confirmation link in your email will bring you back to the onboarding app.';
|
||||
|
||||
export const ENABLE_KYC = false;
|
||||
|
||||
export const HASHED_SUBSCRIBER_ID_KEY = 'subscriberIdHash';
|
||||
|
||||
@@ -22,6 +22,7 @@ assert(PROJECT_ID, "Wallet connect project id not provided");
|
||||
interface ContextValue {
|
||||
connect: () => Promise<void>;
|
||||
session: SessionTypes.Struct | null;
|
||||
isSessionLoading: boolean;
|
||||
signClient: SignClient | undefined;
|
||||
checkPersistedState: (client: SignClient) => Promise<void>;
|
||||
disconnect: () => Promise<void>;
|
||||
@@ -30,6 +31,7 @@ interface ContextValue {
|
||||
const walletConnectContext = createContext<ContextValue>({
|
||||
connect: () => Promise.resolve(),
|
||||
session: null,
|
||||
isSessionLoading: true,
|
||||
signClient: undefined,
|
||||
checkPersistedState: () => Promise.resolve(),
|
||||
disconnect: () => Promise.resolve(),
|
||||
@@ -47,6 +49,7 @@ export const WalletConnectProvider = ({
|
||||
}) => {
|
||||
const [signClient, setSignClient] = useState<SignClient>();
|
||||
const [session, setSession] = useState<SessionTypes.Struct | null>(null);
|
||||
const [isSessionLoading, setIsSessionLoading] = useState(true);
|
||||
const isSignClientInitializing = useRef<boolean>(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -59,6 +62,7 @@ export const WalletConnectProvider = ({
|
||||
}
|
||||
|
||||
setSession(null);
|
||||
setIsSessionLoading(false);
|
||||
}, [signClient, session]);
|
||||
|
||||
const checkPersistedState = useCallback(async (client: SignClient) => {
|
||||
@@ -67,6 +71,8 @@ export const WalletConnectProvider = ({
|
||||
const session = client.session.get(client.session.keys[lastKeyIndex]);
|
||||
setSession(session);
|
||||
}
|
||||
|
||||
setIsSessionLoading(false);
|
||||
}, []);
|
||||
|
||||
const subscribeToEvents = useCallback(
|
||||
@@ -76,10 +82,12 @@ export const WalletConnectProvider = ({
|
||||
const currentSession = client.session.get(topic);
|
||||
const updatedSession = { ...currentSession, namespaces };
|
||||
setSession(updatedSession);
|
||||
setIsSessionLoading(false);
|
||||
});
|
||||
|
||||
client.on("session_delete", () => {
|
||||
setSession(null);
|
||||
setIsSessionLoading(false);
|
||||
navigate("/");
|
||||
});
|
||||
},
|
||||
@@ -133,12 +141,14 @@ export const WalletConnectProvider = ({
|
||||
try {
|
||||
const session = await approval();
|
||||
setSession(session);
|
||||
setIsSessionLoading(false);
|
||||
} catch (error) {
|
||||
enqueueSnackbar("User rejected pairing request", { variant: "error" });
|
||||
console.log(error);
|
||||
}
|
||||
web3Modal.closeModal();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -152,6 +162,7 @@ export const WalletConnectProvider = ({
|
||||
value={{
|
||||
connect,
|
||||
session,
|
||||
isSessionLoading,
|
||||
signClient,
|
||||
checkPersistedState,
|
||||
disconnect,
|
||||
@@ -163,12 +174,13 @@ export const WalletConnectProvider = ({
|
||||
};
|
||||
|
||||
export const useWalletConnectContext = () => {
|
||||
const { connect, session, signClient, checkPersistedState, disconnect } =
|
||||
const { connect, session, signClient, isSessionLoading, checkPersistedState, disconnect } =
|
||||
useContext(walletConnectContext);
|
||||
|
||||
return {
|
||||
connect,
|
||||
session,
|
||||
isSessionLoading,
|
||||
signClient,
|
||||
checkPersistedState,
|
||||
disconnect,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { Outlet, useNavigate } from "react-router-dom";
|
||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
import {
|
||||
Toolbar,
|
||||
@@ -14,10 +14,14 @@ import { useWalletConnectContext } from "../context/WalletConnectContext";
|
||||
const SignPageLayout = () => {
|
||||
const { disconnect, session } = useWalletConnectContext();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const disconnectHandler = async () => {
|
||||
const { pathname } = location;
|
||||
const redirectTo = pathname ? pathname.substring(1) : "";
|
||||
|
||||
await disconnect();
|
||||
navigate("/");
|
||||
navigate(`/connect-wallet?redirectTo=${redirectTo}`);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect } from "react";
|
||||
import {useLocation, useNavigate } from "react-router-dom";
|
||||
import {useLocation, useNavigate, useSearchParams } from "react-router-dom";
|
||||
|
||||
import { Button, Box, Container, Typography, colors } from "@mui/material";
|
||||
|
||||
@@ -7,19 +7,37 @@ import { useWalletConnectContext } from "../context/WalletConnectContext";
|
||||
import { WALLET_DISCLAIMER_MSG } from "../constants";
|
||||
|
||||
const ConnectWallet = () => {
|
||||
const { connect, session } = useWalletConnectContext();
|
||||
const { connect, session, signClient, checkPersistedState } = useWalletConnectContext();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const redirectTo = searchParams.get("redirectTo");
|
||||
|
||||
useEffect(() => {
|
||||
if (signClient && !session) {
|
||||
checkPersistedState(signClient);
|
||||
}
|
||||
}, [checkPersistedState, signClient, session]);
|
||||
|
||||
if (session) {
|
||||
useEffect(() => {
|
||||
if (!session) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (redirectTo) {
|
||||
navigate(`/${redirectTo}`, {
|
||||
state: location.state
|
||||
});
|
||||
}
|
||||
|
||||
else {
|
||||
navigate("/sign-with-nitro-key", {
|
||||
state: location.state
|
||||
});
|
||||
}
|
||||
}, [session, navigate, location]);
|
||||
}, [session, navigate, redirectTo, location.state]);
|
||||
|
||||
const handler = async () => {
|
||||
await connect();
|
||||
|
||||
+6
-6
@@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
import React from 'react';
|
||||
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
import { REDIRECT_EMAIL_MSG } from '../constants'
|
||||
import { REDIRECT_EMAIL_MSG } from '../constants';
|
||||
|
||||
const Email = () => {
|
||||
return (
|
||||
@@ -27,7 +27,7 @@ const Email = () => {
|
||||
{REDIRECT_EMAIL_MSG}
|
||||
</Typography>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default Email
|
||||
export default Email;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { Button, Box, Typography } from '@mui/material';
|
||||
|
||||
import TermsAndConditionsBox from '../components/TermsAndConditionsBox';
|
||||
|
||||
const LandingPage = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [isDisabled, setIsDisabled] = useState(true);
|
||||
|
||||
const handleAccept = () => {
|
||||
navigate('/verify-email');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
margin={10}
|
||||
sx={{
|
||||
border: 1,
|
||||
borderColor: 'grey.500',
|
||||
}}
|
||||
padding={5}
|
||||
>
|
||||
<Typography variant="h6">
|
||||
Welcome to the LORO Testnet Onboarding App. The detailed instructions for completing this first step are found in the{' '}
|
||||
<a href="https://github.com/hyphacoop/loro-testnet/" target="_blank" rel="noopener noreferrer">LORO testnet repo</a>.
|
||||
Once your onboarding transaction has been submitted, await the completion of stage0. The genesis.json file and peer nodes will then be
|
||||
published in the aforementioned repository for validators to begin stage1. Once enough validators are online and the Laconic chain is running,
|
||||
those same validators can complete their service provider setup. Once service providers are live, app publishers can start deploying webapps to individual service providers.
|
||||
</Typography>
|
||||
</Box>
|
||||
<TermsAndConditionsBox height="43vh" onLoad={()=>{setIsDisabled(false);}} />
|
||||
<Box m={2} display="flex" justifyContent="center">
|
||||
<Button variant="contained" color="primary" onClick={handleAccept} disabled={isDisabled}>
|
||||
Accept
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LandingPage;
|
||||
@@ -8,14 +8,8 @@ import SumsubWebSdk from "@sumsub/websdk-react";
|
||||
import { MessageHandler } from "@sumsub/websdk";
|
||||
|
||||
import { config, fetchAccessToken, getAccessTokenExpirationHandler, options } from "../utils/sumsub";
|
||||
import { ENABLE_KYC } from "../constants";
|
||||
|
||||
interface Participant {
|
||||
cosmosAddress: string;
|
||||
nitroAddress: string;
|
||||
role: string;
|
||||
kycId: string;
|
||||
}
|
||||
import { ENABLE_KYC, HASHED_SUBSCRIBER_ID_KEY } from "../constants";
|
||||
import { Participant } from "../types";
|
||||
|
||||
const registry = new Registry(
|
||||
process.env.REACT_APP_REGISTRY_GQL_ENDPOINT!
|
||||
@@ -25,7 +19,7 @@ const OnboardingSuccess = () => {
|
||||
const location = useLocation();
|
||||
const { cosmosAddress } = location.state as {
|
||||
cosmosAddress?: string
|
||||
}
|
||||
};
|
||||
|
||||
const [participant, setParticipant] = useState<Participant>();
|
||||
const [token, setToken] = useState<string>('');
|
||||
@@ -48,6 +42,8 @@ const OnboardingSuccess = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.removeItem(HASHED_SUBSCRIBER_ID_KEY);
|
||||
|
||||
setParticipant(participant);
|
||||
} catch (error) {
|
||||
console.error("Error fetching participants", error);
|
||||
@@ -73,54 +69,79 @@ const OnboardingSuccess = () => {
|
||||
}, [cosmosAddress]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
marginTop: 6,
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5">Transaction Successful</Typography>
|
||||
<Typography variant="body1">
|
||||
Participant onboarded: <br />
|
||||
</Typography>
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "lightgray",
|
||||
padding: 3,
|
||||
wordWrap: "break-word",
|
||||
marginBottom: 6,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
marginTop: 6,
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
|
||||
{participant && (
|
||||
<div>
|
||||
<Typography variant="h5">Transaction Successful</Typography>
|
||||
<Typography variant="body1">
|
||||
Participant onboarded: <br />
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "lightgray",
|
||||
padding: 3,
|
||||
wordWrap: "break-word",
|
||||
marginBottom: 6,
|
||||
}}
|
||||
>
|
||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
|
||||
{participant && (
|
||||
<div>
|
||||
Cosmos Address: {participant.cosmosAddress} <br />
|
||||
Nitro Address: {participant.nitroAddress} <br />
|
||||
Role: {participant.role} <br />
|
||||
KYC ID: {participant.kycId} <br />
|
||||
<br />
|
||||
</div>
|
||||
)}
|
||||
</pre>
|
||||
</Box>
|
||||
{ENABLE_KYC ? (
|
||||
<Box>
|
||||
<Typography variant="h5">KYC Status</Typography>
|
||||
{!loading && token && cosmosAddress && (
|
||||
<SumsubWebSdk
|
||||
accessToken={token}
|
||||
expirationHandler={getAccessTokenExpirationHandler(cosmosAddress)}
|
||||
config={config}
|
||||
options={options}
|
||||
onMessage={messageHandler}
|
||||
/>
|
||||
)}
|
||||
<br />
|
||||
</div>
|
||||
)}
|
||||
</pre>
|
||||
</Box>
|
||||
) : ''
|
||||
}
|
||||
</Box>
|
||||
{ENABLE_KYC ? (
|
||||
<Box>
|
||||
<Typography variant="h5">KYC Status</Typography>
|
||||
{!loading && token && cosmosAddress && (
|
||||
<SumsubWebSdk
|
||||
accessToken={token}
|
||||
expirationHandler={getAccessTokenExpirationHandler(cosmosAddress)}
|
||||
config={config}
|
||||
options={options}
|
||||
onMessage={messageHandler}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
) : ''
|
||||
}
|
||||
</Box>
|
||||
<Typography variant="h5">Next Steps</Typography>
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
marginTop={3}
|
||||
sx={{
|
||||
border: 1,
|
||||
borderColor: 'grey.500',
|
||||
}}
|
||||
padding={5}
|
||||
>
|
||||
<Typography variant="body1" gutterBottom sx={{ p: 2 }}>
|
||||
For participants, await the start of the stage 1 chain, which will be announced in various social media channels. In the meantime, familiarize yourself with the{' '}
|
||||
<a href="https://github.com/hyphacoop/loro-testnet/blob/main/docs/publishing-webapps.md" target="_blank" rel="noopener noreferrer">webapp publishing workflow</a>{' '}
|
||||
as this is the main task you will be participating in.<br />
|
||||
<br />
|
||||
For validators, ensure your service provider is running and ready to deploy webapps. Await publication of the laconicd version, genesis file, and peers to the LORO testnet repo, then follow{' '}
|
||||
<a href="https://git.vdb.to/cerc-io/testnet-laconicd-stack/src/branch/main/testnet-onboarding-validator.md#join-as-a-validator-on-stage1" target="_blank" rel="noopener noreferrer">these instructions</a>{' '}
|
||||
for joining stage 1 as a validator.
|
||||
</Typography>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import { StargateClient } from "@cosmjs/stargate";
|
||||
|
||||
import { useWalletConnectContext } from "../context/WalletConnectContext";
|
||||
import SelectRoleCard, {Role} from "../components/SelectRoleCard";
|
||||
import { HASHED_SUBSCRIBER_ID_KEY } from "../constants";
|
||||
|
||||
const SignWithCosmos = () => {
|
||||
const { session, signClient } = useWalletConnectContext();
|
||||
@@ -25,17 +26,17 @@ const SignWithCosmos = () => {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {message: innerMessage, cosmosAddress, receivedEthSig: ethSignature, subscriberIdHash} = location.state as {
|
||||
const {message: innerMessage, cosmosAddress, receivedEthSig: ethSignature} = location.state as {
|
||||
message?: {
|
||||
msg: string;
|
||||
address: string;
|
||||
};
|
||||
cosmosAddress?: string;
|
||||
receivedEthSig?: string;
|
||||
subscriberIdHash?: string;
|
||||
};
|
||||
|
||||
const ethAddress = innerMessage!.address;
|
||||
const subscriberIdHash = localStorage.getItem(HASHED_SUBSCRIBER_ID_KEY);
|
||||
|
||||
const createCosmosClient = useCallback(async (endpoint: string) => {
|
||||
return await StargateClient.connect(endpoint);
|
||||
@@ -99,6 +100,8 @@ const SignWithCosmos = () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
enqueueSnackbar("View and sign the message from your Laconic Wallet", { variant: "info" });
|
||||
|
||||
const params = { transactionMessage, signer: cosmosAddress };
|
||||
const responseFromWallet = await signClient!.request<{
|
||||
code: number;
|
||||
|
||||
@@ -13,21 +13,28 @@ import LoadingButton from '@mui/lab/LoadingButton';
|
||||
import { utf8ToHex } from "@walletconnect/encoding";
|
||||
|
||||
import { useWalletConnectContext } from "../context/WalletConnectContext";
|
||||
import { ENABLE_KYC } from "../constants";
|
||||
import { ENABLE_KYC, HASHED_SUBSCRIBER_ID_KEY } from "../constants";
|
||||
|
||||
const SignWithNitroKey = () => {
|
||||
|
||||
const { session, signClient, checkPersistedState } =
|
||||
const { session, signClient, isSessionLoading } =
|
||||
useWalletConnectContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (signClient && !session) {
|
||||
checkPersistedState(signClient);
|
||||
}
|
||||
}, [session, signClient, checkPersistedState]);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
if (isSessionLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
navigate("/connect-wallet?redirectTo=sign-with-nitro-key", {
|
||||
state: location.state,
|
||||
});
|
||||
}
|
||||
}, [session, isSessionLoading, navigate, location.state]);
|
||||
|
||||
const [ethAddress, setEthAddress] = useState("");
|
||||
const [ethSignature, setEthSignature] = useState("");
|
||||
|
||||
@@ -35,6 +42,17 @@ const SignWithNitroKey = () => {
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const subscriberIdHash = useMemo(()=>{
|
||||
return localStorage.getItem(HASHED_SUBSCRIBER_ID_KEY);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!subscriberIdHash) {
|
||||
setIsLoading(false);
|
||||
enqueueSnackbar("Subscriber ID not found. Please verify your email and try again", { variant: "error" });
|
||||
}
|
||||
}, [subscriberIdHash]);
|
||||
|
||||
const message = useMemo(() => {
|
||||
return {
|
||||
msg: "Register my account as a participant on the Laconic network",
|
||||
@@ -45,7 +63,10 @@ const SignWithNitroKey = () => {
|
||||
const signEth = async () => {
|
||||
if (session && signClient) {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
setIsLoading(true);
|
||||
|
||||
enqueueSnackbar("View and sign the message from your Laconic Wallet", { variant: "info" });
|
||||
|
||||
const jsonMessage = canonicalStringify(message);
|
||||
const hexMsg = utf8ToHex(jsonMessage, true);
|
||||
const receivedEthSig: string = await signClient!.request({
|
||||
@@ -56,7 +77,7 @@ const SignWithNitroKey = () => {
|
||||
params: [hexMsg, ethAddress],
|
||||
},
|
||||
});
|
||||
setIsLoading(false)
|
||||
setIsLoading(false);
|
||||
setEthSignature(ethSignature);
|
||||
|
||||
if (ENABLE_KYC) {
|
||||
@@ -68,26 +89,17 @@ const SignWithNitroKey = () => {
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const state = location.state as {
|
||||
subscriberIdHash?: string
|
||||
}
|
||||
|
||||
if (!state.subscriberIdHash) {
|
||||
throw new Error("Subscriber ID not found. Please verify your email and try again")
|
||||
}
|
||||
|
||||
navigate("/sign-with-cosmos", {
|
||||
state: {
|
||||
message,
|
||||
cosmosAddress,
|
||||
receivedEthSig,
|
||||
subscriberIdHash: state.subscriberIdHash,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err in signing ", error);
|
||||
setIsLoading(false)
|
||||
setIsLoading(false);
|
||||
enqueueSnackbar("Error signing message", { variant: "error" });
|
||||
}
|
||||
}
|
||||
@@ -151,7 +163,7 @@ const SignWithNitroKey = () => {
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
onClick={signEth}
|
||||
disabled={!Boolean(ethAddress)}
|
||||
disabled={!Boolean(ethAddress) || !subscriberIdHash}
|
||||
sx={{ marginTop: 2 }}
|
||||
loading={isLoading}
|
||||
>
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { Container, Button, Box, Paper } from '@mui/material';
|
||||
|
||||
import TermsAndConditionsBox from '../components/TermsAndConditionsBox';
|
||||
|
||||
const TermsAndConditions = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleAccept = () => {
|
||||
navigate('/verify-email');
|
||||
};
|
||||
|
||||
return (
|
||||
<Container maxWidth="lg">
|
||||
<Paper elevation={3} style={{ padding: '2rem', marginTop: '2rem', height: '83vh', display: 'flex', flexDirection: 'column' }}>
|
||||
<TermsAndConditionsBox height='80vh' />
|
||||
<Box mt={2} display="flex" justifyContent="center">
|
||||
<Button variant="contained" color="primary" onClick={handleAccept}>
|
||||
Accept
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default TermsAndConditions;
|
||||
+10
-10
@@ -1,10 +1,12 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
import { Box, colors, Typography } from '@mui/material';
|
||||
|
||||
import { HASHED_SUBSCRIBER_ID_KEY } from '../constants';
|
||||
|
||||
interface JwtPayload {
|
||||
subscriber_id: string;
|
||||
exp: number;
|
||||
@@ -23,29 +25,27 @@ const Thanks: React.FC = () => {
|
||||
const token = queryParams.get('jwt_token');
|
||||
|
||||
try {
|
||||
if(!token){
|
||||
throw new Error("Invalid JWT Token")
|
||||
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")
|
||||
throw new Error("Subscriber ID not found");
|
||||
}
|
||||
|
||||
if (decoded.exp < currentTime) {
|
||||
throw new Error("Token has expired");
|
||||
}
|
||||
|
||||
const subscriberIdBytes = ethers.utils.toUtf8Bytes(decoded.subscriber_id)
|
||||
const subscriberIdBytes = ethers.utils.toUtf8Bytes(decoded.subscriber_id);
|
||||
const subscriberIdHash = ethers.utils.sha256(subscriberIdBytes);
|
||||
|
||||
navigate('/connect-wallet', {
|
||||
state:{
|
||||
subscriberIdHash
|
||||
}
|
||||
});
|
||||
localStorage.setItem(HASHED_SUBSCRIBER_ID_KEY, subscriberIdHash);
|
||||
|
||||
navigate('/sign-with-nitro-key');
|
||||
} catch (error) {
|
||||
setErr(String(error));
|
||||
}
|
||||
|
||||
@@ -45,13 +45,14 @@ const UserVerification = () => {
|
||||
useEffect(() => {
|
||||
if (applicationSubmitted && kycId !== '') {
|
||||
const kycIdHash = ethers.utils.sha256(ethers.utils.toUtf8Bytes(kycId));
|
||||
|
||||
navigate("/sign-with-cosmos", {
|
||||
state: {
|
||||
message,
|
||||
cosmosAddress,
|
||||
receivedEthSig,
|
||||
subscriberIdHash: kycIdHash,
|
||||
}})
|
||||
kycIdHash
|
||||
}});
|
||||
}
|
||||
}, [applicationSubmitted, kycId, navigate, cosmosAddress, message, receivedEthSig]);
|
||||
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { MsgCreateValidator } from 'cosmjs-types/cosmos/staking/v1beta1/tx';
|
||||
|
||||
import { Box, Link, MenuItem, Select, TextField, Typography } from '@mui/material';
|
||||
import { fromBech32, toBech32 } from '@cosmjs/encoding';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { EncodeObject, encodePubkey } from '@cosmjs/proto-signing';
|
||||
import { Registry } from '@cerc-io/registry-sdk';
|
||||
|
||||
import { useWalletConnectContext } from '../context/WalletConnectContext';
|
||||
import { Participant } from '../types';
|
||||
|
||||
const Validator = () => {
|
||||
const { session, signClient, isSessionLoading } = useWalletConnectContext();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [cosmosAddress, setCosmosAddress] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [moniker, setMoniker] = useState('');
|
||||
const [pubKey, setPubKey] = useState('');
|
||||
const [participant, setParticipant] = useState<Participant | null>(null);
|
||||
const [isError, setIsError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSessionLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
navigate("/connect-wallet?redirectTo=validator");
|
||||
}
|
||||
}, [session, navigate, isSessionLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!cosmosAddress) {
|
||||
setParticipant(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchParticipant = async () => {
|
||||
const registry = new Registry(process.env.REACT_APP_REGISTRY_GQL_ENDPOINT!);
|
||||
|
||||
try {
|
||||
const fetchedParticipant = await registry.getParticipantByAddress(cosmosAddress);
|
||||
if (fetchedParticipant) {
|
||||
setParticipant(fetchedParticipant);
|
||||
} else {
|
||||
throw new Error("Participant not found");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching participant", error);
|
||||
enqueueSnackbar("Error in fetching participant", { variant: "error" });
|
||||
setParticipant(null);
|
||||
}
|
||||
};
|
||||
|
||||
fetchParticipant();
|
||||
}, [cosmosAddress]);
|
||||
|
||||
const isMonikerValid = useMemo(() => moniker.trim().length > 0, [moniker]);
|
||||
const isPubKeyValid = useMemo(() => pubKey.length === 44, [pubKey]);
|
||||
|
||||
const msgCreateValidator: MsgCreateValidator = useMemo(() => {
|
||||
const encodedPubKey = encodePubkey({
|
||||
type: "tendermint/PubKeyEd25519",
|
||||
value: pubKey.length === 44 ? pubKey : '',
|
||||
});
|
||||
|
||||
return {
|
||||
description: {
|
||||
moniker,
|
||||
identity: "",
|
||||
website: "",
|
||||
securityContact: "",
|
||||
details: "",
|
||||
},
|
||||
commission: {
|
||||
maxChangeRate: "10000000000000000", // 0.01
|
||||
maxRate: "200000000000000000", // 0.2
|
||||
rate: "100000000000000000", // 0.1
|
||||
},
|
||||
minSelfDelegation: "1",
|
||||
delegatorAddress: '',
|
||||
validatorAddress: cosmosAddress && toBech32('laconicvaloper', fromBech32(cosmosAddress).data),
|
||||
pubkey: encodedPubKey,
|
||||
value: {
|
||||
amount: process.env.REACT_APP_STAKING_AMOUNT!,
|
||||
denom: process.env.REACT_APP_LACONICD_DENOM!,
|
||||
},
|
||||
};
|
||||
}, [cosmosAddress, pubKey, moniker]);
|
||||
|
||||
const msgCreateValidatorEncodeObject: EncodeObject = {
|
||||
typeUrl: '/cosmos.staking.v1beta1.MsgCreateValidator',
|
||||
value: MsgCreateValidator.toJSON(msgCreateValidator),
|
||||
};
|
||||
|
||||
const sendTransaction = async () => {
|
||||
if (
|
||||
!isMonikerValid ||
|
||||
!isPubKeyValid ||
|
||||
!msgCreateValidator.validatorAddress
|
||||
) {
|
||||
setIsError(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
enqueueSnackbar("View and sign the message from your Laconic Wallet", { variant: "info" });
|
||||
|
||||
try {
|
||||
const params = { transactionMessage: msgCreateValidatorEncodeObject, signer: cosmosAddress };
|
||||
const response = await signClient!.request<{ code: number }>({
|
||||
topic: session!.topic,
|
||||
chainId: `cosmos:${process.env.REACT_APP_LACONICD_CHAIN_ID}`,
|
||||
request: {
|
||||
method: "cosmos_sendTransaction",
|
||||
params,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.code !== 0) {
|
||||
throw new Error("Transaction not sent");
|
||||
} else {
|
||||
navigate("/validator-success", { state: { validatorAddress: msgCreateValidator.validatorAddress, } });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error sending transaction", error);
|
||||
enqueueSnackbar("Error in sending transaction", { variant: "error" });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const replacer = (key: string, value: any): any => {
|
||||
if (value instanceof Uint8Array) {
|
||||
return Buffer.from(value).toString('hex');
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ display: "flex", flexDirection: "column", marginTop: 6, gap: 1 }}>
|
||||
<Typography variant="h5">Create a validator</Typography>
|
||||
<Typography variant="body1">Select Laconic account:</Typography>
|
||||
<Select
|
||||
sx={{ marginBottom: 2 }}
|
||||
id="cosmos-address-select"
|
||||
value={cosmosAddress}
|
||||
onChange={(e) => setCosmosAddress(e.target.value)}
|
||||
style={{ maxWidth: "600px", display: "block" }}
|
||||
>
|
||||
{session?.namespaces.cosmos.accounts.map((address, index) => (
|
||||
<MenuItem value={address.split(":")[2]} key={index}>
|
||||
{address.split(":")[2]}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
{Boolean(cosmosAddress) && (
|
||||
<>
|
||||
{participant ? (
|
||||
<Typography>Onboarded participant</Typography>
|
||||
) : (
|
||||
<Typography>No participant found</Typography>
|
||||
)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: participant ? "lightgray" : "white",
|
||||
padding: 3,
|
||||
wordWrap: "break-word",
|
||||
marginBottom: 3,
|
||||
}}
|
||||
>
|
||||
{participant && (
|
||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
|
||||
Cosmos Address: {participant.cosmosAddress} <br />
|
||||
Nitro Address: {participant.nitroAddress} <br />
|
||||
Role: {participant.role} <br />
|
||||
KYC ID: {participant.kycId} <br />
|
||||
</pre>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{participant?.role === "validator" && (
|
||||
<>
|
||||
<Box style={{ maxWidth: "600px" }}>
|
||||
<TextField
|
||||
id="moniker"
|
||||
label="Enter your node moniker (example: AliceNode)"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
margin="normal"
|
||||
value={moniker}
|
||||
onChange={(e) => {
|
||||
setIsError(false);
|
||||
setMoniker(e.target.value);
|
||||
}}
|
||||
error={!isMonikerValid && isError}
|
||||
helperText={!isMonikerValid && isError ? "Moniker is required" : ""}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Typography sx={{ marginTop: 3}}>
|
||||
Fetch your validator public key using the following command (refer
|
||||
<Link
|
||||
href="https://git.vdb.to/cerc-io/testnet-laconicd-stack/src/branch/main/testnet-onboarding-validator.md#join-as-testnet-validator"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
this guide
|
||||
</Link>
|
||||
)
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ backgroundColor: "lightgray", padding: 3, wordWrap: "break-word" }}>
|
||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
|
||||
{`laconic-so deployment --dir testnet-laconicd-deployment exec laconicd "laconicd cometbft show-validator" | jq -r .key`}
|
||||
</pre>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ maxWidth: "600px" }}>
|
||||
<TextField
|
||||
id="pub-key"
|
||||
label="Enter your validator public key"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
margin="normal"
|
||||
value={pubKey}
|
||||
onChange={(e) => {
|
||||
setIsError(false);
|
||||
setPubKey(e.target.value);
|
||||
}}
|
||||
error={!isPubKeyValid && isError}
|
||||
helperText={!isPubKeyValid && isError ? "Public key must be 44 characters" : ""}
|
||||
/>
|
||||
</Box>
|
||||
<Typography>Send transaction to chain</Typography>
|
||||
<Box sx={{ backgroundColor: "lightgray", padding: 3, wordWrap: "break-word" }}>
|
||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
|
||||
{JSON.stringify(msgCreateValidator, replacer, 2)}
|
||||
</pre>
|
||||
</Box>
|
||||
<Box marginTop={1} marginBottom={1}>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
onClick={sendTransaction}
|
||||
loading={isLoading}
|
||||
disabled={isError}
|
||||
>
|
||||
Send transaction
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Validator;
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { Box, Link, Typography } from '@mui/material';
|
||||
|
||||
const ValidatorSuccess = () => {
|
||||
const location = useLocation();
|
||||
const { validatorAddress } = location.state as {
|
||||
validatorAddress?: string
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
marginTop: 6,
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5">Validator created successfully</Typography>
|
||||
<Typography sx={{ marginTop: 3}}>
|
||||
You can view your validator details using the following command (Refer
|
||||
<Link
|
||||
href="https://git.vdb.to/cerc-io/testnet-laconicd-stack/src/branch/main/testnet-onboarding-validator.md#join-as-testnet-validator"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
this guide
|
||||
</Link>
|
||||
)
|
||||
</Typography>
|
||||
<Box sx={{ backgroundColor: "lightgray", padding: 2, wordWrap: "break-word", marginTop: 2, fontSize: 14}}>
|
||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
|
||||
{`laconic-so deployment --dir testnet-laconicd-deployment exec laconicd "laconicd query staking validators --output json" | jq '.validators[] | select(.operator_address == "${validatorAddress}")'`}
|
||||
</pre>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ValidatorSuccess;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React from 'react';
|
||||
|
||||
const VerifyEmail = () => {
|
||||
return (
|
||||
@@ -18,7 +18,7 @@ const VerifyEmail = () => {
|
||||
}}
|
||||
></iframe>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default VerifyEmail
|
||||
export default VerifyEmail;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface Participant {
|
||||
cosmosAddress: string;
|
||||
nitroAddress: string;
|
||||
role: string;
|
||||
kycId: string;
|
||||
}
|
||||
+1
-1
@@ -29,5 +29,5 @@ export const getAccessTokenExpirationHandler = (userId: string) => {
|
||||
return async () => {
|
||||
const newToken = await fetchAccessToken(userId);
|
||||
return newToken;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user