Merge pull request 'Skin onboarding app' (#33) from style/vaidator into main
Reviewed-on: #33
This commit is contained in:
commit
eba6cd5d68
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "testnet-onboarding-app",
|
"name": "testnet-onboarding-app",
|
||||||
"version": "0.1.0",
|
"version": "0.1.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cerc-io/registry-sdk": "^0.2.5",
|
"@cerc-io/registry-sdk": "^0.2.5",
|
||||||
|
@ -1,21 +1,24 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
<head>
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta
|
<meta name="theme-color" content="#000000" />
|
||||||
name="description"
|
<meta name="description" content="Testnet Onboarding App" />
|
||||||
content="Testnet Onboarding App"
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
/>
|
<!--
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
-->
|
-->
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
<!--
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"
|
||||||
|
rel="stylesheet" />
|
||||||
|
<!--
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
@ -24,52 +27,53 @@
|
|||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>Testnet Onboarding App</title>
|
<title>Testnet Onboarding App</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
border: 16px solid #e3e3e3;
|
||||||
|
border-top: 16px solid #1976d2;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.loader-wrapper {
|
100% {
|
||||||
width: 100%;
|
transform: rotate(360deg);
|
||||||
height: 100%;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.loader {
|
</style>
|
||||||
border: 16px solid #e3e3e3;
|
</head>
|
||||||
border-top: 16px solid #1976d2;
|
|
||||||
border-radius: 50%;
|
<body>
|
||||||
width: 140px;
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
height: 140px;
|
<div id="root">
|
||||||
animation: spin 1s linear infinite;
|
<div class="loader-wrapper">
|
||||||
}
|
<div class="loader"></div>
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id="root">
|
|
||||||
<div class="loader-wrapper">
|
|
||||||
<div class="loader"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<!--
|
</div>
|
||||||
|
<!--
|
||||||
This HTML file is a template.
|
This HTML file is a template.
|
||||||
If you open it directly in the browser, you will see an empty page.
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
@ -79,5 +83,6 @@
|
|||||||
To begin the development, run `npm start` or `yarn start`.
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
-->
|
-->
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
141
src/App.tsx
141
src/App.tsx
@ -16,44 +16,117 @@ import Email from "./pages/Email";
|
|||||||
import Thanks from "./pages/Thanks";
|
import Thanks from "./pages/Thanks";
|
||||||
import Validator from "./pages/Validator";
|
import Validator from "./pages/Validator";
|
||||||
import ValidatorSuccess from "./pages/ValidatorSuccess";
|
import ValidatorSuccess from "./pages/ValidatorSuccess";
|
||||||
|
import { createTheme, Box, ThemeProvider, CssBaseline } from "@mui/material";
|
||||||
|
|
||||||
|
const darkTheme = createTheme({
|
||||||
|
components: {
|
||||||
|
MuiAccordion: {
|
||||||
|
defaultProps: {
|
||||||
|
sx: {
|
||||||
|
border: "1px solid #48474F",
|
||||||
|
borderBottomRightRadius: 3,
|
||||||
|
borderBottomLeftRadius: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiButton: {
|
||||||
|
defaultProps: {
|
||||||
|
color: "primary",
|
||||||
|
sx: {
|
||||||
|
fontFamily: `DM Mono, monospace`,
|
||||||
|
fontWeight: 400,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiLink: {
|
||||||
|
defaultProps: {
|
||||||
|
color: "text.primary",
|
||||||
|
fontSize: "14px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiTypography: {
|
||||||
|
defaultProps: {
|
||||||
|
color: "text.primary",
|
||||||
|
fontWeight: 400,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPaper: {
|
||||||
|
defaultProps: {
|
||||||
|
sx: {
|
||||||
|
backgroundImage: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
palette: {
|
||||||
|
mode: "dark",
|
||||||
|
primary: {
|
||||||
|
main: "#0000F4",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#A2A2FF",
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
main: "#B20710",
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
default: "#0F0F0F",
|
||||||
|
paper: "#18181A",
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#FBFBFB",
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
main: "#FBFBFB",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<Header />
|
<ThemeProvider theme={darkTheme}>
|
||||||
<WalletConnectProvider>
|
<Box
|
||||||
<Routes>
|
sx={{
|
||||||
<Route path="/" element={<LandingPage />} />
|
height: "100vh",
|
||||||
<Route path="/verify-email" element={<VerifyEmail />} />
|
width: "100vw",
|
||||||
<Route path="/email" element={<Email/>} />
|
backgroundColor: "background.default",
|
||||||
<Route path="/connect-wallet" element={<ConnectWallet />} />
|
}}
|
||||||
<Route path="/thanks" element={<Thanks />} />
|
>
|
||||||
<Route element={<SignPageLayout />}>
|
<Header />
|
||||||
<Route path="/sign-with-nitro-key" element={<SignWithNitroKey />} />
|
<CssBaseline />
|
||||||
<Route
|
<WalletConnectProvider>
|
||||||
path="/user-verification"
|
<Routes>
|
||||||
element={<UserVerification />}
|
<Route path="/" element={<LandingPage />} />
|
||||||
/>
|
<Route path="/verify-email" element={<VerifyEmail />} />
|
||||||
<Route
|
<Route path="/email" element={<Email />} />
|
||||||
path="/sign-with-cosmos"
|
<Route path="/connect-wallet" element={<ConnectWallet />} />
|
||||||
element={<SignWithCosmos />}
|
<Route path="/thanks" element={<Thanks />} />
|
||||||
/>
|
<Route element={<SignPageLayout />}>
|
||||||
<Route
|
<Route
|
||||||
path="/onboarding-success"
|
path="/sign-with-nitro-key"
|
||||||
element={<OnboardingSuccess />}
|
element={<SignWithNitroKey />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/validator"
|
path="/user-verification"
|
||||||
element={<Validator />}
|
element={<UserVerification />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route path="/sign-with-cosmos" element={<SignWithCosmos />} />
|
||||||
path="/validator-success"
|
<Route
|
||||||
element={<ValidatorSuccess />}
|
path="/onboarding-success"
|
||||||
/>
|
element={<OnboardingSuccess />}
|
||||||
</Route>
|
/>
|
||||||
<Route path="*" element={<PageNotFound />} />
|
<Route path="/validator" element={<Validator />} />
|
||||||
</Routes>
|
<Route
|
||||||
</WalletConnectProvider>
|
path="/validator-success"
|
||||||
|
element={<ValidatorSuccess />}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
<Route path="*" element={<PageNotFound />} />
|
||||||
|
</Routes>
|
||||||
|
</WalletConnectProvider>
|
||||||
|
</Box>
|
||||||
|
</ThemeProvider>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
25
src/components/CodeBlock.tsx
Normal file
25
src/components/CodeBlock.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Box } from "@mui/material";
|
||||||
|
import React, { PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
export const CodeBlock: React.FC<PropsWithChildren> = ({ children }) => (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#48474F",
|
||||||
|
padding: 3,
|
||||||
|
wordWrap: "break-word",
|
||||||
|
mt: 1,
|
||||||
|
borderRadius: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
whiteSpace: "pre-wrap",
|
||||||
|
margin: 0,
|
||||||
|
backgroundColor: "#48474F",
|
||||||
|
color: "#FBFBFB",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</pre>
|
||||||
|
</Box>
|
||||||
|
);
|
21
src/components/Container.tsx
Normal file
21
src/components/Container.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Box, BoxProps } from "@mui/material";
|
||||||
|
import React, { PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
export const Container: React.FC<
|
||||||
|
PropsWithChildren<{ boxProps?: BoxProps }>
|
||||||
|
> = ({ children, boxProps = {} }) => (
|
||||||
|
<Box
|
||||||
|
{...boxProps}
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: "752px",
|
||||||
|
mx: "auto",
|
||||||
|
backgroundColor: "background.paper",
|
||||||
|
padding: 3,
|
||||||
|
borderRadius: 2,
|
||||||
|
...boxProps.sx,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
@ -1,31 +1,90 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Link, useLocation } from 'react-router-dom';
|
import { Link, useLocation } from "react-router-dom";
|
||||||
|
|
||||||
import { AppBar, Toolbar, Avatar, Box, IconButton } from '@mui/material';
|
import { AppBar, SvgIcon, Stack, Divider, Typography } from "@mui/material";
|
||||||
|
|
||||||
const Header: React.FC = () => {
|
const Header: React.FC = () => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar position="static" color="inherit">
|
<AppBar
|
||||||
<Toolbar>
|
position="static"
|
||||||
<Link to={location.pathname === "/" ? "/" : "/connect-wallet"} style={{ color: "inherit", textDecoration: "none" }}>
|
color="inherit"
|
||||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
sx={{ boxShadow: "none", mb: 4, height: 48 }}
|
||||||
<Avatar
|
>
|
||||||
alt="Laconic logo"
|
<Stack
|
||||||
src="https://avatars.githubusercontent.com/u/92608123"
|
direction="row"
|
||||||
/>
|
sx={{
|
||||||
<IconButton
|
backgroundColor: "background.paper",
|
||||||
edge="start"
|
pl: 2,
|
||||||
color="inherit"
|
alignItems: "center",
|
||||||
aria-label="menu"
|
py: 1,
|
||||||
sx={{ ml: 2, mr: 2 }}
|
}}
|
||||||
|
spacing={1}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
to={location.pathname === "/" ? "/" : "/connect-wallet"}
|
||||||
|
style={{
|
||||||
|
color: "inherit",
|
||||||
|
textDecoration: "none",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
marginRight: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SvgIcon sx={{ height: 20, width: 100 }}>
|
||||||
|
<svg
|
||||||
|
width="115"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 115 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
Testnet Onboarding
|
<path
|
||||||
</IconButton>
|
fillRule="evenodd"
|
||||||
</Box>
|
clipRule="evenodd"
|
||||||
|
d="M3.37388 10.5194C5.70149 8.19185 7.14225 4.97748 7.1416 1.42853C7.14246 0.94681 7.11586 0.470456 7.063 0L-0.000488281 0.000643078L-0.000273922 13.5723C-0.000917354 15.2174 0.62632 16.863 1.88091 18.1175C3.1356 19.3721 4.78235 20.0001 6.42772 19.9993L6.42729 19.9997L19.9995 20L19.999 12.9355C19.5296 12.8838 19.0532 12.857 18.5704 12.8569C15.0224 12.8574 11.8079 14.298 9.48026 16.6255C7.78654 18.2768 5.07093 18.2771 3.39812 16.6043C1.72638 14.9325 1.72562 12.2161 3.37388 10.5194ZM18.5344 1.46863C16.5837 -0.481929 13.4146 -0.48268 11.4633 1.46863C9.512 3.41984 9.51276 6.58895 11.4633 8.53941C13.415 10.491 16.5831 10.4907 18.5344 8.53941C20.4857 6.5882 20.4861 3.42016 18.5344 1.46863Z"
|
||||||
|
fill="#FBFBFB"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M31.4741 18.5838H39.2552V16.3302H34.075V1.41351H31.4741V18.5838Z"
|
||||||
|
fill="#FBFBFB"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M49.8108 1.41351H45.4976L40.9893 18.5838H43.6769L44.8039 14.2913H50.3744L51.5014 18.5838H54.3191L49.8108 1.41351ZM45.3458 12.145L47.6 3.2593H47.6866L49.8541 12.145H45.3458Z"
|
||||||
|
fill="#FBFBFB"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M62.9292 8.06885H65.9636C65.9636 3.17534 64.3813 1.07196 60.6967 1.07196C56.8169 1.07196 55.1479 3.73341 55.1479 9.97909C55.1479 16.2462 56.8169 18.9291 60.6967 18.9291C64.3813 18.9291 65.9636 16.8901 65.9853 12.1468H62.9508C62.9292 15.8599 62.474 16.7828 60.6967 16.7828C58.6593 16.7828 58.1607 15.4307 58.1824 9.97909C58.1824 4.54896 58.6809 3.19678 60.6967 3.21823C62.474 3.21823 62.9292 4.18413 62.9292 8.06885Z"
|
||||||
|
fill="#FBFBFB"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M73.7781 1.07209C77.7229 1.09364 79.4135 3.77643 79.4135 10.0007C79.4135 16.2249 77.7229 18.9078 73.7781 18.9292C69.8117 18.9507 68.1211 16.2678 68.1211 10.0007C68.1211 3.73354 69.8117 1.05064 73.7781 1.07209ZM71.1555 10.0007C71.1555 15.4308 71.6757 16.783 73.7781 16.783C75.8589 16.783 76.3791 15.4308 76.3791 10.0007C76.3791 4.54909 75.8589 3.19691 73.7781 3.21847C71.6757 3.23992 71.1555 4.59209 71.1555 10.0007Z"
|
||||||
|
fill="#FBFBFB"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M85.0819 18.5624L82.481 18.5838V1.41351H87.0544L91.3243 15.4073H91.3676V1.41351H93.968V18.5838H89.677L85.1254 3.51689H85.0819V18.5624Z"
|
||||||
|
fill="#FBFBFB"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M100.468 1.41351H97.8677V18.5838H100.468V1.41351Z"
|
||||||
|
fill="#FBFBFB"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M111.139 8.06885H114.174C114.174 3.17534 112.591 1.07196 108.906 1.07196C105.028 1.07196 103.358 3.73341 103.358 9.97909C103.358 16.2462 105.028 18.9291 108.906 18.9291C112.591 18.9291 114.174 16.8901 114.195 12.1468H111.161C111.139 15.8599 110.684 16.7828 108.906 16.7828C106.869 16.7828 106.371 15.4307 106.393 9.97909C106.393 4.54896 106.891 3.19678 108.906 3.21823C110.684 3.21823 111.139 4.18413 111.139 8.06885Z"
|
||||||
|
fill="#FBFBFB"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</SvgIcon>
|
||||||
</Link>
|
</Link>
|
||||||
</Toolbar>
|
<Divider
|
||||||
|
flexItem
|
||||||
|
orientation="vertical"
|
||||||
|
color="#FBFBFB"
|
||||||
|
sx={{ height: "1.2rem", alignSelf: "center", width: "1px" }}
|
||||||
|
/>
|
||||||
|
<Typography fontSize="1.25rem">Testnet Onboarding</Typography>
|
||||||
|
</Stack>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -44,12 +44,12 @@ const SelectRoleCard = ({ handleAccept, handleRoleChange }: { handleAccept: () =
|
|||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value={Role.Validator}
|
value={Role.Validator}
|
||||||
control={<Radio />}
|
control={<Radio />}
|
||||||
label="Validator"
|
label="Validator / Service Provider"
|
||||||
/>
|
/>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value={Role.Participant}
|
value={Role.Participant}
|
||||||
control={<Radio />}
|
control={<Radio />}
|
||||||
label="Participant"
|
label="App Publisher"
|
||||||
/>
|
/>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
@ -1,43 +1,47 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from "react";
|
||||||
import { Document, Page, pdfjs } from 'react-pdf';
|
import { Document, Page, pdfjs } from "react-pdf";
|
||||||
|
|
||||||
import { Typography } from '@mui/material';
|
import { Typography } from "@mui/material";
|
||||||
|
|
||||||
// https://github.com/wojtekmaj/react-pdf?tab=readme-ov-file#copy-worker-to-public-directory
|
// 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";
|
||||||
|
|
||||||
interface TermsAndConditionsBoxProps {
|
interface TermsAndConditionsBoxProps {
|
||||||
height: string;
|
height: string;
|
||||||
onLoad?: () => void;
|
onLoad?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TermsAndConditionsBox = ({ height, onLoad }: TermsAndConditionsBoxProps ) => {
|
const TermsAndConditionsBox = ({
|
||||||
|
height,
|
||||||
|
onLoad,
|
||||||
|
}: TermsAndConditionsBoxProps) => {
|
||||||
const [numPages, setNumPages] = useState<number>();
|
const [numPages, setNumPages] = useState<number>();
|
||||||
|
|
||||||
function onDocumentLoadSuccess({ numPages }: { numPages: number }): void {
|
function onDocumentLoadSuccess({ numPages }: { numPages: number }): void {
|
||||||
setNumPages(numPages);
|
setNumPages(numPages);
|
||||||
|
|
||||||
if (onLoad){
|
if (onLoad) {
|
||||||
onLoad();
|
onLoad();
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography variant='h4' textAlign="center" gutterBottom>
|
<Typography variant="h4" textAlign="center" gutterBottom>
|
||||||
Terms and Conditions
|
Terms and Conditions
|
||||||
</Typography>
|
</Typography>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: height,
|
height: height,
|
||||||
overflowY: 'auto',
|
overflowY: "auto",
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
alignItems: 'center'
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Document
|
<Document
|
||||||
file={process.env.PUBLIC_URL + '/TermsAndConditions.pdf'}
|
file={process.env.PUBLIC_URL + "/TermsAndConditions.pdf"}
|
||||||
onLoadSuccess={onDocumentLoadSuccess}
|
onLoadSuccess={onDocumentLoadSuccess}
|
||||||
>
|
>
|
||||||
{Array.apply(null, Array(numPages))
|
{Array.apply(null, Array(numPages))
|
||||||
@ -49,7 +53,6 @@ const TermsAndConditionsBox = ({ height, onLoad }: TermsAndConditionsBoxProps )
|
|||||||
pageNumber={page}
|
pageNumber={page}
|
||||||
renderTextLayer={false}
|
renderTextLayer={false}
|
||||||
renderAnnotationLayer={false}
|
renderAnnotationLayer={false}
|
||||||
width={1070}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
34
src/layout/Layout.tsx
Normal file
34
src/layout/Layout.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { Button, Typography } from "@mui/material";
|
||||||
|
import React, { PropsWithChildren } from "react";
|
||||||
|
import { Container } from "../components/Container";
|
||||||
|
import { ArrowBack } from "@mui/icons-material";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
export const Layout: React.FC<
|
||||||
|
PropsWithChildren<{
|
||||||
|
title: string;
|
||||||
|
backLinkTitle?: string;
|
||||||
|
noBackButton?: boolean;
|
||||||
|
}>
|
||||||
|
> = ({ children, title, backLinkTitle = "Home", noBackButton = false }) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container boxProps={{ sx: { backgroundColor: "inherit", padding: 0 } }}>
|
||||||
|
{noBackButton ? null : (
|
||||||
|
<Button
|
||||||
|
startIcon={<ArrowBack />}
|
||||||
|
color="info"
|
||||||
|
sx={{ mb: 4 }}
|
||||||
|
onClick={() => navigate("/")}
|
||||||
|
>
|
||||||
|
{backLinkTitle}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Typography variant="h4" sx={{ mb: 4 }}>
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
<Container>{children}</Container>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
@ -1,15 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import {
|
import { Avatar, Button, Stack, Typography } from "@mui/material";
|
||||||
Toolbar,
|
|
||||||
Avatar,
|
|
||||||
Button,
|
|
||||||
Typography,
|
|
||||||
Container
|
|
||||||
} from "@mui/material";
|
|
||||||
|
|
||||||
import { useWalletConnectContext } from "../context/WalletConnectContext";
|
import { useWalletConnectContext } from "../context/WalletConnectContext";
|
||||||
|
import { Container } from "../components/Container";
|
||||||
|
|
||||||
const SignPageLayout = () => {
|
const SignPageLayout = () => {
|
||||||
const { disconnect, session } = useWalletConnectContext();
|
const { disconnect, session } = useWalletConnectContext();
|
||||||
@ -25,24 +20,19 @@ const SignPageLayout = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Stack justifyContent="center" alignItems="center">
|
||||||
<Toolbar variant="dense">
|
<Container
|
||||||
|
boxProps={{
|
||||||
<Button
|
sx: {
|
||||||
variant="outlined"
|
display: "flex",
|
||||||
style={{
|
flexDirection: "row",
|
||||||
marginLeft: "auto",
|
justifyContent: "space-between",
|
||||||
}}
|
mb: 4,
|
||||||
color="error"
|
},
|
||||||
onClick={disconnectHandler}
|
}}
|
||||||
>
|
>
|
||||||
Disconnect
|
|
||||||
</Button>
|
|
||||||
</Toolbar>
|
|
||||||
|
|
||||||
<Container maxWidth="md">
|
|
||||||
{session && (
|
{session && (
|
||||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
<Stack spacing={0.5}>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -51,7 +41,7 @@ const SignPageLayout = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
Connected to: <b> {session.peer.metadata.name}</b>{" "}
|
<b>Connected to:</b> {session.peer.metadata.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Avatar
|
<Avatar
|
||||||
variant="square"
|
variant="square"
|
||||||
@ -66,13 +56,21 @@ const SignPageLayout = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
Session ID: <b>{session.topic} </b>
|
<b>Session ID:</b> {session.topic}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
<Outlet />
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="error"
|
||||||
|
onClick={disconnectHandler}
|
||||||
|
sx={{ color: "text.primary" }}
|
||||||
|
>
|
||||||
|
Disconnect
|
||||||
|
</Button>
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
<Outlet />
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import {useLocation, useNavigate, useSearchParams } from "react-router-dom";
|
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
import { Button, Box, Container, Typography, colors } from "@mui/material";
|
import { Button, Box, Container } from "@mui/material";
|
||||||
|
|
||||||
import { useWalletConnectContext } from "../context/WalletConnectContext";
|
import { useWalletConnectContext } from "../context/WalletConnectContext";
|
||||||
import { WALLET_DISCLAIMER_MSG } from "../constants";
|
|
||||||
|
|
||||||
const ConnectWallet = () => {
|
const ConnectWallet = () => {
|
||||||
const { connect, session, signClient, checkPersistedState } = useWalletConnectContext();
|
const { connect, session, signClient, checkPersistedState } =
|
||||||
|
useWalletConnectContext();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@ -28,13 +28,11 @@ const ConnectWallet = () => {
|
|||||||
|
|
||||||
if (redirectTo) {
|
if (redirectTo) {
|
||||||
navigate(`/${redirectTo}`, {
|
navigate(`/${redirectTo}`, {
|
||||||
state: location.state
|
state: location.state,
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
|
|
||||||
else {
|
|
||||||
navigate("/sign-with-nitro-key", {
|
navigate("/sign-with-nitro-key", {
|
||||||
state: location.state
|
state: location.state,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [session, navigate, redirectTo, location.state]);
|
}, [session, navigate, redirectTo, location.state]);
|
||||||
@ -44,37 +42,16 @@ const ConnectWallet = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="lg">
|
<Container maxWidth="lg" sx={{ height: "75%" }}>
|
||||||
<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
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
padding={5}
|
padding={5}
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
|
height="100%"
|
||||||
>
|
>
|
||||||
<Button
|
<Button variant="contained" onClick={handler}>
|
||||||
variant="contained"
|
|
||||||
onClick={handler}
|
|
||||||
>
|
|
||||||
Connect Wallet
|
Connect Wallet
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from "react";
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Button, Box, Typography } from '@mui/material';
|
import { Button, Box, Typography } from "@mui/material";
|
||||||
|
|
||||||
import TermsAndConditionsBox from '../components/TermsAndConditionsBox';
|
import TermsAndConditionsBox from "../components/TermsAndConditionsBox";
|
||||||
|
import { Container } from "../components/Container";
|
||||||
|
|
||||||
const LandingPage = () => {
|
const LandingPage = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -11,11 +12,11 @@ const LandingPage = () => {
|
|||||||
const [isDisabled, setIsDisabled] = useState(true);
|
const [isDisabled, setIsDisabled] = useState(true);
|
||||||
|
|
||||||
const handleAccept = () => {
|
const handleAccept = () => {
|
||||||
navigate('/verify-email');
|
navigate("/verify-email");
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Container>
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
@ -24,30 +25,61 @@ const LandingPage = () => {
|
|||||||
margin={10}
|
margin={10}
|
||||||
sx={{
|
sx={{
|
||||||
border: 1,
|
border: 1,
|
||||||
borderColor: 'grey.500',
|
borderColor: "grey.500",
|
||||||
|
borderRadius: 1,
|
||||||
}}
|
}}
|
||||||
padding={5}
|
padding={5}
|
||||||
>
|
>
|
||||||
<Typography variant="h6">
|
<Typography variant="h6">
|
||||||
Welcome to the LORO Testnet Onboarding App. The detailed instructions for completing this first step are found in the{' '}
|
Welcome to the LORO Testnet Onboarding App. The detailed instructions
|
||||||
<a href="https://github.com/hyphacoop/loro-testnet/" target="_blank" rel="noopener noreferrer">LORO testnet repo</a>.
|
for completing this first step are found in the{" "}
|
||||||
Once your onboarding transaction has been submitted, await the completion of stage0. The genesis.json file and peer nodes will then be
|
<a
|
||||||
published in the aforementioned repository for validators to begin stage1. Once enough validators are online and the Laconic chain is running,
|
href="https://github.com/hyphacoop/loro-testnet/"
|
||||||
those same validators can complete their service provider setup. Once service providers are live, app publishers can start deploying webapps to individual service providers.
|
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>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<TermsAndConditionsBox height="43vh" onLoad={()=>{setIsDisabled(false);}} />
|
<TermsAndConditionsBox
|
||||||
|
height="43vh"
|
||||||
|
onLoad={() => {
|
||||||
|
setIsDisabled(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Box m={2} display="flex" justifyContent="center" gap={2}>
|
<Box m={2} display="flex" justifyContent="center" gap={2}>
|
||||||
<Button variant="outlined" color="primary" disabled={isDisabled}>
|
<Button
|
||||||
<a href="/TermsAndConditions.pdf" download style={{textDecoration: "none", color: "inherit"}}>
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
sx={{ color: "text.primary" }}
|
||||||
|
disabled={isDisabled}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="/TermsAndConditions.pdf"
|
||||||
|
download
|
||||||
|
style={{textDecoration: "none", color: "inherit"}}
|
||||||
|
>
|
||||||
Download PDF
|
Download PDF
|
||||||
</a>
|
</a>
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="contained" color="primary" onClick={handleAccept} disabled={isDisabled}>
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleAccept}
|
||||||
|
disabled={isDisabled}
|
||||||
|
>
|
||||||
Accept
|
Accept
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,36 +7,43 @@ import { Registry } from "@cerc-io/registry-sdk";
|
|||||||
import SumsubWebSdk from "@sumsub/websdk-react";
|
import SumsubWebSdk from "@sumsub/websdk-react";
|
||||||
import { MessageHandler } from "@sumsub/websdk";
|
import { MessageHandler } from "@sumsub/websdk";
|
||||||
|
|
||||||
import { config, fetchAccessToken, getAccessTokenExpirationHandler, options } from "../utils/sumsub";
|
import {
|
||||||
|
config,
|
||||||
|
fetchAccessToken,
|
||||||
|
getAccessTokenExpirationHandler,
|
||||||
|
options,
|
||||||
|
} from "../utils/sumsub";
|
||||||
import { ENABLE_KYC, HASHED_SUBSCRIBER_ID_KEY } from "../constants";
|
import { ENABLE_KYC, HASHED_SUBSCRIBER_ID_KEY } from "../constants";
|
||||||
import { Participant } from "../types";
|
import { Participant } from "../types";
|
||||||
|
import { CodeBlock } from "../components/CodeBlock";
|
||||||
|
|
||||||
const registry = new Registry(
|
const registry = new Registry(process.env.REACT_APP_REGISTRY_GQL_ENDPOINT!);
|
||||||
process.env.REACT_APP_REGISTRY_GQL_ENDPOINT!
|
|
||||||
);
|
|
||||||
|
|
||||||
const OnboardingSuccess = () => {
|
const OnboardingSuccess = () => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { cosmosAddress } = location.state as {
|
const { cosmosAddress } = location.state as {
|
||||||
cosmosAddress?: string
|
cosmosAddress?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const [participant, setParticipant] = useState<Participant>();
|
const [participant, setParticipant] = useState<Participant>();
|
||||||
const [token, setToken] = useState<string>('');
|
const [token, setToken] = useState<string>("");
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
|
||||||
const messageHandler: MessageHandler = (event, payload) => {
|
const messageHandler: MessageHandler = (event, payload) => {
|
||||||
console.log('sumsubEvent:', event, payload);
|
console.log("sumsubEvent:", event, payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchParticipants = async () => {
|
const fetchParticipants = async () => {
|
||||||
try {
|
try {
|
||||||
if (!cosmosAddress) {
|
if (!cosmosAddress) {
|
||||||
enqueueSnackbar("Laconic address is not provided", { variant: "error" });
|
enqueueSnackbar("Laconic address is not provided", {
|
||||||
|
variant: "error",
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const participant: Participant = await registry.getParticipantByAddress(cosmosAddress);
|
const participant: Participant =
|
||||||
|
await registry.getParticipantByAddress(cosmosAddress);
|
||||||
if (!participant) {
|
if (!participant) {
|
||||||
enqueueSnackbar("Participant not found", { variant: "error" });
|
enqueueSnackbar("Participant not found", { variant: "error" });
|
||||||
return;
|
return;
|
||||||
@ -61,7 +68,7 @@ const OnboardingSuccess = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (cosmosAddress && ENABLE_KYC) {
|
if (cosmosAddress && ENABLE_KYC) {
|
||||||
getToken(cosmosAddress).catch(error => {
|
getToken(cosmosAddress).catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
alert("Failed to fetch token");
|
alert("Failed to fetch token");
|
||||||
});
|
});
|
||||||
@ -82,41 +89,35 @@ const OnboardingSuccess = () => {
|
|||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
Participant onboarded: <br />
|
Participant onboarded: <br />
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box
|
<CodeBlock>
|
||||||
sx={{
|
{participant && (
|
||||||
backgroundColor: "lightgray",
|
<div>
|
||||||
padding: 3,
|
|
||||||
wordWrap: "break-word",
|
|
||||||
marginBottom: 6,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
|
|
||||||
{participant && (
|
|
||||||
<div>
|
|
||||||
Laconic Address: {participant.cosmosAddress} <br />
|
Laconic Address: {participant.cosmosAddress} <br />
|
||||||
Nitro Address: {participant.nitroAddress} <br />
|
Nitro Address: {participant.nitroAddress} <br />
|
||||||
Role: {participant.role} <br />
|
Role: {participant.role} <br />
|
||||||
KYC ID: {participant.kycId} <br />
|
KYC ID: {participant.kycId} <br />
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</pre>
|
</CodeBlock>
|
||||||
</Box>
|
|
||||||
{ENABLE_KYC ? (
|
{ENABLE_KYC ? (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">KYC Status</Typography>
|
<Typography variant="h5">KYC Status</Typography>
|
||||||
{!loading && token && cosmosAddress && (
|
{!loading && token && cosmosAddress && (
|
||||||
<SumsubWebSdk
|
<SumsubWebSdk
|
||||||
accessToken={token}
|
accessToken={token}
|
||||||
expirationHandler={getAccessTokenExpirationHandler(cosmosAddress)}
|
expirationHandler={getAccessTokenExpirationHandler(
|
||||||
|
cosmosAddress,
|
||||||
|
)}
|
||||||
config={config}
|
config={config}
|
||||||
options={options}
|
options={options}
|
||||||
onMessage={messageHandler}
|
onMessage={messageHandler}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
) : ''
|
) : (
|
||||||
}
|
""
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="h5">Next Steps</Typography>
|
<Typography variant="h5">Next Steps</Typography>
|
||||||
<Box
|
<Box
|
||||||
@ -127,18 +128,35 @@ const OnboardingSuccess = () => {
|
|||||||
marginTop={3}
|
marginTop={3}
|
||||||
sx={{
|
sx={{
|
||||||
border: 1,
|
border: 1,
|
||||||
borderColor: 'grey.500',
|
borderColor: "grey.500",
|
||||||
}}
|
}}
|
||||||
padding={5}
|
padding={5}
|
||||||
>
|
>
|
||||||
<Typography variant="body1" gutterBottom sx={{ p: 2 }}>
|
<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{' '}
|
For app publishers, await the start of the stage 1 chain, which will
|
||||||
<a href="https://github.com/hyphacoop/loro-testnet/blob/main/docs/publishing-webapps.md" target="_blank" rel="noopener noreferrer">webapp publishing workflow</a>{' '}
|
be announced in various social media channels. In the meantime,
|
||||||
as this is the main task you will be participating in.<br />
|
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{' '}
|
<br />
|
||||||
<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 validators, ensure your service provider is running and ready to
|
||||||
for joining stage 1 as a validator.
|
deploy webapps. Await publication of the laconicd version, genesis
|
||||||
|
file, and peer info 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/service provider.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
|
@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
|
|
||||||
import { Box, Card, CardContent, Grid, Typography } from "@mui/material";
|
import { Box, Divider, Typography } from "@mui/material";
|
||||||
import LoadingButton from "@mui/lab/LoadingButton/LoadingButton";
|
import LoadingButton from "@mui/lab/LoadingButton/LoadingButton";
|
||||||
import {
|
import {
|
||||||
MsgOnboardParticipantEncodeObject,
|
MsgOnboardParticipantEncodeObject,
|
||||||
@ -11,22 +11,28 @@ import {
|
|||||||
import { StargateClient } from "@cosmjs/stargate";
|
import { StargateClient } from "@cosmjs/stargate";
|
||||||
|
|
||||||
import { useWalletConnectContext } from "../context/WalletConnectContext";
|
import { useWalletConnectContext } from "../context/WalletConnectContext";
|
||||||
import SelectRoleCard, {Role} from "../components/SelectRoleCard";
|
import SelectRoleCard, { Role } from "../components/SelectRoleCard";
|
||||||
import { HASHED_SUBSCRIBER_ID_KEY } from "../constants";
|
import { HASHED_SUBSCRIBER_ID_KEY } from "../constants";
|
||||||
|
import { Layout } from "../layout/Layout";
|
||||||
|
import { CodeBlock } from "../components/CodeBlock";
|
||||||
|
|
||||||
const SignWithCosmos = () => {
|
const SignWithCosmos = () => {
|
||||||
const { session, signClient } = useWalletConnectContext();
|
const { session, signClient } = useWalletConnectContext();
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [balance, setBalance] = useState('');
|
const [balance, setBalance] = useState("");
|
||||||
const [isRequesting, setIsRequesting] = useState(false);
|
const [isRequesting, setIsRequesting] = useState(false);
|
||||||
const [isTncAccepted, setIsTncAccepted] = useState(false);
|
const [isTncAccepted, setIsTncAccepted] = useState(false);
|
||||||
const [role, setRole] = useState(Role.Participant);
|
const [role, setRole] = useState(Role.Participant);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const {message: innerMessage, cosmosAddress, receivedEthSig: ethSignature} = location.state as {
|
const {
|
||||||
|
message: innerMessage,
|
||||||
|
cosmosAddress,
|
||||||
|
receivedEthSig: ethSignature,
|
||||||
|
} = location.state as {
|
||||||
message?: {
|
message?: {
|
||||||
msg: string;
|
msg: string;
|
||||||
address: string;
|
address: string;
|
||||||
@ -51,7 +57,7 @@ const SignWithCosmos = () => {
|
|||||||
ethPayload: innerMessage,
|
ethPayload: innerMessage,
|
||||||
ethSignature: ethSignature!,
|
ethSignature: ethSignature!,
|
||||||
kycId: subscriberIdHash!,
|
kycId: subscriberIdHash!,
|
||||||
role
|
role,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}, [cosmosAddress, innerMessage, ethSignature, subscriberIdHash, role]);
|
}, [cosmosAddress, innerMessage, ethSignature, subscriberIdHash, role]);
|
||||||
@ -59,22 +65,27 @@ const SignWithCosmos = () => {
|
|||||||
const handleTokenRequest = async () => {
|
const handleTokenRequest = async () => {
|
||||||
try {
|
try {
|
||||||
setIsRequesting(true);
|
setIsRequesting(true);
|
||||||
const response = await fetch(`${process.env.REACT_APP_FAUCET_ENDPOINT!}/faucet`, {
|
const response = await fetch(
|
||||||
method: 'POST',
|
`${process.env.REACT_APP_FAUCET_ENDPOINT!}/faucet`,
|
||||||
headers: {
|
{
|
||||||
'Content-Type': 'application/json',
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
address: cosmosAddress,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
);
|
||||||
address: cosmosAddress,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
enqueueSnackbar('Tokens sent successfully', { variant: "success" });
|
enqueueSnackbar("Tokens sent successfully", { variant: "success" });
|
||||||
} else {
|
} else {
|
||||||
const errorResponse = await response.json();
|
const errorResponse = await response.json();
|
||||||
if (response.status === 429) {
|
if (response.status === 429) {
|
||||||
enqueueSnackbar(`${response.statusText} : ${errorResponse.error}`, { variant: "error" });
|
enqueueSnackbar(`${response.statusText} : ${errorResponse.error}`, {
|
||||||
|
variant: "error",
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Error(errorResponse.error);
|
throw new Error(errorResponse.error);
|
||||||
}
|
}
|
||||||
@ -90,7 +101,7 @@ const SignWithCosmos = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const sendTransaction = async (
|
const sendTransaction = async (
|
||||||
transactionMessage: MsgOnboardParticipantEncodeObject
|
transactionMessage: MsgOnboardParticipantEncodeObject,
|
||||||
) => {
|
) => {
|
||||||
if (!ethAddress) {
|
if (!ethAddress) {
|
||||||
enqueueSnackbar("Set nitro address");
|
enqueueSnackbar("Set nitro address");
|
||||||
@ -100,7 +111,9 @@ const SignWithCosmos = () => {
|
|||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
enqueueSnackbar("View and sign the message from your Laconic Wallet", { variant: "info" });
|
enqueueSnackbar("View and sign the message from your Laconic Wallet", {
|
||||||
|
variant: "info",
|
||||||
|
});
|
||||||
|
|
||||||
const params = { transactionMessage, signer: cosmosAddress };
|
const params = { transactionMessage, signer: cosmosAddress };
|
||||||
const responseFromWallet = await signClient!.request<{
|
const responseFromWallet = await signClient!.request<{
|
||||||
@ -118,8 +131,8 @@ const SignWithCosmos = () => {
|
|||||||
} else {
|
} else {
|
||||||
navigate("/onboarding-success", {
|
navigate("/onboarding-success", {
|
||||||
state: {
|
state: {
|
||||||
cosmosAddress
|
cosmosAddress,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -132,11 +145,16 @@ const SignWithCosmos = () => {
|
|||||||
|
|
||||||
const getBalances = useCallback(async () => {
|
const getBalances = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const cosmosClient = await createCosmosClient(process.env.REACT_APP_LACONICD_RPC_ENDPOINT!);
|
const cosmosClient = await createCosmosClient(
|
||||||
const balance = await cosmosClient.getBalance(cosmosAddress!, process.env.REACT_APP_LACONICD_DENOM!);
|
process.env.REACT_APP_LACONICD_RPC_ENDPOINT!,
|
||||||
|
);
|
||||||
|
const balance = await cosmosClient.getBalance(
|
||||||
|
cosmosAddress!,
|
||||||
|
process.env.REACT_APP_LACONICD_DENOM!,
|
||||||
|
);
|
||||||
setBalance(balance.amount);
|
setBalance(balance.amount);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching balance:', error);
|
console.error("Error fetching balance:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}, [cosmosAddress, createCosmosClient]);
|
}, [cosmosAddress, createCosmosClient]);
|
||||||
@ -146,69 +164,58 @@ const SignWithCosmos = () => {
|
|||||||
}, [getBalances]);
|
}, [getBalances]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<>
|
||||||
sx={{
|
{!isTncAccepted && (
|
||||||
display: "flex",
|
<Layout
|
||||||
flexDirection: "column",
|
title="Please accept the terms and conditions to continue"
|
||||||
marginTop: 6,
|
noBackButton
|
||||||
gap: 1,
|
>
|
||||||
}}
|
<SelectRoleCard
|
||||||
>
|
handleAccept={() => setIsTncAccepted(true)}
|
||||||
<Typography variant="h5" display={`${isTncAccepted ? "none" : "block"}`}>Please accept terms and conditions to continue</Typography>
|
handleRoleChange={setRole}
|
||||||
<SelectRoleCard handleAccept={() => setIsTncAccepted(true)} handleRoleChange={setRole}/>
|
/>
|
||||||
<Typography variant="h5">Send transaction to chain</Typography>
|
</Layout>
|
||||||
<Typography>Laconic Account:</Typography>
|
)}
|
||||||
<Card className='mt-1 mb-1'>
|
<Layout title="Send transaction to chain" noBackButton>
|
||||||
<CardContent>
|
<Typography>Laconic Account:</Typography>
|
||||||
<Grid container spacing={2}>
|
<Box sx={{ backgroundColor: "#29292E", p: 2, borderRadius: 1, mb: 2 }}>
|
||||||
<Grid item xs={9}>
|
<Typography variant="body1">Address: {cosmosAddress}</Typography>
|
||||||
<Typography variant="body1">Address: {cosmosAddress}</Typography>
|
<Typography variant="body1">
|
||||||
<Typography variant="body1">Balance: {balance} {process.env.REACT_APP_LACONICD_DENOM}</Typography>
|
Balance: {balance} {process.env.REACT_APP_LACONICD_DENOM}
|
||||||
</Grid>
|
</Typography>
|
||||||
<Grid item xs={3} container justifyContent="flex-end">
|
</Box>
|
||||||
<LoadingButton
|
|
||||||
variant="contained"
|
|
||||||
onClick={handleTokenRequest}
|
|
||||||
disabled={isTncAccepted ? isRequesting : !isTncAccepted}
|
|
||||||
loading={isRequesting}
|
|
||||||
>
|
|
||||||
Request tokens from Faucet
|
|
||||||
</LoadingButton>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
<Typography variant="body1">
|
|
||||||
Onboarding message: <br />
|
|
||||||
</Typography>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
backgroundColor: "lightgray",
|
|
||||||
padding: 3,
|
|
||||||
wordWrap: "break-word",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
|
|
||||||
{JSON.stringify(onboardParticipantMsg, null, 2)}{" "}
|
|
||||||
</pre>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
paddingBottom: 2,
|
|
||||||
}}>
|
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={async () => {
|
onClick={handleTokenRequest}
|
||||||
await sendTransaction(onboardParticipantMsg);
|
disabled={isTncAccepted ? isRequesting : !isTncAccepted}
|
||||||
}}
|
loading={isRequesting}
|
||||||
loading={isLoading}
|
|
||||||
disabled={isTncAccepted ? (balance === '0') : !isTncAccepted}
|
|
||||||
>
|
>
|
||||||
Send transaction
|
Request tokens from Faucet
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
</Box>
|
<Divider flexItem sx={{ my: 2 }} />
|
||||||
</Box>
|
<Typography variant="body1">Onboarding message:</Typography>
|
||||||
|
|
||||||
|
<CodeBlock>{JSON.stringify(onboardParticipantMsg, null, 2)} </CodeBlock>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
paddingBottom: 2,
|
||||||
|
mt: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LoadingButton
|
||||||
|
variant="contained"
|
||||||
|
onClick={async () => {
|
||||||
|
await sendTransaction(onboardParticipantMsg);
|
||||||
|
}}
|
||||||
|
loading={isLoading}
|
||||||
|
disabled={isTncAccepted ? balance === "0" : !isTncAccepted}
|
||||||
|
>
|
||||||
|
Send transaction
|
||||||
|
</LoadingButton>
|
||||||
|
</Box>
|
||||||
|
</Layout>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,22 +3,17 @@ import { useLocation, useNavigate } from "react-router-dom";
|
|||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import canonicalStringify from "canonical-json";
|
import canonicalStringify from "canonical-json";
|
||||||
|
|
||||||
import {
|
import { Select, MenuItem, Box, Typography, Stack } from "@mui/material";
|
||||||
Select,
|
import LoadingButton from "@mui/lab/LoadingButton";
|
||||||
MenuItem,
|
|
||||||
Box,
|
|
||||||
Typography,
|
|
||||||
} from "@mui/material";
|
|
||||||
import LoadingButton from '@mui/lab/LoadingButton';
|
|
||||||
import { utf8ToHex } from "@walletconnect/encoding";
|
import { utf8ToHex } from "@walletconnect/encoding";
|
||||||
|
|
||||||
import { useWalletConnectContext } from "../context/WalletConnectContext";
|
import { useWalletConnectContext } from "../context/WalletConnectContext";
|
||||||
import { ENABLE_KYC, HASHED_SUBSCRIBER_ID_KEY } from "../constants";
|
import { ENABLE_KYC, HASHED_SUBSCRIBER_ID_KEY } from "../constants";
|
||||||
|
import { Layout } from "../layout/Layout";
|
||||||
|
import { CodeBlock } from "../components/CodeBlock";
|
||||||
|
|
||||||
const SignWithNitroKey = () => {
|
const SignWithNitroKey = () => {
|
||||||
|
const { session, signClient, isSessionLoading } = useWalletConnectContext();
|
||||||
const { session, signClient, isSessionLoading } =
|
|
||||||
useWalletConnectContext();
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@ -42,14 +37,17 @@ const SignWithNitroKey = () => {
|
|||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const subscriberIdHash = useMemo(()=>{
|
const subscriberIdHash = useMemo(() => {
|
||||||
return localStorage.getItem(HASHED_SUBSCRIBER_ID_KEY);
|
return localStorage.getItem(HASHED_SUBSCRIBER_ID_KEY);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!subscriberIdHash) {
|
if (!subscriberIdHash) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
enqueueSnackbar("Subscriber ID not found. Please verify your email and try again", { variant: "error" });
|
enqueueSnackbar(
|
||||||
|
"Subscriber ID not found. Please verify your email and try again",
|
||||||
|
{ variant: "error" },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, [subscriberIdHash]);
|
}, [subscriberIdHash]);
|
||||||
|
|
||||||
@ -65,7 +63,9 @@ const SignWithNitroKey = () => {
|
|||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
enqueueSnackbar("View and sign the message from your Laconic Wallet", { variant: "info" });
|
enqueueSnackbar("View and sign the message from your Laconic Wallet", {
|
||||||
|
variant: "info",
|
||||||
|
});
|
||||||
|
|
||||||
const jsonMessage = canonicalStringify(message);
|
const jsonMessage = canonicalStringify(message);
|
||||||
const hexMsg = utf8ToHex(jsonMessage, true);
|
const hexMsg = utf8ToHex(jsonMessage, true);
|
||||||
@ -106,17 +106,9 @@ const SignWithNitroKey = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Layout title="New Session" noBackButton>
|
||||||
{session ? (
|
{session ? (
|
||||||
<Box
|
<Stack spacing={2}>
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
marginTop: 6,
|
|
||||||
gap: 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="h5">Sign with Nitro key</Typography>
|
|
||||||
<Typography variant="body1">Select Laconic account:</Typography>
|
<Typography variant="body1">Select Laconic account:</Typography>
|
||||||
<Select
|
<Select
|
||||||
labelId="demo-simple-select-label"
|
labelId="demo-simple-select-label"
|
||||||
@ -150,15 +142,9 @@ const SignWithNitroKey = () => {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
{(Boolean(ethAddress) && Boolean(cosmosAddress)) && (<Box
|
{Boolean(ethAddress) && Boolean(cosmosAddress) && (
|
||||||
sx={{
|
<CodeBlock>{canonicalStringify(message, null, 2)} </CodeBlock>
|
||||||
backgroundColor: "lightgray",
|
)}
|
||||||
padding: 3,
|
|
||||||
wordWrap: "break-word",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>{canonicalStringify(message, null, 2)} </pre>
|
|
||||||
</Box>)}
|
|
||||||
<Box>
|
<Box>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
variant="contained"
|
variant="contained"
|
||||||
@ -170,11 +156,11 @@ const SignWithNitroKey = () => {
|
|||||||
Sign using Nitro key
|
Sign using Nitro key
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Stack>
|
||||||
) : (
|
) : (
|
||||||
<>Loading...</>
|
<>Loading...</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,25 +1,34 @@
|
|||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import { enqueueSnackbar } from 'notistack';
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from "react-router-dom";
|
||||||
import { MsgCreateValidator } from 'cosmjs-types/cosmos/staking/v1beta1/tx';
|
import { MsgCreateValidator } from "cosmjs-types/cosmos/staking/v1beta1/tx";
|
||||||
|
|
||||||
import { Box, Link, MenuItem, Select, TextField, Typography } from '@mui/material';
|
import {
|
||||||
import { fromBech32, toBech32 } from '@cosmjs/encoding';
|
Box,
|
||||||
import { LoadingButton } from '@mui/lab';
|
Link,
|
||||||
import { EncodeObject, encodePubkey } from '@cosmjs/proto-signing';
|
MenuItem,
|
||||||
import { Registry } from '@cerc-io/registry-sdk';
|
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 { useWalletConnectContext } from "../context/WalletConnectContext";
|
||||||
import { Participant } from '../types';
|
import { Participant } from "../types";
|
||||||
|
import { Layout } from "../layout/Layout";
|
||||||
|
import { CodeBlock } from "../components/CodeBlock";
|
||||||
|
|
||||||
const Validator = () => {
|
const Validator = () => {
|
||||||
const { session, signClient, isSessionLoading } = useWalletConnectContext();
|
const { session, signClient, isSessionLoading } = useWalletConnectContext();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [cosmosAddress, setCosmosAddress] = useState('');
|
const [cosmosAddress, setCosmosAddress] = useState("");
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [moniker, setMoniker] = useState('');
|
const [moniker, setMoniker] = useState("");
|
||||||
const [pubKey, setPubKey] = useState('');
|
const [pubKey, setPubKey] = useState("");
|
||||||
const [participant, setParticipant] = useState<Participant | null>(null);
|
const [participant, setParticipant] = useState<Participant | null>(null);
|
||||||
const [isError, setIsError] = useState(false);
|
const [isError, setIsError] = useState(false);
|
||||||
|
|
||||||
@ -40,10 +49,13 @@ const Validator = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fetchParticipant = async () => {
|
const fetchParticipant = async () => {
|
||||||
const registry = new Registry(process.env.REACT_APP_REGISTRY_GQL_ENDPOINT!);
|
const registry = new Registry(
|
||||||
|
process.env.REACT_APP_REGISTRY_GQL_ENDPOINT!,
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fetchedParticipant = await registry.getParticipantByAddress(cosmosAddress);
|
const fetchedParticipant =
|
||||||
|
await registry.getParticipantByAddress(cosmosAddress);
|
||||||
if (fetchedParticipant) {
|
if (fetchedParticipant) {
|
||||||
setParticipant(fetchedParticipant);
|
setParticipant(fetchedParticipant);
|
||||||
} else {
|
} else {
|
||||||
@ -65,7 +77,7 @@ const Validator = () => {
|
|||||||
const msgCreateValidator: MsgCreateValidator = useMemo(() => {
|
const msgCreateValidator: MsgCreateValidator = useMemo(() => {
|
||||||
const encodedPubKey = encodePubkey({
|
const encodedPubKey = encodePubkey({
|
||||||
type: "tendermint/PubKeyEd25519",
|
type: "tendermint/PubKeyEd25519",
|
||||||
value: pubKey.length === 44 ? pubKey : '',
|
value: pubKey.length === 44 ? pubKey : "",
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -82,8 +94,10 @@ const Validator = () => {
|
|||||||
rate: "100000000000000000", // 0.1
|
rate: "100000000000000000", // 0.1
|
||||||
},
|
},
|
||||||
minSelfDelegation: "1",
|
minSelfDelegation: "1",
|
||||||
delegatorAddress: '',
|
delegatorAddress: "",
|
||||||
validatorAddress: cosmosAddress && toBech32('laconicvaloper', fromBech32(cosmosAddress).data),
|
validatorAddress:
|
||||||
|
cosmosAddress &&
|
||||||
|
toBech32("laconicvaloper", fromBech32(cosmosAddress).data),
|
||||||
pubkey: encodedPubKey,
|
pubkey: encodedPubKey,
|
||||||
value: {
|
value: {
|
||||||
amount: process.env.REACT_APP_STAKING_AMOUNT!,
|
amount: process.env.REACT_APP_STAKING_AMOUNT!,
|
||||||
@ -93,7 +107,7 @@ const Validator = () => {
|
|||||||
}, [cosmosAddress, pubKey, moniker]);
|
}, [cosmosAddress, pubKey, moniker]);
|
||||||
|
|
||||||
const msgCreateValidatorEncodeObject: EncodeObject = {
|
const msgCreateValidatorEncodeObject: EncodeObject = {
|
||||||
typeUrl: '/cosmos.staking.v1beta1.MsgCreateValidator',
|
typeUrl: "/cosmos.staking.v1beta1.MsgCreateValidator",
|
||||||
value: MsgCreateValidator.toJSON(msgCreateValidator),
|
value: MsgCreateValidator.toJSON(msgCreateValidator),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -108,10 +122,15 @@ const Validator = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
enqueueSnackbar("View and sign the message from your Laconic Wallet", { variant: "info" });
|
enqueueSnackbar("View and sign the message from your Laconic Wallet", {
|
||||||
|
variant: "info",
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const params = { transactionMessage: msgCreateValidatorEncodeObject, signer: cosmosAddress };
|
const params = {
|
||||||
|
transactionMessage: msgCreateValidatorEncodeObject,
|
||||||
|
signer: cosmosAddress,
|
||||||
|
};
|
||||||
const response = await signClient!.request<{ code: number }>({
|
const response = await signClient!.request<{ code: number }>({
|
||||||
topic: session!.topic,
|
topic: session!.topic,
|
||||||
chainId: `cosmos:${process.env.REACT_APP_LACONICD_CHAIN_ID}`,
|
chainId: `cosmos:${process.env.REACT_APP_LACONICD_CHAIN_ID}`,
|
||||||
@ -124,7 +143,9 @@ const Validator = () => {
|
|||||||
if (response.code !== 0) {
|
if (response.code !== 0) {
|
||||||
throw new Error("Transaction not sent");
|
throw new Error("Transaction not sent");
|
||||||
} else {
|
} else {
|
||||||
navigate("/validator-success", { state: { validatorAddress: msgCreateValidator.validatorAddress, } });
|
navigate("/validator-success", {
|
||||||
|
state: { validatorAddress: msgCreateValidator.validatorAddress },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error sending transaction", error);
|
console.error("Error sending transaction", error);
|
||||||
@ -134,16 +155,15 @@ const Validator = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const replacer = (key: string, value: any): any => {
|
const replacer = (_key: string, value: any): any => {
|
||||||
if (value instanceof Uint8Array) {
|
if (value instanceof Uint8Array) {
|
||||||
return Buffer.from(value).toString('hex');
|
return Buffer.from(value).toString("hex");
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", marginTop: 6, gap: 1 }}>
|
<Layout title="Create a validator">
|
||||||
<Typography variant="h5">Create a validator</Typography>
|
|
||||||
<Typography variant="body1">Select Laconic account:</Typography>
|
<Typography variant="body1">Select Laconic account:</Typography>
|
||||||
<Select
|
<Select
|
||||||
sx={{ marginBottom: 2 }}
|
sx={{ marginBottom: 2 }}
|
||||||
@ -167,23 +187,14 @@ const Validator = () => {
|
|||||||
<Typography>No participant found</Typography>
|
<Typography>No participant found</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box
|
{participant && (
|
||||||
sx={{
|
<CodeBlock>
|
||||||
backgroundColor: participant ? "lightgray" : "white",
|
Laconic Address: {participant.cosmosAddress} <br />
|
||||||
padding: 3,
|
Nitro Address: {participant.nitroAddress} <br />
|
||||||
wordWrap: "break-word",
|
Role: {participant.role} <br />
|
||||||
marginBottom: 3,
|
KYC ID: {participant.kycId} <br />
|
||||||
}}
|
</CodeBlock>
|
||||||
>
|
)}
|
||||||
{participant && (
|
|
||||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
|
|
||||||
Laconic Address: {participant.cosmosAddress} <br />
|
|
||||||
Nitro Address: {participant.nitroAddress} <br />
|
|
||||||
Role: {participant.role} <br />
|
|
||||||
KYC ID: {participant.kycId} <br />
|
|
||||||
</pre>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{participant?.role === "validator" && (
|
{participant?.role === "validator" && (
|
||||||
<>
|
<>
|
||||||
@ -200,12 +211,15 @@ const Validator = () => {
|
|||||||
setMoniker(e.target.value);
|
setMoniker(e.target.value);
|
||||||
}}
|
}}
|
||||||
error={!isMonikerValid && isError}
|
error={!isMonikerValid && isError}
|
||||||
helperText={!isMonikerValid && isError ? "Moniker is required" : ""}
|
helperText={
|
||||||
|
!isMonikerValid && isError ? "Moniker is required" : ""
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Typography sx={{ marginTop: 3}}>
|
<Typography sx={{ marginTop: 3 }}>
|
||||||
Fetch your validator public key using the following command (refer
|
Fetch your validator public key using the following command
|
||||||
|
(refer
|
||||||
<Link
|
<Link
|
||||||
href="https://git.vdb.to/cerc-io/testnet-laconicd-stack/src/branch/main/testnet-onboarding-validator.md#join-as-testnet-validator"
|
href="https://git.vdb.to/cerc-io/testnet-laconicd-stack/src/branch/main/testnet-onboarding-validator.md#join-as-testnet-validator"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -213,14 +227,12 @@ const Validator = () => {
|
|||||||
>
|
>
|
||||||
this guide
|
this guide
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Box sx={{ backgroundColor: "lightgray", padding: 3, wordWrap: "break-word" }}>
|
<CodeBlock>
|
||||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
|
{`laconic-so deployment --dir testnet-laconicd-deployment exec laconicd "laconicd cometbft show-validator" | jq -r .key`}
|
||||||
{`laconic-so deployment --dir testnet-laconicd-deployment exec laconicd "laconicd cometbft show-validator" | jq -r .key`}
|
</CodeBlock>
|
||||||
</pre>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box sx={{ maxWidth: "600px" }}>
|
<Box sx={{ maxWidth: "600px" }}>
|
||||||
<TextField
|
<TextField
|
||||||
@ -235,15 +247,17 @@ const Validator = () => {
|
|||||||
setPubKey(e.target.value);
|
setPubKey(e.target.value);
|
||||||
}}
|
}}
|
||||||
error={!isPubKeyValid && isError}
|
error={!isPubKeyValid && isError}
|
||||||
helperText={!isPubKeyValid && isError ? "Public key must be 44 characters" : ""}
|
helperText={
|
||||||
|
!isPubKeyValid && isError
|
||||||
|
? "Public key must be 44 characters"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Typography>Send transaction to chain</Typography>
|
<Typography>Send transaction to chain</Typography>
|
||||||
<Box sx={{ backgroundColor: "lightgray", padding: 3, wordWrap: "break-word" }}>
|
<CodeBlock>
|
||||||
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
|
{JSON.stringify(msgCreateValidator, replacer, 2)}
|
||||||
{JSON.stringify(msgCreateValidator, replacer, 2)}
|
</CodeBlock>
|
||||||
</pre>
|
|
||||||
</Box>
|
|
||||||
<Box marginTop={1} marginBottom={1}>
|
<Box marginTop={1} marginBottom={1}>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
variant="contained"
|
variant="contained"
|
||||||
@ -258,7 +272,7 @@ const Validator = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from "react-router-dom";
|
||||||
|
|
||||||
import { Box, Link, Typography } from '@mui/material';
|
import { Link, Typography } from "@mui/material";
|
||||||
|
import { Layout } from "../layout/Layout";
|
||||||
|
import { CodeBlock } from "../components/CodeBlock";
|
||||||
|
|
||||||
const ValidatorSuccess = () => {
|
const ValidatorSuccess = () => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { validatorAddress } = location.state as {
|
const { validatorAddress } = location.state as {
|
||||||
validatorAddress?: string
|
validatorAddress?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Layout title="Validator created successfully">
|
||||||
sx={{
|
<Typography sx={{ marginTop: 3 }}>
|
||||||
display: "flex",
|
You can view your validator details using the following command
|
||||||
flexDirection: "column",
|
(Refer
|
||||||
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
|
<Link
|
||||||
href="https://git.vdb.to/cerc-io/testnet-laconicd-stack/src/branch/main/testnet-onboarding-validator.md#join-as-testnet-validator"
|
href="https://git.vdb.to/cerc-io/testnet-laconicd-stack/src/branch/main/testnet-onboarding-validator.md#join-as-testnet-validator"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -28,14 +23,12 @@ const ValidatorSuccess = () => {
|
|||||||
>
|
>
|
||||||
this guide
|
this guide
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ backgroundColor: "lightgray", padding: 2, wordWrap: "break-word", marginTop: 2, fontSize: 14}}>
|
<CodeBlock>
|
||||||
<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}")'`}
|
||||||
{`laconic-so deployment --dir testnet-laconicd-deployment exec laconicd "laconicd query staking validators --output json" | jq '.validators[] | select(.operator_address == "${validatorAddress}")'`}
|
</CodeBlock>
|
||||||
</pre>
|
</Layout>
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user