testnet-onboarding-app/src/pages/SignWithCosmos.tsx

223 lines
6.4 KiB
TypeScript

import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { enqueueSnackbar } from "notistack";
import { Box, Divider, Typography } from "@mui/material";
import LoadingButton from "@mui/lab/LoadingButton/LoadingButton";
import {
MsgOnboardParticipantEncodeObject,
typeUrlMsgOnboardParticipant,
} from "@cerc-io/registry-sdk";
import { StargateClient } from "@cosmjs/stargate";
import { useWalletConnectContext } from "../context/WalletConnectContext";
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 [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 {
message?: {
msg: string;
address: string;
};
cosmosAddress?: string;
receivedEthSig?: string;
};
const ethAddress = innerMessage!.address;
const subscriberIdHash = localStorage.getItem(HASHED_SUBSCRIBER_ID_KEY);
const createCosmosClient = useCallback(async (endpoint: string) => {
return await StargateClient.connect(endpoint);
}, []);
const onboardParticipantMsg: MsgOnboardParticipantEncodeObject =
useMemo(() => {
return {
typeUrl: typeUrlMsgOnboardParticipant,
value: {
participant: cosmosAddress!,
ethPayload: innerMessage,
ethSignature: ethSignature!,
kycId: subscriberIdHash!,
role,
},
};
}, [cosmosAddress, innerMessage, ethSignature, subscriberIdHash, role]);
const handleTokenRequest = async () => {
try {
setIsRequesting(true);
const response = await fetch(
`${process.env.REACT_APP_FAUCET_ENDPOINT!}/faucet`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
address: cosmosAddress,
}),
},
);
if (response.ok) {
enqueueSnackbar("Tokens sent successfully", { variant: "success" });
} else {
const errorResponse = await response.json();
if (response.status === 429) {
enqueueSnackbar(`${response.statusText} : ${errorResponse.error}`, {
variant: "error",
});
} else {
throw new Error(errorResponse.error);
}
}
getBalances();
} catch (error) {
console.error(error);
enqueueSnackbar("Error getting tokens from faucet", { variant: "error" });
} finally {
setIsRequesting(false);
}
};
const sendTransaction = async (
transactionMessage: MsgOnboardParticipantEncodeObject,
) => {
if (!ethAddress) {
enqueueSnackbar("Set nitro address");
return;
}
try {
setIsLoading(true);
enqueueSnackbar("View and sign the message from your Laconic Wallet", {
variant: "info",
});
const params = { transactionMessage, signer: cosmosAddress };
const responseFromWallet = await signClient!.request<{
code: number;
}>({
topic: session!.topic,
chainId: `cosmos:${process.env.REACT_APP_LACONICD_CHAIN_ID}`,
request: {
method: "cosmos_sendTransaction",
params,
},
});
if (responseFromWallet.code !== 0) {
enqueueSnackbar("Transaction not sent", { variant: "error" });
} else {
navigate("/onboarding-success", {
state: {
cosmosAddress,
},
});
}
} catch (error) {
console.error(error);
enqueueSnackbar("Error in sending transaction", { variant: "error" });
} finally {
setIsLoading(false);
}
};
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!,
);
setBalance(balance.amount);
} catch (error) {
console.error("Error fetching balance:", error);
throw error;
}
}, [cosmosAddress, createCosmosClient]);
useEffect(() => {
getBalances();
}, [getBalances]);
return (
<>
{!isTncAccepted && (
<Layout
title="Please accept the terms and conditions to continue"
noBackButton
>
<SelectRoleCard
handleAccept={() => setIsTncAccepted(true)}
handleRoleChange={setRole}
/>
</Layout>
)}
<Layout title="Send transaction to chain" noBackButton>
<Typography>Laconic Account:</Typography>
<Box sx={{ backgroundColor: "#29292E", p: 2, borderRadius: 1, mb: 2 }}>
<Typography variant="body1">Address: {cosmosAddress}</Typography>
<Typography variant="body1">
Balance: {balance} {process.env.REACT_APP_LACONICD_DENOM}
</Typography>
</Box>
<LoadingButton
variant="contained"
onClick={handleTokenRequest}
disabled={isTncAccepted ? isRequesting : !isTncAccepted}
loading={isRequesting}
>
Request tokens from Faucet
</LoadingButton>
<Divider flexItem sx={{ my: 2 }} />
<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>
</>
);
};
export default SignWithCosmos;