Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Message Signing tools #439

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,13 @@ export default function Header(props: Readonly<Props>) {
<hr />
</Col>
</Row>
{printMobileRow("Sign Message", "/tools/sign-message")}
{printMobileRow("Verify Signature", "/tools/verify-signature")}
<Row>
<Col xs={{ span: 6, offset: 3 }}>
<hr />
</Col>
</Row>
{printMobileRow("Meme Accounting", "/meme-accounting")}
{printMobileRow("Meme Gas", "/meme-gas")}
<Row>
Expand Down Expand Up @@ -842,6 +849,19 @@ export default function Header(props: Readonly<Props>) {
}}
/>
<NavDropdown.Divider />
<HeaderDesktopLink
link={{
name: "Sign Message",
path: "/tools/sign-message",
}}
/>
<HeaderDesktopLink
link={{
name: "Verify Signature",
path: "/tools/verify-signature",
}}
/>
<NavDropdown.Divider />
<HeaderDesktopLink
link={{
name: "Meme Accounting",
Expand Down
1 change: 1 addition & 0 deletions components/messageSignatures/MessageSignatures.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "../../styles/variables.scss";
144 changes: 144 additions & 0 deletions components/messageSignatures/SignMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import styles from "./MessageSignatures.module.scss";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useEffect, useState } from "react";
import { Container, Row, Col, Form } from "react-bootstrap";
import { useAccount, useSignMessage } from "wagmi";
import {
faCopy,
faRotate,
faSignature,
} from "@fortawesome/free-solid-svg-icons";
import HeaderUserConnect from "../header/user/HeaderUserConnect";

export default function SignMessage() {
const account = useAccount();

const sign = useSignMessage();
const [message, setMessage] = useState<string>("");
const [signedMessage, setSignedMessage] = useState<string>("");

useEffect(() => {
if (sign.data) {
setSignedMessage(sign.data);
}
}, [sign.data]);

const onSign = async () => {
sign.signMessage({ message });
};

const onCopy = () => {
navigator.clipboard.writeText(signedMessage);
};

const onReset = () => {
setMessage("");
setSignedMessage("");
sign.reset();
};

if (!account.isConnected) {
return (
<Container className="no-padding pt-4 pb-4">
<Row>
<Col
xs={{ span: 12 }}
sm={{ span: 12 }}
md={{ span: 10, offset: 1 }}
lg={{ span: 8, offset: 2 }}
className="d-flex justify-content-center">
<HeaderUserConnect />
</Col>
</Row>
</Container>
);
}

return (
<Container className="no-padding pt-2 pb-4">
<Row>
<Col
xs={{ span: 12 }}
sm={{ span: 12 }}
md={{ span: 10, offset: 1 }}
lg={{ span: 8, offset: 2 }}>
<Form.Group className="pb-3">
<Form.Label>Signing with</Form.Label>
<Form.Control disabled value={account.address} />
</Form.Group>
<Form.Group>
<Form.Label>Message</Form.Label>
<Form.Control
as="textarea"
rows={3}
value={message}
disabled={!!signedMessage}
placeholder="Type your message here"
className={styles.formInput}
onChange={(e) => setMessage(e.target.value)}
/>
</Form.Group>
</Col>
</Row>
<Row className="pt-3">
<Col
xs={{ span: 12 }}
sm={{ span: 12 }}
md={{ span: 10, offset: 1 }}
lg={{ span: 8, offset: 2 }}>
<button
onClick={onSign}
className="btn btn-block btn-lg btn-primary"
disabled={!!signedMessage}>
Sign
<FontAwesomeIcon icon={faSignature} height={16} className="ms-2" />
</button>
</Col>
</Row>
{signedMessage && (
<>
<Row className="pt-5">
<Col
xs={{ span: 12 }}
sm={{ span: 12 }}
md={{ span: 10, offset: 1 }}
lg={{ span: 8, offset: 2 }}>
<Form.Group>
<Form.Label>Signed Message</Form.Label>
<Form.Control
as="textarea"
rows={3}
value={signedMessage}
disabled
/>
</Form.Group>
</Col>
</Row>
<Row className="pt-3">
<Col
xs={{ span: 12 }}
sm={{ span: 12 }}
md={{ span: 10, offset: 1 }}
lg={{ span: 8, offset: 2 }}
className="d-flex align-items-center justify-content-center gap-2">
<button
onClick={onCopy}
className="btn btn-lg btn-white"
disabled={!signedMessage}>
Copy
<FontAwesomeIcon icon={faCopy} height={16} className="ms-2" />
</button>
<button
onClick={onReset}
className="btn btn-lg btn-white"
disabled={!signedMessage}>
Reset
<FontAwesomeIcon icon={faRotate} height={16} className="ms-2" />
</button>
</Col>
</Row>
</>
)}
</Container>
);
}
181 changes: 181 additions & 0 deletions components/messageSignatures/VerifySignature.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import styles from "./MessageSignatures.module.scss";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useEffect, useState } from "react";
import { Container, Row, Col, Form } from "react-bootstrap";
import { useVerifyMessage } from "wagmi";
import {
faCheckCircle,
faRotate,
faShieldAlt,
faXmarkCircle,
} from "@fortawesome/free-solid-svg-icons";

