add signatures

This commit is contained in:
horsefacts 2024-11-23 23:55:52 -05:00 committed by lucas-neynar
parent 33eea440b3
commit 2bbf813e20
No known key found for this signature in database
2 changed files with 118 additions and 9 deletions

View File

@ -1,6 +1,12 @@
import { useEffect, useCallback, useState } from "react"; import { useEffect, useCallback, useState } from "react";
import sdk, { type FrameContext } from "@farcaster/frame-sdk"; import sdk, { type FrameContext } from "@farcaster/frame-sdk";
import { useAccount, useSendTransaction } from "wagmi"; import {
useAccount,
useSendTransaction,
useSignMessage,
useSignTypedData,
useWaitForTransactionReceipt,
} from "wagmi";
import { Button } from "~/components/ui/Button"; import { Button } from "~/components/ui/Button";
import { truncateAddress } from "~/lib/truncateAddress"; import { truncateAddress } from "~/lib/truncateAddress";
@ -9,9 +15,34 @@ export default function Demo() {
const [isSDKLoaded, setIsSDKLoaded] = useState(false); const [isSDKLoaded, setIsSDKLoaded] = useState(false);
const [context, setContext] = useState<FrameContext>(); const [context, setContext] = useState<FrameContext>();
const [isContextOpen, setIsContextOpen] = useState(false); const [isContextOpen, setIsContextOpen] = useState(false);
const [txHash, setTxHash] = useState<string | null>(null);
const { address, isConnected } = useAccount(); const { address, isConnected } = useAccount();
const { sendTransaction } = useSendTransaction(); const {
sendTransaction,
error: sendTxError,
isError: isSendTxError,
isPending: isSendTxPending,
} = useSendTransaction();
const {
signMessage,
error: signError,
isError: isSignError,
isPending: isSignPending,
} = useSignMessage();
const {
signTypedData,
error: signTypedError,
isError: isSignTypedError,
isPending: isSignTypedPending,
} = useSignTypedData();
const { isLoading: isConfirming, isSuccess: isConfirmed } =
useWaitForTransactionReceipt({
hash: txHash as `0x${string}`,
});
useEffect(() => { useEffect(() => {
const load = async () => { const load = async () => {
@ -33,16 +64,49 @@ export default function Demo() {
}, []); }, []);
const sendTx = useCallback(() => { const sendTx = useCallback(() => {
sendTransaction({ sendTransaction(
to: "0x4bBFD120d9f352A0BEd7a014bd67913a2007a878", {
data: "0x9846cd9efc000023c0", to: "0x4bBFD120d9f352A0BEd7a014bd67913a2007a878",
}); data: "0x9846cd9efc000023c0",
},
{
onSuccess: (hash) => {
setTxHash(hash);
},
}
);
}, [sendTransaction]); }, [sendTransaction]);
const sign = useCallback(() => {
signMessage({ message: "Hello from Frames v2!" });
}, [signMessage]);
const signTyped = useCallback(() => {
signTypedData({
domain: {
name: "Frames v2 Demo",
version: "1",
chainId: 8453,
},
types: {
Message: [{ name: "content", type: "string" }],
},
message: {
content: "Hello from Frames v2!",
},
primaryType: "Message",
});
}, [signTypedData]);
const toggleContext = useCallback(() => { const toggleContext = useCallback(() => {
setIsContextOpen((prev) => !prev); setIsContextOpen((prev) => !prev);
}, []); }, []);
const renderError = (error: Error | null) => {
if (!error) return null;
return <div className="text-red-500 text-xs mt-1">{error.message}</div>;
};
if (!isSDKLoaded) { if (!isSDKLoaded) {
return <div>Loading...</div>; return <div>Loading...</div>;
} }
@ -110,9 +174,47 @@ export default function Demo() {
{isConnected && ( {isConnected && (
<> <>
<div className="mb-4"> <div className="mb-4">
<Button onClick={sendTx} disabled={!isConnected}> <Button
onClick={sendTx}
disabled={!isConnected || isSendTxPending}
isLoading={isSendTxPending}
>
Send Transaction Send Transaction
</Button> </Button>
{isSendTxError && renderError(sendTxError)}
{txHash && (
<div className="mt-2 text-xs">
<div>Hash: {truncateAddress(txHash)}</div>
<div>
Status:{" "}
{isConfirming
? "Confirming..."
: isConfirmed
? "Confirmed!"
: "Pending"}
</div>
</div>
)}
</div>
<div className="mb-4">
<Button
onClick={sign}
disabled={!isConnected || isSignPending}
isLoading={isSignPending}
>
Sign Message
</Button>
{isSignError && renderError(signError)}
</div>
<div className="mb-4">
<Button
onClick={signTyped}
disabled={!isConnected || isSignTypedPending}
isLoading={isSignTypedPending}
>
Sign Typed Data
</Button>
{isSignTypedError && renderError(signTypedError)}
</div> </div>
</> </>
)} )}

View File

@ -1,14 +1,21 @@
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode; children: React.ReactNode;
isLoading?: boolean;
} }
export function Button({ children, className = "", ...props }: ButtonProps) { export function Button({ children, className = "", isLoading = false, ...props }: ButtonProps) {
return ( return (
<button <button
className={`w-full max-w-xs mx-auto block bg-[#7C65C1] text-white py-3 px-6 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-[#7C65C1] hover:bg-[#6952A3] ${className}`} className={`w-full max-w-xs mx-auto block bg-[#7C65C1] text-white py-3 px-6 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-[#7C65C1] hover:bg-[#6952A3] ${className}`}
{...props} {...props}
> >
{children} {isLoading ? (
<div className="flex items-center justify-center">
<div className="animate-spin h-5 w-5 border-2 border-white border-t-transparent rounded-full" />
</div>
) : (
children
)}
</button> </button>
); );
} }