Files
Fuma Nama d9a51ed298 refactor(fhe): unify API - reveal() replaces getDecryptResult()
- Update to unified FHE decryption API
- Update dependencies to @luxfi/contracts
- Note: TypeScript types pending for @luxfhe/sdk/web
2025-12-31 21:12:09 -08:00

151 lines
5.7 KiB
TypeScript

"use client";
import { useCallback, useState } from "react";
import { zeroAddress } from "viem";
import { ShieldCheckIcon } from "@heroicons/react/24/solid";
import { useFHEjsAccount, useFHEjsCreatePermit, useFHEjsModalStore } from "~~/app/useFHE";
import { AddressInput } from "~~/components/scaffold-eth";
type ExpirationOption = "1 day" | "1 week" | "1 month";
const shareablePermits = false;
export const FHEPermitModal = () => {
const { generatePermitModalOpen, generatePermitModalCallback, setGeneratePermitModalOpen } = useFHEjsModalStore();
const createPermit = useFHEjsCreatePermit();
const account = useFHEjsAccount();
const [expiration, setExpiration] = useState<ExpirationOption>("1 week");
const [recipient, setRecipient] = useState<string>("");
const [name, setName] = useState<string>("");
const [showRecipientInput, setShowRecipientInput] = useState(false);
const handleClose = useCallback(() => {
setGeneratePermitModalOpen(false);
// Reset state when closing
setExpiration("1 week");
setRecipient("");
setName("");
setShowRecipientInput(false);
}, [setGeneratePermitModalOpen]);
const handleGeneratePermit = useCallback(async () => {
const type = recipient ? "sharing" : "self";
const permitName = name.length > 0 ? name.slice(0, 24) : undefined;
const expirationDate = new Date();
expirationDate.setDate(expirationDate.getDate() + (expiration === "1 day" ? 1 : expiration === "1 week" ? 7 : 30));
const recipientAddress = recipient ? recipient : zeroAddress;
const result = await createPermit({
type,
name: permitName,
issuer: account,
expiration: Math.round(expirationDate.getTime() / 1000),
recipient: recipientAddress,
});
if (result?.success) {
handleClose();
// If there was a callback provided when opening the modal, execute it
if (generatePermitModalCallback) {
generatePermitModalCallback();
}
}
}, [createPermit, generatePermitModalCallback, handleClose, account, recipient, expiration, name]);
return (
<dialog className={`modal ${generatePermitModalOpen ? "modal-open" : ""}`}>
<div className="modal-box relative">
<button className="btn btn-ghost btn-sm btn-circle absolute right-3 top-3" onClick={handleClose}>
✕
</button>
<div className="flex items-center justify-center mb-4">
<ShieldCheckIcon className="h-12 w-12 text-primary" />
</div>
<h2 className="text-2xl font-bold text-center mb-4">Generate CoFHE Permit</h2>
<p className="text-center mb-6">
A permit is required to authenticate your identity and grant access to your encrypted data.
</p>
<p className="text-center mb-6">
Generating a permit will open your wallet to sign a message (EIP712) which verifies your ownership of the
connected wallet.
</p>
<div className="flex flex-col gap-4 mb-6">
{/* Name */}
<div className="flex flex-col gap-2">
<div className="flex flex-row items-center justify-between gap-2">
<label className="text-sm font-semibold">
Name <span className="font-normal">(optional - used for display purposes)</span>
</label>
<span className="text-xs text-gray-500">{name.length}/24</span>
</div>
<input
type="text"
placeholder="Enter permit name"
className="input input-bordered w-full"
value={name}
onChange={e => setName(e.target.value.slice(0, 24))}
maxLength={24}
/>
</div>
{/* Expiration */}
<div className="flex flex-row items-center justify-between gap-2">
<label className="text-sm font-semibold">Expiration</label>
<div className="flex gap-2">
{(["1 day", "1 week", "1 month"] as const).map(option => (
<div
key={option}
className={`btn btn-sm ${expiration === option ? "btn-fhe" : "btn-ghost"}`}
onClick={() => setExpiration(option)}
>
{option}
</div>
))}
</div>
</div>
{/* Recipient */}
{shareablePermits && (
<div className="flex flex-col gap-2">
<div className="flex flex-row items-center justify-between gap-2">
<label className="text-sm font-semibold">Recipient</label>
<div className="flex gap-2">
<div
className={`btn btn-sm ${!showRecipientInput ? "btn-fhe" : "btn-ghost"}`}
onClick={() => setShowRecipientInput(false)}
>
None
</div>
<div
className={`btn btn-sm ${showRecipientInput ? "btn-fhe" : "btn-ghost"}`}
onClick={() => setShowRecipientInput(true)}
>
Enter address
</div>
</div>
</div>
{showRecipientInput && (
<AddressInput value={recipient} onChange={setRecipient} placeholder="Enter recipient address" />
)}
</div>
)}
</div>
<div className="flex justify-end gap-4">
<button className="btn btn-ghost" onClick={handleClose}>
Cancel
</button>
<button className="btn btn-fhe" onClick={handleGeneratePermit}>
Generate Permit
</button>
</div>
</div>
<div className="modal-backdrop" onClick={handleClose}>
<button>close</button>
</div>
</dialog>
);
};