EazPay is live on Tempo mainnet! Try it now →

Documentation

Everything you need to know about EazPay — commands, architecture, security, fees, bridging, smart contracts, and more.

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 10 sends 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/split and /request for 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
/startCreate wallet + welcomeNo
/walletView address + balanceSession
/depositShow deposit addressSession
/send @user amountSend USDCSession + Rate Limit
/split total @u1 @u2Split payment equallySession + Rate Limit
/request @user amount [memo]Request payment (24h expiry)Session
/historyRecent transactionsSession
/leaderboardTop payers in groupNo
/exportExport private key (DM only)Session
/setpin PINSet security PINNo
/unlock PINUnlock sessionNo
/setcode wordRotate anti-phishing code (auto-generated on /start since audit #15)Session
/auditLast 20 sensitive actions on your wallet (send, export, setpin, migrate, …)Session
/mydataExport full user data as JSON (LGPD self-service)Session
/deleteaccountBegin account deletion (blocked if on-chain balance > 0)Session
/migrateMove legacy V3 wallet to V4 (enables external send)Session
/revenueRevenue dashboardAdmin
/pauseack <id>ACK an auto-pause arming (admin)Admin
/pausenowManual pause both contract stacks (admin emergency)Admin
/incident <type>Print pre-written incident disclosure template (admin)Admin
/helpList commandsNo

Smart Contracts (Tempo Mainnet)

Contract Address Function
EazPay V4 (current)0x07950a498B83b5b2958bC5152884Ffc56409160FOrchestrator — P2P, split batches, external withdrawals, emergency pause
UserWalletFactory V20x1BE92ea599bBb3896e3e0F1a98fb2363e0c379f7Deploys per-user wallets (V4 as botOperator)
TreasuryRouter V20x6384a449B09501F0BFed3881E29CF8dCF582678cFee 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 + fee
    • withdraw(to, amount) — owner-only, no limit
    • setDailyLimit(newLimit) — owner-only
    • setBotOperator(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 wallet
    • getWallet(telegramUserId) — lookup by Telegram ID
    • hasWallet(telegramUserId) — check existence

EazPay Orchestrator

  • Purpose: Central orchestrator
  • Access: Only botOperator
  • Key functions:
    • registerUser(telegramUserId, userOwner) — creates wallet via factory
    • sendPayment(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 send
    • sweepToTreasury(token) — forward fees to treasury wallet
    • setFeePercent(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 keyMax 1000 USDC/day per walletDaily limit on-chain
Railway serverOperator key exposedDaily limit caps damage
DatabaseLegacy encrypted keys (being phased out)Contract wallets don't need DB keys
TreasuryRouter ownerFee change (max 2%)MAX_FEE cap in contract

Tech Stack

Layer Technology
RuntimeNode.js 18+ / TypeScript
Bot FrameworkgrammY
Blockchainethers.js v6
DatabasePostgreSQL (Prisma ORM)
Smart ContractsSolidity 0.8.20 (Hardhat)
HostingRailway (bot), Vercel (website)
CI/CDGitHub Actions

Database Schema

Model Purpose
UserTelegram user → wallet mapping, PIN, anti-phishing
TransactionPayment records with fee tracking
PendingTransferConfirmation flow (5min TTL)
PaymentRequest/request with 24h expiry

Key Fields on User

  • walletAddress — user's EOA
  • contractAddress — smart contract wallet (Phase 6+)
  • encryptedKey — legacy, being phased out
  • pinHash — 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

  • /export only 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

  1. Set a PIN — protects against unauthorized access if someone has your Telegram
  2. Set an anti-phishing code — verify every message is from the real bot
  3. Don't share your wallet details in groups — use /export in DMs only
  4. Verify transaction details — always confirm recipient and amount before sending

Fees

Transaction Fee

  • 0.1% per /send transaction
  • 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

  1. You send /send @friend 100
  2. Bot calls EazPay.sendPayment() on-chain
  3. Contract reads fee rate from TreasuryRouter
  4. Fee is split automatically: 99.90 → recipient, 0.10 → treasury
  5. 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 /send transactions

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 V30x3867C0D7b77A996b50759eec330f5Ef5113E96f6Accepts sendPayment/batchSend for pre-V4 users. No external path.
UserWalletFactory (V3)0xD0Da292b42c82a153122Aff31C4D6E9a9876c925Holds legacy UserWallets. Pre-V4 users had empty encryptedKey (I-501) — resolved per user on /migrate.
TreasuryRouter (V3)0xF830dbdAdC00671372EcFEFb9d1246a431707636Still receives fees for V3 traffic.
Rescue admin wallet (V3)0x04d5d5864BEb93b2d1B2D50a2492840eb4F86538Synthetic admin (tgId 99999999999) whose owner is the operator EOA — used by /migrate to drain residual V3 balances.

Deprecated Contracts

Contract Address Note
TempoPay V10xc8aF7810751Fa7e7542372eADc44A3aB1cDCaaA9Replaced by V2
EazPay V20x1CF26AAbb80Ba614672C37fde6A27D9de2566dc5Replaced 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 users
  • sendExternal(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 · ReentrancyGuard on 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 /start for new users and on /migrate for legacy V3 users
  • Sets the user's EOA as owner, V4 contract as botOperator
  • 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 /export to 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%) — setFee reverts 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.