Skip to content

Commit

Permalink
split otp verification and joining semaphore group
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnGuilding committed Sep 5, 2024
1 parent 4d3c79a commit ac5105d
Showing 1 changed file with 118 additions and 74 deletions.
192 changes: 118 additions & 74 deletions packages/interface/src/features/signup/components/VerifyOtp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { getSemaphoreProof } from "~/utils/semaphore";
import { useMaci } from "~/contexts/Maci";
import { useEthersSigner } from "~/hooks/useEthersSigner";
import { Spinner } from "~/components/ui/Spinner";
import { getHatsClient } from "~/utils/hatsProtocol";
import { Signer } from "ethers";

interface IVerifyOtpProps {
emailField: {
Expand All @@ -29,19 +31,22 @@ const VerifyOtp = ({ emailField }: IVerifyOtpProps): JSX.Element => {
const { updateEligibility } = useMaci();
const router = useRouter();

const [verifying, setVerifying] = useState(false);
const [loading, setLoading] = useState(false);
const [otpVerified, setOtpVerified] = useState(false);

const verifyOtp = async (otpField: OtpField) => {
if (!address) {
throw new Error("Smart account does not exist");
}

let response: Response | undefined;
try {
setVerifying(true);
if (!address) {
throw new Error("Smart account does not exist");
}
setLoading(true);

const { email: email } = emailField; // the component that can call this function only renders when the email exists
const { otp: otp } = otpField;

const response = await fetch(`${config.backendUrl}/verify-otp`, {
response = await fetch(`${config.backendUrl}/verify-otp`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand All @@ -52,88 +57,127 @@ const VerifyOtp = ({ emailField }: IVerifyOtpProps): JSX.Element => {
address,
}),
});
const json = await response.json();

if (!response.ok) {
console.log(response.status);
console.error(json);
toast.error((json.errors && json.errors[0]) ?? json.message);
} else {
toast.success("OTP verified - now joining Semaphore group");
await joinSemaphoreGroup();
router.push("/signup");
}
} catch (error: any) {
console.error(error);
toast.error("An unexpected error occured verifying the OTP");
} finally {
setVerifying(false);
setLoading(false);
toast.error(
`An unexpected error occured verifying the OTP ${error instanceof Error ? `- ${error.message}` : ""}`
);
return;
}

if (!response.ok) {
const json = await response.json();
console.log(response.status);
console.error(json);
toast.error((json.errors && json.errors[0]) ?? json.message);
} else {
toast.success("OTP verified - now joining Semaphore group");
setOtpVerified(true);
await joinSemaphoreGroup();
}
};

const joinSemaphoreGroup = async () => {
if (!smartAccount || !smartAccountClient) {
throw new Error("Smart account does not exist");
}
try {
setLoading(true);
if (!address || !smartAccount || !smartAccountClient) {
throw new Error("Smart account does not exist");
}

const semaphoreIdentity = localStorage.getItem("semaphoreIdentity");
if (!semaphoreIdentity || !signer) {
throw new Error("No Semaphore Identity or signer");
}

const hatsClient = getHatsClient();
const isWearerOfHat = await hatsClient.isWearerOfHat({
wearer: address,
hatId: semaphore.hatId,
});
if (!isWearerOfHat) {
throw new Error("Account not wearing hat");
}

const semaphoreIdentity = localStorage.getItem("semaphoreIdentity");
if (!semaphoreIdentity || !signer) {
throw new Error("No Semaphore Identity or signer");
const identityCommitment = new Identity(semaphoreIdentity).commitment;
const data = encodeAbiParameters(parseAbiParameters("uint"), [
semaphore.hatId,
]);

const { request } = await publicClient.simulateContract({
account: smartAccount,
address: semaphore.contracts.semaphore as Address,
abi: SemaphoreAbi.abi,
functionName: "gateAndAddMember",
args: [identityCommitment, data],
});
const txHash = await smartAccountClient.writeContract(request);
console.log("txHash", txHash);

// TODO: (merge-ok) come up with a better fix
await new Promise((resolve) => setTimeout(resolve, 20000));
toast.success("Joined Semaphore group");

await tryUpdateEligibility(signer, semaphoreIdentity);
} catch (error) {
toast.error(
`An unexpected error occured ${error instanceof Error ? `- ${error.message}` : ""}`
);
} finally {
setLoading(false);
}
};

const identityCommitment = new Identity(semaphoreIdentity).commitment;
const data = encodeAbiParameters(parseAbiParameters("uint"), [
semaphore.hatId,
]);

const { request } = await publicClient.simulateContract({
account: smartAccount,
address: semaphore.contracts.semaphore as Address,
abi: SemaphoreAbi.abi,
functionName: "gateAndAddMember",
args: [identityCommitment, data],
});
const txHash = await smartAccountClient.writeContract(request);
console.log("txHash", txHash);

// TODO: (merge-ok) come up with a better fix
await new Promise((resolve) => setTimeout(resolve, 20000));

const proof = await getSemaphoreProof(
signer,
new Identity(semaphoreIdentity)
);
await updateEligibility(proof, address);

toast.success("Joined Semaphore group");
const tryUpdateEligibility = async (
signer: Signer,
semaphoreIdentity: string
) => {
try {
const proof = await getSemaphoreProof(
signer,
new Identity(semaphoreIdentity)
);
await updateEligibility(proof, address);
router.push("/signup");
} catch {
throw new Error(
"Could not update eligibility but joined semaphore group. Navigate to the homepage and wait a few mins for eligibility to be updated"
);
}
};

return (
<div className="w-72 sm:w-96">
<Form schema={OtpFieldSchema} onSubmit={(otp) => verifyOtp(otp)}>
<FormSection
description="Please enter the one-time-password (OTP) you recieved in your email"
title="Enter OTP"
>
<FormControl
required
valueAsNumber
hint="Check your 'pse.dev' inbox for the OTP"
label="OTP"
name="otp"
>
<Input placeholder="1234" type="number" />
</FormControl>
<Button
suppressHydrationWarning
size="auto"
type="submit"
variant="secondary"
{!otpVerified ? (
<Form schema={OtpFieldSchema} onSubmit={(otp) => verifyOtp(otp)}>
<FormSection
description="Please enter the one-time-password (OTP) you recieved in your email"
title="Enter OTP"
>
{verifying ? <Spinner className="h-6 w-6" /> : "Verify OTP"}
</Button>
</FormSection>
</Form>
<FormControl
required
valueAsNumber
hint="Check your 'pse.dev' inbox for the OTP"
label="OTP"
name="otp"
>
<Input placeholder="1234" type="number" />
</FormControl>
<Button
suppressHydrationWarning
size="auto"
type="submit"
variant="secondary"
>
{loading ? <Spinner className="h-6 w-6" /> : "Verify OTP"}
</Button>
</FormSection>
</Form>
) : (
<Button variant="secondary" onClick={joinSemaphoreGroup}>
{loading ? <Spinner className="h-6 w-6" /> : "Join Semaphore group"}
</Button>
)}
</div>
);
};
Expand Down

0 comments on commit ac5105d

Please sign in to comment.