export default function SignMessage() {
const [verifyEnabled, setVerifyEnabled] = useState<boolean>(false);
const [signer, setSigner] = useState<string>("");
const [message, setMessage] = useState<string>("");
const [signature, setSignature] = useState<string>("");

const [verifyResult, setVerifyResult] = useState<boolean | undefined>(
undefined
);

const [error, setError] = useState<string | undefined>(undefined);

const verify = useVerifyMessage({
address: signer as `0x${string}`,
message: message,
signature: signature as `0x${string}`,
query: {
enabled: verifyEnabled,
},
});

useEffect(() => {
if (verify.data !== undefined) {
setVerifyResult(verify.data);
}
}, [verify.data]);

useEffect(() => {
if (verify.error) {
setError(verify.error.message);
setVerifyEnabled(false);
}
}, [verify.error]);

const onVerify = async () => {
setVerifyEnabled(true);
setError(undefined);
};

const onReset = () => {
setSigner("");
setMessage("");
setSignature("");
setVerifyEnabled(false);
setVerifyResult(undefined);
};

return (
<Container className="no-padding pt-2 pb-4">
<Row>
<Col
xs={{ span: 12 }}
sm={{ span: 12 }}
md={{ span: 10, offset: 1 }}
lg={{ span: 8, offset: 2 }}
className="pt-1 pb-1">
<Form.Group className="pb-3">
<Form.Label>Address</Form.Label>
<Form.Control
disabled={verifyResult !== undefined}
placeholder="0x..."
type="text"
value={signer}
onChange={(e) => setSigner(e.target.value)}
/>
</Form.Group>
<Form.Group className="pb-3">
<Form.Label>Message</Form.Label>
<Form.Control
disabled={verifyResult !== undefined}
as="textarea"
rows={3}
value={message}
placeholder="Message..."
className={styles.formInput}
onChange={(e) => setMessage(e.target.value)}
/>
</Form.Group>
<Form.Group>
<Form.Label>Signature</Form.Label>
<Form.Control
disabled={verifyResult !== undefined}
as="textarea"
rows={3}
value={signature}
placeholder="Signature..."
className={styles.formInput}
onChange={(e) => setSignature(e.target.value)}
/>
</Form.Group>
</Col>
</Row>
<Row className="pt-3">
<Col
xs={{ span: 12 }}
sm={{ span: 12 }}
md={{ span: 10, offset: 1 }}
lg={{ span: 8, offset: 2 }}
className="d-flex align-items-end gap-2 pt-1 pb-1">
<button
onClick={onVerify}
className="btn btn-block btn-lg btn-primary d-flex align-items-center justify-content-center"
disabled={
verify.isFetching ||
verifyEnabled ||
!signer ||
!message ||
!signature
}>
{verify.isFetching ? "Verifying..." : "Verify"}
<FontAwesomeIcon icon={faShieldAlt} height={16} className="ms-2" />
</button>
</Col>
</Row>
{error && (
<Row className="pt-3">
<Col
xs={{ span: 12 }}
sm={{ span: 12 }}
md={{ span: 10, offset: 1 }}
lg={{ span: 8, offset: 2 }}>
Something went wrong: <br />
{error}
</Col>
</Row>
)}
{verifyResult !== undefined && (
<>
<Row className="pt-5">
<Col
xs={{ span: 12 }}
sm={{ span: 12 }}
md={{ span: 10, offset: 1 }}
lg={{ span: 8, offset: 2 }}
className="d-flex align-items-center justify-content-center gap-2">
{verifyResult ? (
<span className="text-success d-flex align-items-center gap-2 font-bolder font-larger">
<FontAwesomeIcon icon={faCheckCircle} height={20} />
Signature is valid
</span>
) : (
<span className="text-danger d-flex align-items-center gap-2 font-bolder font-larger">
<FontAwesomeIcon icon={faXmarkCircle} height={20} />
Signature is invalid
</span>
)}
</Col>
</Row>
<Row className="pt-3">
<Col
xs={{ span: 12 }}
sm={{ span: 12 }}
md={{ span: 10, offset: 1 }}
lg={{ span: 8, offset: 2 }}
className="d-flex align-items-center justify-content-center gap-2">
<button
onClick={onReset}
className="btn btn-lg btn-white"
disabled={!verifyEnabled && !verify.isFetching}>
Reset
<FontAwesomeIcon icon={faRotate} height={14} className="ms-2" />
</button>
</Col>
</Row>
</>
)}
</Container>
);
}
Loading