SignMessage.tsx raw

   1  import { CopyIcon } from "lucide-react";
   2  import React from "react";
   3  import { toast } from "sonner";
   4  import AppHeader from "src/components/AppHeader";
   5  import { Button } from "src/components/ui/button";
   6  import {
   7    Card,
   8    CardContent,
   9    CardDescription,
  10    CardHeader,
  11    CardTitle,
  12  } from "src/components/ui/card";
  13  import { LoadingButton } from "src/components/ui/custom/loading-button";
  14  import { Input } from "src/components/ui/input";
  15  import { Label } from "src/components/ui/label";
  16  
  17  import { copyToClipboard } from "src/lib/clipboard";
  18  import { SignMessageResponse } from "src/types";
  19  import { request } from "src/utils/request";
  20  
  21  export default function SignMessage() {
  22    const [isLoading, setLoading] = React.useState(false);
  23    const [message, setMessage] = React.useState("");
  24    const [signature, setSignature] = React.useState("");
  25    const [signatureMessage, setSignatureMessage] = React.useState("");
  26  
  27    const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
  28      event.preventDefault();
  29  
  30      try {
  31        setLoading(true);
  32        const signMessageResponse = await request<SignMessageResponse>(
  33          "/api/wallet/sign-message",
  34          {
  35            method: "POST",
  36            headers: {
  37              "Content-Type": "application/json",
  38            },
  39            body: JSON.stringify({ message: message.trim() }),
  40          }
  41        );
  42        setSignatureMessage(message);
  43        setMessage("");
  44        if (signMessageResponse) {
  45          setSignature(signMessageResponse.signature);
  46          toast("Successfully signed message");
  47        }
  48      } catch (e) {
  49        toast.error("Failed to sign message", {
  50          description: "" + e,
  51        });
  52        console.error(e);
  53      } finally {
  54        setLoading(false);
  55      }
  56    };
  57  
  58    return (
  59      <div className="grid gap-5">
  60        <AppHeader
  61          pageTitle="Sign Message"
  62          title="Sign Message"
  63          description="Manually sign a message with your node's key (e.g. to proof ownership of your node)"
  64        />
  65        <div className="max-w-lg">
  66          <form onSubmit={handleSubmit} className="grid gap-5">
  67            <div className="grid gap-2">
  68              <Label htmlFor="message">Message</Label>
  69              <Input
  70                id="message"
  71                type="text"
  72                value={message}
  73                placeholder=""
  74                onChange={(e) => {
  75                  setMessage(e.target.value);
  76                  setSignature("");
  77                }}
  78              />
  79            </div>
  80            <div>
  81              <LoadingButton
  82                loading={isLoading}
  83                type="submit"
  84                disabled={!message}
  85              >
  86                Sign
  87              </LoadingButton>
  88            </div>
  89            {signature && (
  90              <Card>
  91                <CardHeader>
  92                  <CardTitle>Signed Message</CardTitle>
  93                  <CardDescription>{signatureMessage}</CardDescription>
  94                </CardHeader>
  95                <CardContent>
  96                  <div className="flex flex-row items-center gap-2">
  97                    <Input
  98                      type="text"
  99                      value={signature}
 100                      className="flex-1"
 101                      readOnly
 102                    />
 103                    <Button
 104                      type="button"
 105                      variant="secondary"
 106                      size="icon"
 107                      onClick={() => {
 108                        copyToClipboard(signature);
 109                      }}
 110                    >
 111                      <CopyIcon className="size-4" />
 112                    </Button>
 113                  </div>
 114                </CardContent>
 115              </Card>
 116            )}
 117          </form>
 118        </div>
 119      </div>
 120    );
 121  }
 122