EazPay Bot
P2P stablecoin payments on Telegram, powered by the Tempo blockchain.
What is EazPay?
A Telegram bot that lets users send USDC to each other instantly via smart contract wallets on the Tempo L1 blockchain. Sub-$0.001 fees, 0.6s finality, fully on-chain.
Key Features
- P2P Payments —
/send @user 10sends 10 USDC instantly - Smart Contract Wallets — each user gets an on-chain wallet with daily limits
- Atomic Fee Collection — 0.1% fee handled automatically by TreasuryRouter
- Group Payments —
/splitand/requestfor group expenses - Security — PIN lock, 2FA for large transactions, anti-phishing codes
Quick Start
Prerequisites
- Node.js 18+
- PostgreSQL database
- Telegram Bot Token (from @BotFather)
- Operator wallet with USDC on Tempo for gas
Environment Variables
# Telegram
BOT_TOKEN=$BOT_TOKEN
# Tempo Network
TEMPO_RPC_URL=https://rpc.tempo.xyz
CHAIN_ID=4217
# Database
DATABASE_URL=$DATABASE_URL
# Security
WALLET_ENCRYPTION_KEY=$WALLET_ENCRYPTION_KEY
# Smart Contract System — V4 (current)
OPERATOR_PRIVATE_KEY=$OPERATOR_PRIVATE_KEY
OPERATOR_ADDRESS=$OPERATOR_ADDRESS
EAZPAY_V4_ADDRESS=0x07950a498B83b5b2958bC5152884Ffc56409160F
USER_WALLET_FACTORY_V4_ADDRESS=0x1BE92ea599bBb3896e3e0F1a98fb2363e0c379f7
TREASURY_ROUTER_V4_ADDRESS=0x6384a449B09501F0BFed3881E29CF8dCF582678c
# Legacy V3 — kept for users who haven't run /migrate yet
TREASURY_ROUTER_ADDRESS=0xF830dbdAdC00671372EcFEFb9d1246a431707636
USER_WALLET_FACTORY_ADDRESS=0xD0Da292b42c82a153122Aff31C4D6E9a9876c925
EAZPAY_CONTRACT_ADDRESS=0x3867C0D7b77A996b50759eec330f5Ef5113E96f6
# Admin
ADMIN_TELEGRAM_ID=$ADMIN_TELEGRAM_ID
# Webhook (optional — polling mode if not set)
# WEBHOOK_URL=https://your-domain.com/webhook
# WEBHOOK_SECRET=$WEBHOOK_SECRET
Source Access
The bot and web repositories are currently private during active development and security hardening. Smart contracts are verified on Tempo Explorer (EazPay V4) — you can read the Solidity source and confirm the deployed bytecode directly on-chain. The application-layer repos will be opened once the contracts pass independent audit.
Bot Commands
| Command | Description | Auth Required |
|---|---|---|
/start | Create wallet + welcome | No |
/wallet | View address + balance | Session |
/deposit | Show deposit address | Session |
/send @user amount | Send USDC | Session + Rate Limit |
/split total @u1 @u2 | Split payment equally | Session + Rate Limit |
/request @user amount [memo] | Request payment (24h expiry) | Session |
/history | Recent transactions | Session |
/leaderboard | Top payers in group | No |
/export | Export private key (DM only) | Session |
/setpin PIN | Set security PIN | No |
/unlock PIN | Unlock session | No |
/setcode word | Rotate anti-phishing code (auto-generated on /start since audit #15) | Session |
/audit | Last 20 sensitive actions on your wallet (send, export, setpin, migrate, …) | Session |
/mydata | Export full user data as JSON (LGPD self-service) | Session |
/deleteaccount | Begin account deletion (blocked if on-chain balance > 0) | Session |
/migrate | Move legacy V3 wallet to V4 (enables external send) | Session |
/revenue | Revenue dashboard | Admin |
/pauseack <id> | ACK an auto-pause arming (admin) | Admin |
/pausenow | Manual pause both contract stacks (admin emergency) | Admin |
/incident <type> | Print pre-written incident disclosure template (admin) | Admin |
/help | List commands | No |
Smart Contracts (Tempo Mainnet)
| Contract | Address | Function |
|---|---|---|
| EazPay V4 (current) | 0x07950a498B83b5b2958bC5152884Ffc56409160F | Orchestrator — P2P, split batches, external withdrawals, emergency pause |
| UserWalletFactory V2 | 0x1BE92ea599bBb3896e3e0F1a98fb2363e0c379f7 | Deploys per-user wallets (V4 as botOperator) |
| TreasuryRouter V2 | 0x6384a449B09501F0BFed3881E29CF8dCF582678c | Fee rate registry + treasury sink (0.1%, 2% ceiling) |
Legacy V3 stack (for users registered before 2026-04-16 who haven't run /migrate):
EazPayV3 0x3867C0D7...Ef5113E96f6, Factory 0xD0Da292b...4D6E9a9876c925, Router 0xF830dbdA...31707636.
- Chain: Tempo Mainnet (Chain ID: 4217)
- USDC:
0x20c000000000000000000000b9537d11c60e8b50(TIP-20) - Explorer: https://explore.tempo.xyz
Architecture
See ARCHITECTURE for detailed system design.
License
MIT
Commands Reference
All commands available in @EazPay_bot.
Getting Started
/start
Creates your account and deploys a smart contract wallet on Tempo L1.
/start
Your wallet is created in the background. You'll receive a confirmation once it's ready.
Wallet
/wallet
Shows your wallet address and current USDC balance (on-chain).
/wallet
/deposit
Shows your wallet address for receiving USDC on Tempo L1.
/deposit
Send USDC to this address from any wallet on Tempo (Chain ID: 4217).
/export
Shows your contract wallet address and self-custody instructions. Works only in DMs with the bot (not in groups).
/export
Your funds are always on-chain in your smart contract wallet. Even if EazPay goes offline, you can interact with the contract directly.
Payments
/send
Send USDC to another user. A 0.1% fee is automatically deducted.
/send @username 10
/send @username 25.50
- Recipient must have an EazPay wallet
- Transaction settles in ~0.6 seconds
- Fee is collected on-chain by TreasuryRouter
/split
Split a bill equally among multiple users. Executed as a single atomic batch transaction.
/split 30 @alice @bob @carol
Each person pays 10 USDC (30 ÷ 3). All transfers happen in one on-chain transaction — all succeed or all fail.
/request
Request a payment from another user. Expires after 24 hours.
/request @username 15
/request @username 50 dinner
The recipient gets a notification and can accept or decline.
History
/history
Shows your recent transactions.
/history
Each entry includes: direction (sent/received), amount, counterparty, tx hash, and timestamp.
Security
/setpin
Set a PIN to protect your wallet. Required before high-value transactions.
/setpin
/unlock
Unlock your session with your PIN.
/unlock
/setcode
Set an anti-phishing code. This code appears in every bot message so you can verify it's really EazPay.
/setcode
Groups
/leaderboard
Shows the payment leaderboard for the current group.
/leaderboard
Rankings based on volume sent within the group.
Admin (restricted)
/revenue
Admin-only dashboard showing revenue stats, volume, and fee collection.
/revenue
Only available to the admin account.
Architecture — EazPay Bot
System Overview
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ Telegram User │────▶│ Bot (Railway) │────▶│ Tempo Blockchain │
│ (@EazPay_bot) │◀────│ Node.js/grammY │◀────│ Chain ID: 4217 │
└─────────────────┘ └──────────────────┘ └─────────────────────┘
│ │
▼ │
┌──────────┐ ┌─────┴──────────┐
│ Postgres │ │ Smart Contracts │
│ (Railway)│ │ (4 contracts) │
└──────────┘ └────────────────┘
On-Chain Architecture
[Telegram User]
|
v
[Bot Server] -- uses OPERATOR_KEY (limited permissions)
|
v
[EazPay Orchestrator] -- orchestrator
|
|-- registerUser(telegramId, userEOA)
| |
| v
| [UserWalletFactory.sol]
| |
| v
| [UserWallet.sol] -- per-user smart contract wallet
| - owner = user's EOA
| - botOperator = bot's key
| - dailyLimit = 1000 USDC
|
+-- sendPayment(fromTgId, toTgId, amount)
|
v
[UserWallet.send()]
|
|-- fee --> [TreasuryRouter.sol] --> Treasury wallet
|
+-- net --> [Recipient wallet]
Smart Contracts
UserWallet.sol
- Purpose: Per-user smart contract wallet
- Owner: User's EOA — on-chain role with no daily limit. By default the bot holds this EOA private key (encrypted in DB). After
/export, the user controls it directly from any external wallet. - Bot Operator: Can call
send()within daily limits (separate dedicated EOA since 2026-04-16) - Daily Limit: 1000 USDC default (configurable by owner)
- Key functions:
send(token, to, amount)— bot-operated transfer with daily limit + feewithdraw(to, amount)— owner-only, no limitsetDailyLimit(newLimit)— owner-onlysetBotOperator(newOperator)— owner-only
UserWalletFactory.sol
- Purpose: Deploys one UserWallet per Telegram user
- Access: Only botOperator can create wallets
- Key functions:
createWallet(telegramUserId, ownerAddress)— deploys new walletgetWallet(telegramUserId)— lookup by Telegram IDhasWallet(telegramUserId)— check existence
EazPay Orchestrator
- Purpose: Central orchestrator
- Access: Only botOperator
- Key functions:
registerUser(telegramUserId, userOwner)— creates wallet via factorysendPayment(fromTelegramId, toTelegramId, amount)— atomic P2P payment
TreasuryRouter.sol
- Purpose: Automatic fee collection
- Fee: 0.1% (10 basis points), max 2%
- Key functions:
collectFee(token, amount)— called by UserWallet during sendsweepToTreasury(token)— forward fees to treasury walletsetFeePercent(newFee)— owner-only
Security Model
Access Control
Deployer (0x3e55...e915)
+-- owner of: TreasuryRouter, UserWalletFactory, EazPay
+-- can: change fees, set treasury, update bot operator
Operator (0x99aD...f8BB)
+-- botOperator of: UserWalletFactory, EazPay, each UserWallet
+-- can: create wallets, execute payments (within daily limits)
+-- cannot: withdraw, change limits, change operator
User EOA
+-- owner of: their UserWallet
+-- can: withdraw any amount, change daily limit, change bot operator
Compromise Scenarios
| Compromised | Impact | Mitigation |
|---|---|---|
| Operator key | Max 1000 USDC/day per wallet | Daily limit on-chain |
| Railway server | Operator key exposed | Daily limit caps damage |
| Database | Legacy encrypted keys (being phased out) | Contract wallets don't need DB keys |
| TreasuryRouter owner | Fee change (max 2%) | MAX_FEE cap in contract |
Tech Stack
| Layer | Technology |
|---|---|
| Runtime | Node.js 18+ / TypeScript |
| Bot Framework | grammY |
| Blockchain | ethers.js v6 |
| Database | PostgreSQL (Prisma ORM) |
| Smart Contracts | Solidity 0.8.20 (Hardhat) |
| Hosting | Railway (bot), Vercel (website) |
| CI/CD | GitHub Actions |
Database Schema
| Model | Purpose |
|---|---|
| User | Telegram user → wallet mapping, PIN, anti-phishing |
| Transaction | Payment records with fee tracking |
| PendingTransfer | Confirmation flow (5min TTL) |
| PaymentRequest | /request with 24h expiry |
Key Fields on User
walletAddress— user's EOAcontractAddress— smart contract wallet (Phase 6+)encryptedKey— legacy, being phased outpinHash— bcrypt hashed PIN
Payment Flow
1. User sends: /send @bob 10
2. Bot validates: amount, balance, recipient exists
3. Small tx (<100 USDC): inline confirm/cancel buttons
Large tx (>=100 USDC): 2FA code required
4. User confirms
5. Bot calls: EazPay.sendPayment(fromTgId, toTgId, 10_000000)
6. On-chain:
a. UserWallet.send() checks daily limit
b. TreasuryRouter.collectFee() -> 0.05 USDC fee
c. 9.95 USDC -> recipient wallet
d. 0.05 USDC -> treasury
7. Bot updates DB, notifies sender + receiver
Middleware Stack
Request -> updateUsername -> rateLimitGeneral -> [command handler]
|
requireSession (if sensitive)
|
rateLimitSend (if /send, /split)
- rateLimitGeneral: 5 commands / 30 seconds
- rateLimitSend: 3 sends / 60 seconds
- requireSession: checks PIN unlock status
Security
Overview
EazPay uses a layered security model: smart contract constraints on-chain + user-facing protections in the bot.
On-Chain Security
Limited Operator
The bot's operator key can only call send() on your UserWallet. It cannot:
- Withdraw funds
- Execute arbitrary contract calls
- Change the wallet owner
- Exceed the daily limit
Daily Spending Limit
- 1,000 USDC per day per wallet, enforced on-chain
- Resets every 24 hours
- Only applies to operator-initiated transfers
- Owner can withdraw directly without limits
Emergency Pause
The EazPay contract (V3) includes an emergency pause function. If a vulnerability is detected, the contract owner can pause all operations until resolved.
No Generic Execute
Unlike many account abstraction wallets, EazPay's UserWallet does not have a generic execute() function. The operator can only call the specific send() function — no arbitrary calldata.
User-Facing Security
PIN Protection
- Set a PIN with
/setpin - Required to unlock sessions after inactivity
- Unlock with
/unlock
Anti-Phishing Code
- Set with
/setcode - Your personal code appears in every bot message
- If a message doesn't include your code, it's not from EazPay
Session Auto-Lock
Sessions automatically lock after a period of inactivity. Use /unlock with your PIN to resume.
DM-Only Sensitive Operations
/exportonly works in direct messages with the bot- Private key and wallet details are never shown in group chats
Audit Status
- Contracts open for inspection — verified source and bytecode on Tempo Explorer
- Internal audits — 15 rounds of self-audit performed (SAST, manual review, on-chain state verification, STRIDE, LGPD gap analysis, threat-modeling). Latest audit: 2026-04-20 (threat-driven user-security review)
- Not formally audited by a third party — OpenZeppelin / Trail of Bits / Cure53 engagement is on the roadmap before public mass-market launch
- Responsible-disclosure channel — report vulnerabilities to security@eazpay.xyz. A funded bug bounty program (Immunefi class) will launch post external audit.
Best Practices
- Set a PIN — protects against unauthorized access if someone has your Telegram
- Set an anti-phishing code — verify every message is from the real bot
- Don't share your wallet details in groups — use
/exportin DMs only - Verify transaction details — always confirm recipient and amount before sending
Fees
Transaction Fee
- 0.1% per
/sendtransaction - Collected automatically on-chain by the TreasuryRouter contract
- Hard cap: 2% — enforced on-chain, cannot be raised above this
Example
Sending 100 USDC:
- Fee: 0.10 USDC (0.1%)
- Recipient receives: 99.90 USDC
- Treasury receives: 0.10 USDC
How It Works
- You send
/send @friend 100 - Bot calls
EazPay.sendPayment()on-chain - Contract reads fee rate from TreasuryRouter
- Fee is split automatically: 99.90 → recipient, 0.10 → treasury
- All in one atomic transaction
The fee rate is read from the TreasuryRouter contract — not hardcoded in the bot. You can verify it on-chain at any time.
Gas Fees
- You pay $0 in gas — the operator pays all gas costs
- Tempo L1 gas costs are sub-$0.001 per transaction
- Gas is paid in stablecoin (no native token needed)
No Hidden Fees
- No account creation fee
- No monthly fees
- No withdrawal fees (direct on-chain access is free)
- No fee for receiving payments
- Only the 0.1% on outgoing
/sendtransactions
Smart Contracts
All contracts are deployed on Tempo L1 Mainnet (Chain ID: 4217).
Explorer: explore.tempo.xyz
Deployed Contracts (V4 — current)
| Contract | Address | Purpose |
|---|---|---|
| EazPay V4 | 0x07950a498B83b5b2958bC5152884Ffc56409160F |
Payment orchestrator — P2P sends, split batches, external withdrawals, emergency pause |
| UserWalletFactory V2 | 0x1BE92ea599bBb3896e3e0F1a98fb2363e0c379f7 |
Deploys per-user UserWallets with V4 as their botOperator |
| TreasuryRouter V2 | 0x6384a449B09501F0BFed3881E29CF8dCF582678c |
Fee rate registry + treasury sink (0.1%, hardcoded cap 2%). V1's collectFee dead code removed. |
Key Addresses
| Role | Address |
|---|---|
| Treasury | 0x3e553952DfbEd4ACa65B604922689fd31fe915c4 |
| USDC (TIP-20) | 0x20c000000000000000000000b9537d11c60e8b50 |
Legacy V3 stack (still on-chain for users who haven't run /migrate)
| Contract | Address | Status |
|---|---|---|
| EazPay V3 | 0x3867C0D7b77A996b50759eec330f5Ef5113E96f6 | Accepts sendPayment/batchSend for pre-V4 users. No external path. |
| UserWalletFactory (V3) | 0xD0Da292b42c82a153122Aff31C4D6E9a9876c925 | Holds legacy UserWallets. Pre-V4 users had empty encryptedKey (I-501) — resolved per user on /migrate. |
| TreasuryRouter (V3) | 0xF830dbdAdC00671372EcFEFb9d1246a431707636 | Still receives fees for V3 traffic. |
| Rescue admin wallet (V3) | 0x04d5d5864BEb93b2d1B2D50a2492840eb4F86538 | Synthetic admin (tgId 99999999999) whose owner is the operator EOA — used by /migrate to drain residual V3 balances. |
Deprecated Contracts
| Contract | Address | Note |
|---|---|---|
| TempoPay V1 | 0xc8aF7810751Fa7e7542372eADc44A3aB1cDCaaA9 | Replaced by V2 |
| EazPay V2 | 0x1CF26AAbb80Ba614672C37fde6A27D9de2566dc5 | Replaced by V3 (added emergency pause) |
Contract Details
EazPay V4 (Payment Orchestrator)
Entry point for every user-initiated movement of USDC. Handles:
sendPayment(fromTgId, toTgId, amount)— P2P transfer between EazPay userssendExternal(fromTgId, address, amount)— V4-only: withdraw USDC to any on-chain address (MetaMask, exchange deposit, another wallet)batchSend()— atomic batch payments (used by/split)- Emergency pause — owner can halt the orchestrator while funds in UserWallets remain withdrawable via owner-signed
withdraw()
Key properties:
onlyBot·whenNotPaused·ReentrancyGuardon every transfer entry- Operator cannot withdraw, cannot change ownership, cannot change daily limits — all owner-only
- Fee rate read live from TreasuryRouter (not hardcoded)
- All-or-nothing batch execution (up to 20 recipients per call)
address(0)guard on every address parameter- Events emitted on every admin action + per-transfer events
- Solidity ^0.8.24, Slither clean at every severity level
UserWalletFactory V2
Deploys one UserWallet contract per user:
- Called on
/startfor new users and on/migratefor legacy V3 users - Sets the user's EOA as
owner, V4 contract asbotOperator - Reverts on duplicate Telegram ID registration
UserWallet
Each user's individual smart contract wallet:
- Owner: the user's EOA (full control, no daily limit). Key is bot-held by default; use
/exportto take custody. - Operator: the V4 contract itself, not the operator EOA directly. The bot signs V4 calls; V4 then talks to UserWallet as the contract-level botOperator.
- Daily limit: 1,000 USDC/day on operator-initiated transfers (on-chain enforced).
withdraw(to, amount)— owner-only, no daily limit, no fee — the self-custody exit.- No generic
execute()— no arbitrary calldata path.
TreasuryRouter V2
Fee rate registry + treasury sink. V2 removed V1's collectFee(from, amount) function (Slither-flagged dead code). Fees are routed exclusively by EazPayV4._routeFee.
- Current fee: 0.1% (10 basis points)
- Hardcoded ceiling:
MAX_FEE_BPS = 200(2%) —setFeereverts above it - Routes fees to
treasury()address - Fee rate readable on-chain:
fee()returns basis points withdraw(amount)can only send to the configured treasury — no arbitrary destination
Source Verification
- Contracts: verified on Tempo Explorer — Solidity source and deployed bytecode both readable on-chain
- Bot + web: repositories currently private; will be opened after independent audit of contracts
- Contracts written in Solidity, compiled with Hardhat
Verification
All contract interactions can be verified on the Tempo Explorer. Every /send and /split creates an on-chain transaction with a visible tx hash.
FAQ
General
Is EazPay custodial?
Yes, by default. The bot generates your EOA private key and stores it encrypted (AES-256-GCM) in its database. Your funds sit on-chain in a smart contract wallet that your EOA owns, and the bot's operator key has limited on-chain permissions (can only call send() within a 1,000 USDC/day cap — it cannot withdraw or change ownership). However, because the bot holds your owner key, a bot-side breach is in scope. If you want full self-custody, use /export to receive your EOA private key and manage your wallet from any external wallet — the bot no longer needs to be trusted. See Architecture and Security.
What tokens are supported?
USDC (TIP-20) on Tempo L1. Deposit USDC from any source into your wallet address to use the bot.
What blockchain does EazPay run on?
Tempo L1 — an EVM-compatible blockchain with sub-$0.001 fees and 0.6-second finality. Chain ID: 4217.
Fees
What are the fees?
0.1% per outgoing /send transaction. Capped at 2% on-chain. No other fees — no account fee, no monthly fee, no withdrawal fee. See Fees.
Do I pay gas?
No. The operator pays all gas costs. Tempo gas fees are sub-$0.001 per transaction.
Security
Can the operator steal my funds?
No. The operator (bot) can only call send() on your wallet, limited to 1,000 USDC/day. It cannot withdraw funds, make arbitrary calls, or change the wallet owner. See Security.
What happens if EazPay goes down?
Your funds remain on-chain in your smart contract wallet. You can interact with the contract directly on the Tempo Explorer using the owner key. No dependency on the bot.
Is there a daily limit?
Yes — 1,000 USDC/day per wallet, enforced on-chain. This limits what the operator can do. As the wallet owner, you can withdraw directly without this limit.
Are the contracts audited?
Community-reviewed + 15 rounds of internal self-audit, not yet formally audited by a third-party firm. External audit (OpenZeppelin / Trail of Bits / Cure53 class) is on the roadmap before mass-market launch. Responsible disclosure channel active — report vulnerabilities to security@eazpay.xyz. See also /.well-known/security.txt.
Wallet
What if I lose access to my Telegram account?
Your funds are on-chain in your smart contract wallet. If you have your owner key, you can call withdraw() on the contract directly. Use /export (in DMs) to see your contract address and self-custody instructions while you still have access.
How do I withdraw my funds?
Use /export in DMs with the bot for instructions. You can also call withdraw() directly on your UserWallet contract via the Tempo Explorer or any web3 wallet.
Can I use my wallet outside of EazPay?
Yes. Your UserWallet is a standard smart contract on Tempo L1. You can interact with it from any EVM-compatible wallet or directly on the explorer.