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
  • Cross-Chain Bridge/bridge to bring USDC from 8+ chains
  • Security — PIN lock, 2FA for large transactions, anti-phishing codes
  • Mini App — Telegram Web App with full wallet UI

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 (Phase 6)
OPERATOR_PRIVATE_KEY=$OPERATOR_PRIVATE_KEY
OPERATOR_ADDRESS=$OPERATOR_ADDRESS
TREASURY_ROUTER_ADDRESS=0xf6b1285907F206848e1D51857eA4554B53E45ebc
USER_WALLET_FACTORY_ADDRESS=0x7F076a59501b2d61c64e196d697a95415cCf3C40
EAZPAY_CONTRACT_ADDRESS=0x8741ac2b1e4715084618B2540a4079EdA5D45b99

# 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

Install & Run

git clone https://github.com/tomajackmac/eazpay-bot.git
cd eazpay-bot
npm install
cp .env.example .env  # fill in your values
npx prisma db push
npm run build
npm start

Development

npm run dev  # runs with ts-node in watch mode

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
/bridge amount from chainBridge USDC from other chainsSession + Rate Limit
/chainsList supported bridge chainsNo
/historyRecent transactionsSession
/leaderboardTop payers in groupNo
/exportExport private key (DM only)Session
/setpin PINSet security PINNo
/unlock PINUnlock sessionNo
/setcode wordSet anti-phishing codeNo
/revenueRevenue dashboard (admin)Admin
/helpList commandsNo

Smart Contracts (Tempo Mainnet)

Contract Address Function
TreasuryRouter0xf6b1285907F206848e1D51857eA4554B53E45ebcFee collection (0.1%)
UserWalletFactory0x7F076a59501b2d61c64e196d697a95415cCf3C40Deploys per-user wallets
EazPay V30x8741ac2b1e4715084618B2540a4079EdA5D45b99Payment router — sends, batch payments, emergency pause
  • 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.


Cross-Chain Bridge

/bridge

Bridge USDC from another chain to your Tempo wallet via Relay Protocol.

/bridge 100 from ethereum
/bridge 50 from base

/chains

Shows all supported networks for bridging.

/chains

Supported networks: Ethereum, Base, Arbitrum, Polygon, Optimism, Avalanche, BSC, Solana

Bridge times vary by source chain (typically 1–10 minutes). See Bridge docs for details.


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 (full control, no daily limit)
  • Bot Operator: Can call send() within daily limits
  • 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
BridgeTransactionCross-chain bridge status tracking

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, /bridge)
  • rateLimitGeneral: 5 commands / 30 seconds
  • rateLimitSend: 3 sends / 60 seconds
  • requireSession: checks PIN unlock status

Bridge (Cross-Chain)

Uses Relay Protocol API for cross-chain USDC bridging:

  • Supported chains: Ethereum, Arbitrum, Optimism, Base, Polygon, Avalanche, BSC, Zora
  • Flow: get quote → generate deposit address → user deposits → Relay bridges to Tempo
  • Status tracking via BridgeTransaction model

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

  • Community-reviewed — contract code is open for inspection
  • Not formally audited — no third-party audit firm has reviewed the contracts
  • Bug bounty active — responsible disclosure encouraged at contact@eazpay.xyz

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)

Bridge Fees

Cross-chain bridge transfers via Relay Protocol have their own fee structure:

  • Relay Protocol charges a small fee per bridge transaction
  • Fees vary by source chain and amount
  • See /bridge output for exact fee before confirming

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

Cross-Chain Bridge

Overview

EazPay supports cross-chain USDC transfers from 8 networks to your Tempo wallet via Relay Protocol.


Supported Networks

Network Token
EthereumUSDC
BaseUSDC
ArbitrumUSDC
PolygonUSDC
OptimismUSDC
AvalancheUSDC
BSCUSDC
SolanaUSDC

How to Bridge

