diff --git a/package.json b/package.json index 0a61602..17b99b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "testnet-onboarding-app", - "version": "0.1.0", + "version": "0.1.2", "private": true, "dependencies": { "@cerc-io/registry-sdk": "^0.2.5", diff --git a/public/index.html b/public/index.html index 4fbd2e7..7fbd272 100644 --- a/public/index.html +++ b/public/index.html @@ -1,21 +1,24 @@ - + - - - - - - - - - - - Testnet Onboarding App - - - - -
-
-
-
+ } + + + + + +
+
+
- - + + diff --git a/src/App.tsx b/src/App.tsx index 3bd3147..cb7cbac 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,44 +16,117 @@ import Email from "./pages/Email"; import Thanks from "./pages/Thanks"; import Validator from "./pages/Validator"; 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() { return ( -
- - - } /> - } /> - } /> - } /> - } /> - }> - } /> - } - /> - } - /> - } - /> - } - /> - } - /> - - } /> - - + + +
+ + + + } /> + } /> + } /> + } /> + } /> + }> + } + /> + } + /> + } /> + } + /> + } /> + } + /> + + } /> + + + + ); } diff --git a/src/components/CodeBlock.tsx b/src/components/CodeBlock.tsx new file mode 100644 index 0000000..6fb7f80 --- /dev/null +++ b/src/components/CodeBlock.tsx @@ -0,0 +1,25 @@ +import { Box } from "@mui/material"; +import React, { PropsWithChildren } from "react"; + +export const CodeBlock: React.FC = ({ children }) => ( + +
+      {children}
+    
+
+); diff --git a/src/components/Container.tsx b/src/components/Container.tsx new file mode 100644 index 0000000..c738e46 --- /dev/null +++ b/src/components/Container.tsx @@ -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 = {} }) => ( + + {children} + +); diff --git a/src/components/Header.tsx b/src/components/Header.tsx index c1bd679..a533ddb 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,31 +1,90 @@ -import React from 'react'; -import { Link, useLocation } from 'react-router-dom'; +import React from "react"; +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 location = useLocation(); return ( - - - - - - + + + + - Testnet Onboarding - - + + + + + + + + + + - + + Testnet Onboarding + ); }; diff --git a/src/components/SelectRoleCard.tsx b/src/components/SelectRoleCard.tsx index 4397de9..3c67c68 100644 --- a/src/components/SelectRoleCard.tsx +++ b/src/components/SelectRoleCard.tsx @@ -44,12 +44,12 @@ const SelectRoleCard = ({ handleAccept, handleRoleChange }: { handleAccept: () = } - label="Validator" + label="Validator / Service Provider" /> } - label="Participant" + label="App Publisher" /> diff --git a/src/components/TermsAndConditionsBox.tsx b/src/components/TermsAndConditionsBox.tsx index ccc3a3c..73309b0 100644 --- a/src/components/TermsAndConditionsBox.tsx +++ b/src/components/TermsAndConditionsBox.tsx @@ -1,43 +1,47 @@ -import React, { useState } from 'react'; -import { Document, Page, pdfjs } from 'react-pdf'; +import React, { useState } from "react"; +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 -pdfjs.GlobalWorkerOptions.workerSrc = process.env.PUBLIC_URL + '/pdf.worker.min.mjs'; +pdfjs.GlobalWorkerOptions.workerSrc = + process.env.PUBLIC_URL + "/pdf.worker.min.mjs"; interface TermsAndConditionsBoxProps { height: string; onLoad?: () => void; } -const TermsAndConditionsBox = ({ height, onLoad }: TermsAndConditionsBoxProps ) => { +const TermsAndConditionsBox = ({ + height, + onLoad, +}: TermsAndConditionsBoxProps) => { const [numPages, setNumPages] = useState(); function onDocumentLoadSuccess({ numPages }: { numPages: number }): void { setNumPages(numPages); - if (onLoad){ + if (onLoad) { onLoad(); - }; + } } return ( <> - + Terms and Conditions
{Array.apply(null, Array(numPages)) @@ -49,7 +53,6 @@ const TermsAndConditionsBox = ({ height, onLoad }: TermsAndConditionsBoxProps ) pageNumber={page} renderTextLayer={false} renderAnnotationLayer={false} - width={1070} /> ); })} diff --git a/src/layout/Layout.tsx b/src/layout/Layout.tsx new file mode 100644 index 0000000..9ba9a19 --- /dev/null +++ b/src/layout/Layout.tsx @@ -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 ( + + {noBackButton ? null : ( + + )} + + {title} + + {children} + + ); +}; diff --git a/src/layout/SignPageLayout.tsx b/src/layout/SignPageLayout.tsx index b985f1c..24bdfbf 100644 --- a/src/layout/SignPageLayout.tsx +++ b/src/layout/SignPageLayout.tsx @@ -1,15 +1,10 @@ import React from "react"; import { Outlet, useLocation, useNavigate } from "react-router-dom"; -import { - Toolbar, - Avatar, - Button, - Typography, - Container -} from "@mui/material"; +import { Avatar, Button, Stack, Typography } from "@mui/material"; import { useWalletConnectContext } from "../context/WalletConnectContext"; +import { Container } from "../components/Container"; const SignPageLayout = () => { const { disconnect, session } = useWalletConnectContext(); @@ -25,24 +20,19 @@ const SignPageLayout = () => { }; return ( - <> - - - - - - + + {session && ( -
+
{ }} > - Connected to: {session.peer.metadata.name}{" "} + Connected to: {session.peer.metadata.name} { />
- Session ID: {session.topic} + Session ID: {session.topic} -
+
)} - +
- + + ); }; diff --git a/src/pages/ConnectWallet.tsx b/src/pages/ConnectWallet.tsx index e904c33..13c959b 100644 --- a/src/pages/ConnectWallet.tsx +++ b/src/pages/ConnectWallet.tsx @@ -1,13 +1,13 @@ 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 { WALLET_DISCLAIMER_MSG } from "../constants"; const ConnectWallet = () => { - const { connect, session, signClient, checkPersistedState } = useWalletConnectContext(); + const { connect, session, signClient, checkPersistedState } = + useWalletConnectContext(); const navigate = useNavigate(); const location = useLocation(); @@ -28,13 +28,11 @@ const ConnectWallet = () => { if (redirectTo) { navigate(`/${redirectTo}`, { - state: location.state + state: location.state, }); - } - - else { + } else { navigate("/sign-with-nitro-key", { - state: location.state + state: location.state, }); } }, [session, navigate, redirectTo, location.state]); @@ -44,37 +42,16 @@ const ConnectWallet = () => { }; return ( - - - - Disclaimer - - - {WALLET_DISCLAIMER_MSG} - - + - diff --git a/src/pages/LandingPage.tsx b/src/pages/LandingPage.tsx index 9fbfd7a..677f53b 100644 --- a/src/pages/LandingPage.tsx +++ b/src/pages/LandingPage.tsx @@ -1,9 +1,10 @@ -import React, { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; +import React, { useState } from "react"; +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 navigate = useNavigate(); @@ -11,11 +12,11 @@ const LandingPage = () => { const [isDisabled, setIsDisabled] = useState(true); const handleAccept = () => { - navigate('/verify-email'); + navigate("/verify-email"); }; return ( - <> + { margin={10} sx={{ border: 1, - borderColor: 'grey.500', + borderColor: "grey.500", + borderRadius: 1, }} padding={5} > - Welcome to the LORO Testnet Onboarding App. The detailed instructions for completing this first step are found in the{' '} - LORO testnet repo. - 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. + Welcome to the LORO Testnet Onboarding App. The detailed instructions + for completing this first step are found in the{" "} + + LORO testnet repo + + . 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. - {setIsDisabled(false);}} /> + { + setIsDisabled(false); + }} + /> - - - + ); }; diff --git a/src/pages/OnboardingSuccess.tsx b/src/pages/OnboardingSuccess.tsx index 2c053f3..b7e307a 100644 --- a/src/pages/OnboardingSuccess.tsx +++ b/src/pages/OnboardingSuccess.tsx @@ -7,36 +7,43 @@ import { Registry } from "@cerc-io/registry-sdk"; import SumsubWebSdk from "@sumsub/websdk-react"; 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 { Participant } from "../types"; +import { CodeBlock } from "../components/CodeBlock"; -const registry = new Registry( - process.env.REACT_APP_REGISTRY_GQL_ENDPOINT! -); +const registry = new Registry(process.env.REACT_APP_REGISTRY_GQL_ENDPOINT!); const OnboardingSuccess = () => { const location = useLocation(); const { cosmosAddress } = location.state as { - cosmosAddress?: string + cosmosAddress?: string; }; const [participant, setParticipant] = useState(); - const [token, setToken] = useState(''); + const [token, setToken] = useState(""); const [loading, setLoading] = useState(true); const messageHandler: MessageHandler = (event, payload) => { - console.log('sumsubEvent:', event, payload); + console.log("sumsubEvent:", event, payload); }; useEffect(() => { const fetchParticipants = async () => { try { if (!cosmosAddress) { - enqueueSnackbar("Laconic address is not provided", { variant: "error" }); + enqueueSnackbar("Laconic address is not provided", { + variant: "error", + }); return; } - const participant: Participant = await registry.getParticipantByAddress(cosmosAddress); + const participant: Participant = + await registry.getParticipantByAddress(cosmosAddress); if (!participant) { enqueueSnackbar("Participant not found", { variant: "error" }); return; @@ -61,7 +68,7 @@ const OnboardingSuccess = () => { }; if (cosmosAddress && ENABLE_KYC) { - getToken(cosmosAddress).catch(error => { + getToken(cosmosAddress).catch((error) => { console.error(error); alert("Failed to fetch token"); }); @@ -82,41 +89,35 @@ const OnboardingSuccess = () => { Participant onboarded:
- -
-            {participant && (
-              
+ + {participant && ( +
Laconic Address: {participant.cosmosAddress}
Nitro Address: {participant.nitroAddress}
Role: {participant.role}
KYC ID: {participant.kycId}
-
-
- )} -
-
+
+
+ )} + {ENABLE_KYC ? ( KYC Status {!loading && token && cosmosAddress && ( )} - ) : '' - } + ) : ( + "" + )} Next Steps { marginTop={3} sx={{ border: 1, - borderColor: 'grey.500', + borderColor: "grey.500", }} padding={5} > - 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{' '} - webapp publishing workflow{' '} - as this is the main task you will be participating in.
+ For app publishers, await the start of the stage 1 chain, which will + be announced in various social media channels. In the meantime, + familiarize yourself with the{" "} + + webapp publishing workflow + {" "} + as this is the main task you will be participating in.
- 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{' '} - these instructions{' '} - for joining stage 1 as a validator. +
+ For validators, ensure your service provider is running and ready to + deploy webapps. Await publication of the laconicd version, genesis + file, and peer info to the LORO testnet repo, then follow{" "} + + these instructions + {" "} + for joining stage 1 as a validator/service provider.
diff --git a/src/pages/SignWithCosmos.tsx b/src/pages/SignWithCosmos.tsx index b369d5d..d7223a9 100644 --- a/src/pages/SignWithCosmos.tsx +++ b/src/pages/SignWithCosmos.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; 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 { MsgOnboardParticipantEncodeObject, @@ -11,22 +11,28 @@ import { import { StargateClient } from "@cosmjs/stargate"; 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 { Layout } from "../layout/Layout"; +import { CodeBlock } from "../components/CodeBlock"; const SignWithCosmos = () => { const { session, signClient } = useWalletConnectContext(); const location = useLocation(); const [isLoading, setIsLoading] = useState(false); - const [balance, setBalance] = useState(''); + const [balance, setBalance] = useState(""); const [isRequesting, setIsRequesting] = useState(false); const [isTncAccepted, setIsTncAccepted] = useState(false); const [role, setRole] = useState(Role.Participant); const navigate = useNavigate(); - const {message: innerMessage, cosmosAddress, receivedEthSig: ethSignature} = location.state as { + const { + message: innerMessage, + cosmosAddress, + receivedEthSig: ethSignature, + } = location.state as { message?: { msg: string; address: string; @@ -51,7 +57,7 @@ const SignWithCosmos = () => { ethPayload: innerMessage, ethSignature: ethSignature!, kycId: subscriberIdHash!, - role + role, }, }; }, [cosmosAddress, innerMessage, ethSignature, subscriberIdHash, role]); @@ -59,22 +65,27 @@ const SignWithCosmos = () => { const handleTokenRequest = async () => { try { setIsRequesting(true); - const response = await fetch(`${process.env.REACT_APP_FAUCET_ENDPOINT!}/faucet`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + const response = await fetch( + `${process.env.REACT_APP_FAUCET_ENDPOINT!}/faucet`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + address: cosmosAddress, + }), }, - body: JSON.stringify({ - address: cosmosAddress, - }), - }); + ); if (response.ok) { - enqueueSnackbar('Tokens sent successfully', { variant: "success" }); + enqueueSnackbar("Tokens sent successfully", { variant: "success" }); } else { const errorResponse = await response.json(); if (response.status === 429) { - enqueueSnackbar(`${response.statusText} : ${errorResponse.error}`, { variant: "error" }); + enqueueSnackbar(`${response.statusText} : ${errorResponse.error}`, { + variant: "error", + }); } else { throw new Error(errorResponse.error); } @@ -90,7 +101,7 @@ const SignWithCosmos = () => { }; const sendTransaction = async ( - transactionMessage: MsgOnboardParticipantEncodeObject + transactionMessage: MsgOnboardParticipantEncodeObject, ) => { if (!ethAddress) { enqueueSnackbar("Set nitro address"); @@ -100,7 +111,9 @@ const SignWithCosmos = () => { try { 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 responseFromWallet = await signClient!.request<{ @@ -118,8 +131,8 @@ const SignWithCosmos = () => { } else { navigate("/onboarding-success", { state: { - cosmosAddress - } + cosmosAddress, + }, }); } } catch (error) { @@ -132,11 +145,16 @@ const SignWithCosmos = () => { const getBalances = useCallback(async () => { try { - const cosmosClient = await createCosmosClient(process.env.REACT_APP_LACONICD_RPC_ENDPOINT!); - const balance = await cosmosClient.getBalance(cosmosAddress!, process.env.REACT_APP_LACONICD_DENOM!); + const cosmosClient = await createCosmosClient( + process.env.REACT_APP_LACONICD_RPC_ENDPOINT!, + ); + const balance = await cosmosClient.getBalance( + cosmosAddress!, + process.env.REACT_APP_LACONICD_DENOM!, + ); setBalance(balance.amount); } catch (error) { - console.error('Error fetching balance:', error); + console.error("Error fetching balance:", error); throw error; } }, [cosmosAddress, createCosmosClient]); @@ -146,69 +164,58 @@ const SignWithCosmos = () => { }, [getBalances]); return ( - - Please accept terms and conditions to continue - setIsTncAccepted(true)} handleRoleChange={setRole}/> - Send transaction to chain - Laconic Account: - - - - - Address: {cosmosAddress} - Balance: {balance} {process.env.REACT_APP_LACONICD_DENOM} - - - - Request tokens from Faucet - - - - - - - Onboarding message:
-
- -
-          {JSON.stringify(onboardParticipantMsg, null, 2)}{" "}
-        
-
- - + <> + {!isTncAccepted && ( + + setIsTncAccepted(true)} + handleRoleChange={setRole} + /> + + )} + + Laconic Account: + + Address: {cosmosAddress} + + Balance: {balance} {process.env.REACT_APP_LACONICD_DENOM} + + { - await sendTransaction(onboardParticipantMsg); - }} - loading={isLoading} - disabled={isTncAccepted ? (balance === '0') : !isTncAccepted} + onClick={handleTokenRequest} + disabled={isTncAccepted ? isRequesting : !isTncAccepted} + loading={isRequesting} > - Send transaction + Request tokens from Faucet - -
+ + Onboarding message: + + {JSON.stringify(onboardParticipantMsg, null, 2)} + + + { + await sendTransaction(onboardParticipantMsg); + }} + loading={isLoading} + disabled={isTncAccepted ? balance === "0" : !isTncAccepted} + > + Send transaction + + + + ); }; diff --git a/src/pages/SignWithNitroKey.tsx b/src/pages/SignWithNitroKey.tsx index 740090b..8388962 100644 --- a/src/pages/SignWithNitroKey.tsx +++ b/src/pages/SignWithNitroKey.tsx @@ -3,22 +3,17 @@ import { useLocation, useNavigate } from "react-router-dom"; import { enqueueSnackbar } from "notistack"; import canonicalStringify from "canonical-json"; -import { - Select, - MenuItem, - Box, - Typography, -} from "@mui/material"; -import LoadingButton from '@mui/lab/LoadingButton'; +import { Select, MenuItem, Box, Typography, Stack } from "@mui/material"; +import LoadingButton from "@mui/lab/LoadingButton"; import { utf8ToHex } from "@walletconnect/encoding"; import { useWalletConnectContext } from "../context/WalletConnectContext"; import { ENABLE_KYC, HASHED_SUBSCRIBER_ID_KEY } from "../constants"; +import { Layout } from "../layout/Layout"; +import { CodeBlock } from "../components/CodeBlock"; const SignWithNitroKey = () => { - - const { session, signClient, isSessionLoading } = - useWalletConnectContext(); + const { session, signClient, isSessionLoading } = useWalletConnectContext(); const navigate = useNavigate(); const location = useLocation(); @@ -42,14 +37,17 @@ const SignWithNitroKey = () => { const [isLoading, setIsLoading] = useState(false); - const subscriberIdHash = useMemo(()=>{ + 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" }); + enqueueSnackbar( + "Subscriber ID not found. Please verify your email and try again", + { variant: "error" }, + ); } }, [subscriberIdHash]); @@ -65,7 +63,9 @@ const SignWithNitroKey = () => { try { 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 hexMsg = utf8ToHex(jsonMessage, true); @@ -106,17 +106,9 @@ const SignWithNitroKey = () => { }; return ( -
+ {session ? ( - - Sign with Nitro key + Select Laconic account: - {(Boolean(ethAddress) && Boolean(cosmosAddress)) && ( -
{canonicalStringify(message, null, 2)} 
-
)} + {Boolean(ethAddress) && Boolean(cosmosAddress) && ( + {canonicalStringify(message, null, 2)} + )} { Sign using Nitro key -
+ ) : ( <>Loading... )} -
+ ); }; diff --git a/src/pages/Validator.tsx b/src/pages/Validator.tsx index d17b46e..5f54d69 100644 --- a/src/pages/Validator.tsx +++ b/src/pages/Validator.tsx @@ -1,25 +1,34 @@ -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 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 { + 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'; +import { useWalletConnectContext } from "../context/WalletConnectContext"; +import { Participant } from "../types"; +import { Layout } from "../layout/Layout"; +import { CodeBlock } from "../components/CodeBlock"; const Validator = () => { const { session, signClient, isSessionLoading } = useWalletConnectContext(); const navigate = useNavigate(); - const [cosmosAddress, setCosmosAddress] = useState(''); + const [cosmosAddress, setCosmosAddress] = useState(""); const [isLoading, setIsLoading] = useState(false); - const [moniker, setMoniker] = useState(''); - const [pubKey, setPubKey] = useState(''); + const [moniker, setMoniker] = useState(""); + const [pubKey, setPubKey] = useState(""); const [participant, setParticipant] = useState(null); const [isError, setIsError] = useState(false); @@ -40,10 +49,13 @@ const Validator = () => { } 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 { - const fetchedParticipant = await registry.getParticipantByAddress(cosmosAddress); + const fetchedParticipant = + await registry.getParticipantByAddress(cosmosAddress); if (fetchedParticipant) { setParticipant(fetchedParticipant); } else { @@ -65,7 +77,7 @@ const Validator = () => { const msgCreateValidator: MsgCreateValidator = useMemo(() => { const encodedPubKey = encodePubkey({ type: "tendermint/PubKeyEd25519", - value: pubKey.length === 44 ? pubKey : '', + value: pubKey.length === 44 ? pubKey : "", }); return { @@ -82,8 +94,10 @@ const Validator = () => { rate: "100000000000000000", // 0.1 }, minSelfDelegation: "1", - delegatorAddress: '', - validatorAddress: cosmosAddress && toBech32('laconicvaloper', fromBech32(cosmosAddress).data), + delegatorAddress: "", + validatorAddress: + cosmosAddress && + toBech32("laconicvaloper", fromBech32(cosmosAddress).data), pubkey: encodedPubKey, value: { amount: process.env.REACT_APP_STAKING_AMOUNT!, @@ -93,7 +107,7 @@ const Validator = () => { }, [cosmosAddress, pubKey, moniker]); const msgCreateValidatorEncodeObject: EncodeObject = { - typeUrl: '/cosmos.staking.v1beta1.MsgCreateValidator', + typeUrl: "/cosmos.staking.v1beta1.MsgCreateValidator", value: MsgCreateValidator.toJSON(msgCreateValidator), }; @@ -108,10 +122,15 @@ const Validator = () => { } 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 { - const params = { transactionMessage: msgCreateValidatorEncodeObject, signer: cosmosAddress }; + 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}`, @@ -124,7 +143,9 @@ const Validator = () => { if (response.code !== 0) { throw new Error("Transaction not sent"); } else { - navigate("/validator-success", { state: { validatorAddress: msgCreateValidator.validatorAddress, } }); + navigate("/validator-success", { + state: { validatorAddress: msgCreateValidator.validatorAddress }, + }); } } catch (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) { - return Buffer.from(value).toString('hex'); + return Buffer.from(value).toString("hex"); } return value; }; return ( - - Create a validator + Select Laconic account: