This repository implements a simple on-chain Gift Card system called "Monaco". It combines a Solidity smart contract for creating, claiming, and tracking ETH-backed gift cards with a Next.js frontend for a friendly web UI.
This README explains the product, high-level architecture, how the contract works, how the frontend is structured, onboarding guidance for non-crypto users, event/draw ideas, and the custodial smart-wallet (email) onboarding model supported by this project.
- Product name: Monaco Gift Card
- Purpose: Let users create ETH-backed gift cards that others can claim by using a secret string. Useful for gifting, raffles, events, and introducing people to crypto.
- Core value: Simple UX — send someone a secret (via email, chat, QR). They paste the secret in the app and claim the ETH to their wallet.
- Create a gift card by sending ETH and a secret (string) — contract mints a gift card id and stores the hash.
- Claim a gift card by providing the matching secret — contract sends ETH to claimer and marks the card redeemed.
- Query gift card state (details, validity, user-created IDs).
- Events emitted for create, claim, and transfer to enable off-chain indexing and UI updates.
The core contract is contract/src/GiftCard.sol.
Important points gleaned from the contract:
- Gift cards have a numeric giftId, an ETH amount, creator address, claim status, claimant address, creation and redemption timestamps.
- Secrets: The contract stores a hash of the secret for verification and also stores the plaintext secret in a mapping (this is convenient for the current demo but not recommended for production, see security notes below).
- Creation:
createGiftCard(string secret)is payable. It requires msg.value > 0 and returns a generated giftId. It emitsGiftCardCreated. - Claiming:
claimGiftCard(uint256 giftId, string secret)verifies the provided secret's hash, marks redeemed, transfers the ETH to the caller, and emitsGiftCardClaimed. - Views:
getGiftCard,isGiftCardValid,getSecret(creator only in practice — currently the contract returns the secret but contracts cannot enforce off-chain restrictions), and user enumeration helpers. - Events:
GiftCardCreated,GiftCardClaimed,GiftCardTransferred(transfer API not exposed in current contract but event exists for future use).
Security note: The contract currently stores plaintext secrets in the secrets mapping. That leaks secret values on-chain (anyone can read the state), so for production you should only store a hash and never store the plaintext. Also consider gas, reentrancy protections, and safe randomness for id generation.
The frontend lives in frontend/ and is a Next.js app (see frontend/package.json). Key tech:
- Next.js 15, React 19
- Ethers.js v6 for blockchain interactions
- thirdweb present as a dependency (may be used for wallet/connect helpers)
- UI: Tailwind + MUI + Radix + custom components in
frontend/src/components
Relevant paths:
frontend/src/app— top-level pages and routingfrontend/src/abi/giftcards.abi.ts— contract ABI for interactionsfrontend/src/components— UI components (buttons, dialogs, card, confetti, etc.)
Scripts:
npm run dev— run the Next.js dev servernpm run buildandnpm run start— build and start
Run locally (from frontend/):
cd frontend
npm install
npm run dev
This product is aimed to introduce non-crypto users to ETH by making gifting delightfully simple. Here are recommended flows:
-
Creator flow:
-
Login with email (magic link). The platform creates and manages a custodial smart wallet for the user if one does not already exist.
-
Enter an amount and a secret, then create the gift card. The platform will submit the on-chain transaction from the user's custodial smart wallet (or optionally sponsor the gas on behalf of the creator).
-
Copy or share the gift card id + secret via email, SMS, or QR.
-
Recipient flow (email-first, non-technical):
-
Click the claim link in an email or open the web app.
-
Login with email (magic link). The platform automatically creates a custodial smart wallet for the recipient (if needed) and associates it with their account.
-
Paste the secret and claim — the platform will call
claimGiftCardon-chain and credit the recipient's custodial smart wallet address. The platform can sponsor gas so the recipient doesn't need to hold ETH to receive funds.
Reducing friction: This app uses email-based magic links and custodial smart wallets so recipients do not need to install MetaMask or manage private keys. A claim link in an email can log a user in and pre-fill the gift id/secret.
Because the contract emits events on create and claim, this system is useful beyond 1:1 gifting:
- Raffles & draws: Create many small gift cards and publish IDs to attendees; use off-chain tooling to randomly pick winners from
GiftCardCreatedevents and notify them. - Airdrops: Pre-create gift cards with secrets distributed to a list. Recipients claim at their convenience.
- Live events: Use gift card creation as a drop-in reward mechanism (e.g., event attendees get secret QR codes in physical swag). The frontend can monitor
GiftCardCreatedandGiftCardClaimedto render live stats.
Implementation tip: Run an off-chain indexer (a simple node script using ethers.js or The Graph) that listens to events and stores friendly metadata in a DB for fast querying and dashboards.
This project supports a custodial smart-wallet model only: users sign up or login with email (magic links) and the platform creates and manages a smart wallet for them. The wallet is a smart contract wallet or an account abstraction-backed wallet that the platform provisions and associates with the user's account.
Key points:
- Account creation: When a user logs in with email, the backend creates a smart wallet for them (or links an existing one). The smart wallet acts as the user's blockchain address for receiving and sending ETH.
- Transaction submission: The platform can submit transactions on behalf of users, or allow users to trigger actions through the web UI while the backend signs/authorizes transactions using secure key management (HSM/KMS). The platform can sponsor gas so recipients never need ETH to claim funds.
- Recovery & UX: Users recover access via email (magic link) and don't manage private keys themselves. The platform provides account recovery, session management, and any validation (e.g., 2FA) required.
- Security & compliance: Custodial models require strong key-management (HSM/KMS), audit logs, rate limits, optional KYC/AML depending on jurisdiction, and clear user-facing terms about custody of funds.
Implementation guidance:
- Use battle-tested smart wallet SDKs or account-abstraction tooling (thirdweb, Biconomy, OpenZeppelin Defender, or custom smart-wallet contracts) to create wallets programmatically.
- Keep private keys and signing capabilities in a secure KMS or HSM. Consider using threshold signatures for higher assurance.
- Log and monitor all signer activity and provide users visibility (activity feed) and recovery options.
- Plan for gas sponsorship costs and relayer infrastructure if you want zero-friction claims.
Security and UX note: Because this repo provides a custodial smart-wallet approach, do not ask users for seed phrases or private keys. Instead, focus on secure backend key management, transparent UX, and legal/compliance controls as required.
- createGiftCard(string secret) payable -> returns uint256 giftId
- claimGiftCard(uint256 giftId, string secret) -> claims and transfers ETH
- getGiftCard(uint256 giftId) -> returns details tuple
- getUserGiftCardIds(address user) -> returns uint256[]
Events:
- GiftCardCreated(giftCardId, creator, amount)
- GiftCardClaimed(giftCardId, claimedBy, amount)
- GiftCardTransferred(giftCardId, from, to)
- The current contract is a demo: it stores plaintext secrets on-chain. For production, secrets should only be stored as salted hashes off-chain or hashed on-chain without storing the plaintext.
- The app uses Next.js + ethers.js. The frontend uses email-based custodial smart wallets (no MetaMask required for the core user experience).
- This README focuses on ETH as the asset; adapting to ERC-20 requires adding token transfer logic and allowance handling.
-
Security hardening for production:
- Remove plaintext secrets from contract storage.
- Consider reentrancy guards and using Checks-Effects-Interactions pattern (the contract already does the transfer after state updates, which is good).
- Add limits, expiry, and refund paths for unclaimed gift cards.
-
UX / onboarding:
- Improve and harden the email-claim flow and the custodial smart-wallet UX (magic links, recovery, activity feed).
- Experiment with sponsored gas (meta-transactions) and relayer infrastructure for zero-fee claims.
-
Infrastructure:
- Add an events indexer (ethers.js script or The Graph subgraph) to provide fast search, dashboards, and analytics.
-
Testing & QA:
- Add unit tests for edge cases (double-claim, invalid secrets, transfer failures).
- Add integration tests with a local forked chain and a headless Web3 wallet for the frontend.
- Contract:
contract/src/GiftCard.sol - Frontend:
frontend/(Next.js app) - Frontend ABI:
frontend/src/abi/giftcards.abi.ts
If you'd like help hardening the contract or shipping a production-ready onboarding flow (email-based smart wallets, sponsored gas), open an issue or a PR with the specific area you'd like to improve.
This README aims to give product teams, engineers, and event organizers a concise starting point to understand and extend the Monaco Gift Card system.