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.
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!
CijeTheCreator
/
maillet
DESCRIPTION
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
# fill out .env.example
mv .env.example .env
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
docker build -t maillet .
docker run -d --name maillet -p 3000:3000 -p 5000:5000 maillet_app --env-file .env
- 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
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.