| ID Range | Type | Purpose |
|---|---|---|
| > 0 | User Accounts | Real Telegram users (ID = Telegram userId) |
| -1 | BOT_TREASURY | Collects fines, bail payments, system revenue |
| -2 | SYSTEM_RESERVE | Reconciliation adjustments, discrepancy handling |
| -3 | UNCLAIMED | Deposits with invalid/missing memo |
| -100 to -999 | Shared Accounts | Multi-user or shared wallets (current/future feature) |
| ≤ -1001 | Giveaway Escrow | One per giveaway: -1000 - giveawayId |
┌─────────────────────────────────────────────────────────────────┐
│ ON-CHAIN (Juno Blockchain) │
│ │
│ ┌───────────────────────┐ │
│ │ BOT_TREASURY_ADDR │ ← Holds ALL program funds │
│ │ (juno1...) │ Mnemonic in env; signs withdrawals│
│ └───────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
│ Deposits detected via RPC polling
│ Withdrawals signed & broadcast
▼
┌─────────────────────────────────────────────────────────────────┐
│ INTERNAL LEDGER (SQLite) │
│ │
│ ┌───────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ user_balances │ │ transactions │ │ users │ │
│ │ (userId, bal) │ │ full audit log │ │ id, role, name │ │
│ └───────────────┘ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Core principle: A single on-chain wallet backs all internal balances. The SQLite ledger is the source of truth for ownership accounting.
External Wallet ──JUNO──▶ BOT_TREASURY_ADDR
│
│ RPC polling detects tx
│ Memo parsed for userId
▼
┌──────────────────────┐
│ Valid userId memo? │
└──────────────────────┘
│ │
YES NO
│ │
▼ ▼
User balance credited UNCLAIMED (-3)
(admin can reassign)
User requests /withdraw
▼
┌────────────────────────┐
│ Balance check │
│ Transaction lock (mutex)│
└────────────────────────┘
│
▼
Internal balance debited
▼
On-chain tx signed & broadcast
▼
Funds arrive in user's external address
User A ──/send 10 @userB──▶ LedgerService.transferBetweenUsers()
│
▼
┌─────────────────┐
│ A.balance ≥ 10? │
└─────────────────┘
│
YES
│
▼
A.balance -= 10
B.balance += 10
transactions record created
(No on-chain activity. Instant. Free.)
/giveaway 100 (10 slots)
▼
Balance check
▼
Create giveaway (ID = 1)
▼
Create escrow account (ID = -1001)
▼
Transfer 100 → Escrow(-1001)
▼
┌─────────────────────────────────────────┐
│ GIVEAWAY ACTIVE │
│ Escrow -1001 holds 100 JUNO │
│ 10 slots × 10 JUNO each │
└─────────────────────────────────────────┘
│ │
│ User claims │ Cancel
▼ ▼
10 from Escrow → User Remaining → Funder
Violation detected
▼
User jailed (bail = X)
▼
/paybail or /bail @user
▼
Balance check (payer ≥ X)
▼
Payer.balance -= X
BOT_TREASURY.balance += X
User unjailed
| Operation | Who Can Do It | From | To |
|---|---|---|---|
| Deposit | Anyone (external) | On-chain | User / UNCLAIMED |
| Withdraw | Any user | User | External address |
| Transfer | Any user | User | User |
| Create Giveaway | Any user | User | Giveaway Escrow |
| Create Giveaway | Owner/Admin | Treasury/Self | Giveaway Escrow |
| Claim Giveaway | Any user (once) | Giveaway Escrow | User |
| Cancel Giveaway | Creator/Admin | Giveaway Escrow | Funder |
| Pay Fine | Any user | User | BOT_TREASURY |
| Pay Bail | Any user | User | BOT_TREASURY |
| Claim Unclaimed | Admin | UNCLAIMED | User |
| Reconciliation Adj | System only | SYSTEM_RESERVE | Any |
All debit operations enforce:
- Sufficient balance (
fromBalance >= amount) - No negative balances ever
- Execution-time revalidation (balance checked again during commit)
- Full audit via
transactionsrecords
Exception:
SYSTEM_RESERVE may temporarily go negative during reconciliation.
users # Account registry (positive=real, negative=system)
user_balances # Current balances
transactions # Full immutable audit log
giveaways # Metadata & state
giveaway_claims # Claim records
processed_deposits # Ensures idempotent deposit handling
transaction_locks # Prevents concurrent double-spend