DEV Community

Cover image for Maillet: The World's First Mail-Based EVM Wallet
Chijioke Osadebe
Chijioke Osadebe Subscriber

Posted on

Maillet: The World's First Mail-Based EVM Wallet

This is a submission for the Postmark Challenge: Inbox Innovators.

What I Built

I created Maillet — an EVM wallet designed to be operated completely through mail.

Maillet lets you do everything a traditional crypto wallet can—through email. Users can send transactions and check their Ethereum balances just by sending a message.

Every email address becomes a wallet with Maillet. You can send funds to friends, co-workers, or anyone using just their email, whether they’ve signed up or not. When they onboard, the funds are already waiting for them.

Demo

You can try Maillet by sending an email with your intentions to transactions@maillet.tech

Things you can try:

  • Create a wallet
  • Transfer ether to any email address
  • Get your transaction history
  • Get your balance

You can visit the live site for Maillet too here.

Account creation

Balance inquiry

Transfer Ether

Dashboard

Testing

To try out Maillet, just send an email to transactions@maillet.tech. You'll receive 0.0025 test ETH to explore its features.

If you prefer using a dashboard, onboard here with the same email address you used to create your account.

Code Repository

Check out the complete source code for Maillet on GitHub! The repo includes everything you need — installation instructions, well-commented code, and all logic behind the scenes.

Feel free to explore, fork, contribute, or adapt it for your own use. The project is regularly maintained, and all pull requests are welcome!

Maillet

An EVM wallet designed to be operated completely through mail.

Things you can do

  • Send/Receive funds to any email address
  • Send/Receive funds to any wallet address
  • Fetch your balance
  • Fetch your transaction history

Quickstart

Host System Package Dependencies

  • Docker

After system dependencies are installed, clone this repository:

# clone and enter repo
git clone https://github.com/CijeTheCreator/maillet
Enter fullscreen mode Exit fullscreen mode
# fill out .env.example
mv .env.example .env
Enter fullscreen mode Exit fullscreen mode
DATABASE_URL=VALUE_HERE
ENCRYPTION_KEY=VALUE_HERE
RPC_URL=VALUE_HERE
NEXTAUTH_SECRET=VALUE_HERE

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=VALUE_HERE
CLERK_SECRET_KEY=VALUE_HERE
CLERK_WEBHOOK_SIGNING_SECRET=VALUE_HERE

COINGECKO_API_KEY=VALUE_HERE
ETHERSCAN_API_KEY=VALUE_HERE

FAUCET_PRIVATE_KEY=VALUE_HERE


GOOGLE_API_KEY=VALUE_HERE
SENDGRID_API_KEY=VALUE_HERE
ETH_RATE=VALUE_HERE
WALLET_API_URL=VALUE_HERE
Enter fullscreen mode Exit fullscreen mode
docker build -t maillet .
docker run -d --name maillet -p 3000:3000 -p 5000:5000 maillet_app --env-file .env
Enter fullscreen mode Exit fullscreen mode
  • The Next.js client will run on localhost:3000.
  • The flask server (for parsing mails) will run on localhost:5000.

Contributing

Contributions are welcome! Please feel free to submit a Pull…

How I Built It

I built Maillet as a simple mail-based Ethereum wallet for the hackathon. It lets users send an email to transactions@maillet.tech to interact with the Ethereum blockchain. They can create an account, check their balance, or send funds—either to another Maillet user or to a wallet address using a public key. Everything is done by writing a plain email.

When a user sends a message, Postmark parses the email and sends the content to my Inbound Webhook at /postmark-webhook. I then pass the message to Google Gemini’s LLM, which figures out what the user wants to do—like send ETH or check their balance. Once the intent is clear, the transaction is handled on-chain.

Maillet sends a confirmation email back to the user with the result. Since I couldn’t get my Postmark account verified in time, I used a different mail service to send those receipts.

You can view the code for the webhook here.

@app.route('/postmark-webhook', methods=['POST'])
def postmark_webhook():
    """
    Endpoint to receive Postmark webhooks and print the request body
    """
    try:
        received_email = request.get_json()
        from_account = received_email['From']
        subject = received_email['Subject']
        text = received_email['TextBody']
        user_message = f"Sender: {from_account}\nSubject: {subject}\n{text}"

        inputs = {"messages": [
            ("system", generate_system_message(from_account)),
            ("user", user_message),
        ]}
        result = graph.invoke(inputs)
        print(result)
        return jsonify({"status": "success", "message": "Webhook received"}), 200
    except Exception as e:
        print(f"Error processing webhook: {e}")
        return jsonify({"status": "error", "message": str(e)}), 500


Enter fullscreen mode Exit fullscreen mode

Architecture

Image description

Image description

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.