/bridge 100 from ethereum
/bridge 50 from base
  1. Run /bridge <amount> from <chain>
  2. Bot generates a Relay Protocol bridge request
  3. You receive a deposit address on the source chain
  4. Send USDC to that address from your external wallet
  5. Relay bridges the funds to your Tempo wallet

Timing

Bridge times depend on the source chain:

Source Chain Estimated Time
Base, Arbitrum, Optimism1–3 minutes
Polygon, Avalanche, BSC2–5 minutes
Ethereum5–15 minutes
Solana2–5 minutes

Times are estimates and may vary based on network congestion.


Fees

Relay Protocol charges a fee per bridge transaction. The exact fee depends on:

  • Source chain
  • Amount being bridged
  • Current network conditions

The fee is shown before you confirm the bridge — no surprises.


Checking Status

Use /history to see the status of bridge transactions. Each bridge entry shows:

  • Source chain
  • Amount
  • Status (pending / completed / failed)
  • Relay request ID

Limitations

  • Direction: Currently supports inbound bridges only (other chains → Tempo)
  • Token: USDC only
  • Minimum: Varies by chain (typically ~$1)

Smart Contracts

All contracts are deployed on Tempo L1 Mainnet (Chain ID: 4217).

Explorer: explore.tempo.xyz


Deployed Contracts

Contract Address Purpose
EazPay V3 0x8741ac2b1e4715084618B2540a4079EdA5D45b99 Payment router — handles sends, batch payments, fee splitting
UserWalletFactory 0x7F076a59501b2d61c64e196d697a95415cCf3C40 Deploys individual UserWallet contracts for each user
TreasuryRouter 0xf6b1285907F206848e1D51857eA4554B53E45ebc Collects fees (0.1%, capped at 2%) and routes to treasury

Key Addresses

Role Address
Treasury 0x7A33c090E6cbb65ed909cD174713857be171169a
USDC (TIP-20) 0x20c000000000000000000000b9537d11c60e8b50

Deprecated Contracts

Contract Address Note
TempoPay V10xc8aF7810751Fa7e7542372eADc44A3aB1cDCaaA9Replaced by V2
EazPay V20x1CF26AAbb80Ba614672C37fde6A27D9de2566dc5Replaced by V3 (added emergency pause)

Contract Details

EazPay V3 (Payment Router)

The core payment contract. Handles:

  • sendPayment() — single P2P transfer with automatic fee deduction
  • batchSend() — atomic batch payments (used by /split)
  • Emergency pause — owner can pause all operations if needed

Key properties:

  • Operator can only call send functions (no arbitrary execution)
  • Fee rate read from TreasuryRouter (not hardcoded)
  • All-or-nothing batch execution

UserWalletFactory

Deploys one UserWallet contract per user:

  • Called on /start for new users
  • Deterministic addresses
  • Sets user as owner, bot as limited operator

UserWallet

Each user's individual smart contract wallet:

  • Owner: the user (full control, can withdraw anytime)
  • Operator: the bot (can only call send(), limited to daily cap)
  • Daily limit: 1,000 USDC/day (on-chain enforced)
  • No generic execute() — operator cannot make arbitrary calls

TreasuryRouter

Fee collection contract:

  • Current fee: 0.1%
  • Hard cap: 2% (on-chain, cannot exceed)
  • Routes fees to treasury address
  • Fee rate readable on-chain: feePercent()

Source Code


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?

No. Every user gets a smart contract wallet on Tempo L1. You are the owner. Your funds live on-chain, not on our servers. The bot's operator key has limited permissions (can only execute send() within a daily limit). See Wallet Architecture.

What tokens are supported?

USDC (TIP-20) on Tempo L1. You can bridge USDC from 8 other chains — see Bridge.

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, not formally audited by a third-party firm. Bug bounty is active — report vulnerabilities to contact@eazpay.xyz.


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.


Bridge

How long does a bridge take?

Depends on the source chain — typically 1–15 minutes. See Bridge for estimated times per network.

Can I bridge out of Tempo?

Currently, only inbound bridges are supported (other chains → Tempo). Outbound bridges are planned.