diff --git a/.env.example b/.env.example index 549c3f7..7bd4da4 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ # Retrieve your API Key and Policy ID from the smart wallets dashboard default configuration or create new keys: https://dashboard.alchemy.com/services/smart-wallets/configuration NEXT_PUBLIC_ALCHEMY_API_KEY=YOUR_APP_API_KEY # Get your app API key: https://dashboard.alchemy.com/apps NEXT_PUBLIC_ALCHEMY_POLICY_ID=YOUR_SPONSORSHIP_POLICY_ID # Get your gas sponsorship policy ID: https://dashboard.alchemy.com/services/gas-manager/configuration +NEXT_PUBLIC_CHAIN_ID=421614 # Arbitrum Sepolia as default # NOTE: make sure to set up a smart wallet configuration for your app to enable login \ No newline at end of file diff --git a/README.md b/README.md index 7b2661e..018c431 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,15 @@ Use this template to get started with **embedded smart wallets** using [Alchemy - Email, passkey & social login using pre‑built UI components - Flexible, secure, and cheap smart accounts - Gasless transactions powered by ERC-4337 Account Abstraction -- One‑click NFT mint on Arbitrum Sepolia (no ETH required) +- One‑click NFT mint (no ETH required) - Server‑side rendering ready – session persisted with cookies - TailwindCSS + shadcn/ui components, React Query, TypeScript +![Smart Wallet Quickstart](https://github.com/user-attachments/assets/2903fb78-e632-4aaa-befd-5775c60e1ca2) + ## 📍 Network & Demo Contract -This quickstart is configured to run on **Arbitrum Sepolia** testnet. A free demo NFT contract has been deployed specifically for this quickstart, allowing you to mint NFTs without any setup or deployment steps. The contract is pre-configured and ready to use out of the box. +This quickstart is configured to run on **Arbitrum Sepolia** testnet, by default. A free demo NFT contract has been deployed specifically for this quickstart, allowing you to mint NFTs without any setup or deployment steps. The contract is pre-configured and ready to use out of the box. ## 🚀 Quick start @@ -73,7 +75,7 @@ tailwind.config.ts ## 🏗️ How it works -1. `config.ts` initializes Account Kit with your API key, Base Sepolia chain, and Gas Sponsorship policy. +1. `config.ts` initializes Account Kit with your API key, chain, and Gas Sponsorship policy. 2. `Providers` wraps the app with `AlchemyAccountProvider` & React Query. 3. `LoginCard` opens the authentication modal (`useAuthModal`). 4. After login, `useSmartAccountClient` exposes the smart wallet. diff --git a/app/components/nft-mint-card.tsx b/app/components/nft-mint-card.tsx index 06bb9cc..b24bb0e 100644 --- a/app/components/nft-mint-card.tsx +++ b/app/components/nft-mint-card.tsx @@ -1,5 +1,11 @@ -import { useState } from "react"; -import { ExternalLink, Loader2, PlusCircle } from "lucide-react"; +import { useState, useEffect } from "react"; +import { + ExternalLink, + Loader2, + PlusCircle, + ImageIcon, + CheckCircle, +} from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, @@ -15,15 +21,17 @@ import Link from "next/link"; import { useReadNFTData } from "@/app/hooks/useReadNFTData"; import { useMint } from "@/app/hooks/useMintNFT"; import { useSmartAccountClient } from "@account-kit/react"; -import { NFT_CONTRACT_ADDRESS } from "@/lib/constants"; +import { useNftContractAddress } from "@/app/hooks/useNftContractAddress"; export default function NftMintCard() { const [isImageLoading, setIsImageLoading] = useState(true); + const [showSuccess, setShowSuccess] = useState(true); + const nftContractAddress = useNftContractAddress(); const { client } = useSmartAccountClient({}); const { uri, count, isLoadingCount, refetchCount } = useReadNFTData({ - contractAddress: NFT_CONTRACT_ADDRESS, + contractAddress: nftContractAddress, ownerAddress: client?.account?.address, }); @@ -33,6 +41,17 @@ export default function NftMintCard() { }, }); + // Reset success animation when new transaction appears + useEffect(() => { + if (transactionUrl) { + setShowSuccess(true); + const timer = setTimeout(() => { + setShowSuccess(false); + }, 4000); + return () => clearTimeout(timer); + } + }, [transactionUrl]); + return ( @@ -105,7 +124,7 @@ export default function NftMintCard() { )} > - Mint New NFT + Mint New NFT {!!client?.chain?.name && `on ${client.chain.name}`} - View Transaction - + {showSuccess ? ( + <> +
+ Successful mint! + + + + ) : ( + <> + View Transaction + + + )} )} diff --git a/app/components/user-info-card.tsx b/app/components/user-info-card.tsx index 438cdec..713c317 100644 --- a/app/components/user-info-card.tsx +++ b/app/components/user-info-card.tsx @@ -1,4 +1,5 @@ -import { Info, ExternalLink, Copy } from "lucide-react"; +import { useState } from "react"; +import { ExternalLink, Copy } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, @@ -8,14 +9,27 @@ import { CardTitle, } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { formatAddress } from "@/lib/utils"; import { useUser, useSmartAccountClient } from "@account-kit/react"; export default function UserInfo() { + const [isCopied, setIsCopied] = useState(false); const user = useUser(); const userEmail = user?.email ?? "anon"; const { client } = useSmartAccountClient({}); + const handleCopy = () => { + navigator.clipboard.writeText(client?.account?.address ?? ""); + setIsCopied(true); + setTimeout(() => setIsCopied(false), 2000); + }; + return ( @@ -41,25 +55,32 @@ export default function UserInfo() { {formatAddress(client?.account?.address ?? "")} - + + + + + + +

Copied!

+
+
+