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