Your BTC-backed dollar account inside Telegram.
Borrow MUSD against Bitcoin on Mezo, get protected from liquidation automatically, and send, split and claim payments with anyone.
Bot · Mini App · Demo video (link coming soon) · Deck (link coming soon) · Testnet evidence
Bitcoin holders want dollars without selling their Bitcoin. Mezo's MUSD makes that possible: a fixed-rate loan against BTC. But using it today means a DeFi dashboard, a ratio to manage, and the risk of being liquidated or redeemed while you sleep.
Keel puts that whole account in the app people already use every day. You chat with a bot, review the details in a Mini App, and sign in your own wallet. Keel never holds your keys or your funds.
- Borrow MUSD against BTC from chat ("borrow 1800 at 160%") or from the Mini App.
- Before you sign you see the 0.1% fee, the fixed interest, your collateral ratio and your liquidation price.
- Unsafe loans are blocked before signing, not just flagged with a warning. This covers loans under the 1,800 MUSD minimum, under 110%, or under 150% while Mezo is in Recovery Mode.
- Repay, add collateral, or close the loan. Repayments follow Mezo's minimum-debt rule, so a transaction Mezo would reject can't be sent.
- Each loan lives in its own guardian contract (an EIP-1167 clone).
- You fund a small BTC (or MUSD) reserve and pick a trigger, for example "below 170%, bring me back to 180%".
- When the ratio drops below the trigger, anyone can call
protect(). Keel runs a keeper that does this. - A keeper can only move your reserve into your own loan, with a cooldown and a tip capped at 1%. It can't withdraw, borrow or redirect anything.
Keel watches three risks for you and sends early alerts:
- Liquidation: your ratio is near the 110% line.
- Redemption queue: Mezo redeems the lowest-ratio loans first, so Keel tells you your rank and how much MUSD is ahead of you.
- Recovery Mode: the system ratio has fallen below 150%.
Alerts use hysteresis, so you are not spammed. Every alert has one-tap buttons that open a ready-made review screen: add collateral, repay, fund the reserve, or turn on protection.
- Send to @username: works in chat or in the Mini App. Success is shown only after the transaction is confirmed on Mezo, and the recipient gets a Telegram notice.
- Bill splits: equal, custom or percentage shares, posted as a live card in a group or a DM, with Pay buttons and reminders. Each payment is matched on-chain, and the card updates as people pay.
- Group requests: "who owes what" in a group, with ✅/⏳ updating live.
- Escrowed claim links: pay someone who has no wallet yet. The MUSD is escrowed in
KeelPayand the link opens straight in the Mini App. The recipient claims in one step and Keel pays the gas. Unclaimed links refund to the sender after 72 hours, and the sender can cancel at any time. - Live account: balances and an activity feed that update in real time (server-sent events), plus one history across chat and the Mini App.
Also included, and working:
- Natural-language chat, for example "how safe is my loan?", "what if BTC drops 20%?" or "send 20 to @ada for lunch". It uses a deterministic parser with no LLM and no external calls, repeats the amount and recipient back before acting, and asks when something is missing.
- x402 premium alerts: pay-per-use near-real-time monitoring, paid in MUSD with a Permit2 signature through the Mezo x402 facilitator. Settlement was proven on testnet.
| Protection review | Guardian top-up | Payment received | Claim link, claimed |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
- Built for Telegram. You chat, review and sign without leaving Telegram, and you can pay anyone who has a Telegram account, even without a wallet.
- Protection that acts, not just alerts. Most tools tell you that you're about to be liquidated. Keel's guardian fixes the ratio on-chain, using only funds you set aside for that purpose.
- Honest about Mezo's rules. MUSD has a 1,800 MUSD minimum net debt and a 200 MUSD gas reserve, and repayments can't go below them. Keel builds those rules into every quote, repay screen and alert button, so you're never offered a transaction Mezo would reject. It also explains why: "this loan can't be partly repaid, close it instead".
- Payments make new users. Every claim link and split helps someone new start using MUSD, because they can claim without gas and without a wallet.
- Non-custodial by design. The server only ever creates intents. Every fund-moving action is signed by the user in their own wallet.
flowchart LR
U([Telegram user]) -- chat / buttons --> B[Bot · grammY]
U -- opens --> M[Mini App · React + wagmi]
B -- creates intent --> DB[(SQLite + Turso mirror)]
M -- reads intent · API with initData HMAC --> API[API · Hono]
API --- DB
M -- user signs in own wallet --> W{{User wallet<br/>WalletConnect}}
W -- tx --> G[KeelGuardian<br/>per-user clone]
W -- tx --> P[KeelPay<br/>escrow / claim links]
G -- openTrove / adjust / repay --> MZ[(Mezo MUSD<br/>BorrowerOperations · TroveManager)]
K[Keeper] -- protect --> G
R[Claim relayer · gas only] -- claim --> P
BW[Block watcher] -- reads blocks, 3 confirmations --> MZ
BW -- confirms sends, splits, claims --> DB
A[Alerts engine] -- ICR · redemption rank · Recovery Mode --> MZ
A -- alerts + one-tap buttons --> U
API -- SSE live updates --> M
X[x402 paywall] -- MUSD Permit2 --> F[Mezo x402 facilitator]
| Path | What lives there |
|---|---|
contracts/contracts/keel/ |
KeelGuardian, KeelGuardianFactory, KeelPay |
contracts/ |
Fork of mezo-org/musd: Keel's contracts are tested against the real protocol |
shared/ |
ABIs, chain config, deployments, risk maths (ICR, liquidation price, quotes, redemption rank), batched readers |
bot/ |
Telegram bot, HTTP API, alerts, keeper, claim relayer, block watcher, live updates, x402 premium, testnet scripts |
miniapp/ |
The Mini App: dashboard, borrow, protect, pay, splits, claim, activity, wallet linking, review-and-sign |
docs/ |
Deployment, status and testnet evidence, submission text |
- BTC collateral and MUSD loans. Loans are opened through Mezo's
BorrowerOperations, called from the user's guardian. Native BTC is sent asmsg.value, and the insert hints come fromHintHelpers.getApproxHintandSortedTroves.findInsertPosition. - Risk reads. Keel reads straight from
TroveManager,PriceFeed,InterestRateManagerandSortedTrovesusing batched JSON-RPC. That gives each loan's ratio, liquidation price, redemption-queue rank and the Recovery Mode check (TCR < 150%). - Protocol constants are respected:
- minimum collateral ratio 110%;
- critical (Recovery Mode) ratio 150%;
- 0.1% borrow fee;
- 1,800 MUSD minimum net debt;
- 200 MUSD gas compensation;
- fixed interest.
- MUSD as money. MUSD is the unit for loans, payments, splits, claim-link escrow and x402. x402 uses the token's ERC20Permit (EIP-2612) and Permit2.
Deployed contracts on Mezo testnet (chain 31611):
| Contract | Address |
|---|---|
| KeelGuardianFactory | 0x44AA5CBb78eA506Fd91FA7942d9326De29bF2E49 |
| KeelPay | 0xa0C53049202A655f5Ca5fE3131971C1B79cBEe29 |
| MUSD (Mezo) | 0x118917a40FAF1CD7a13dB0Ef56C86De7973Ac503 |
| BorrowerOperations (Mezo) | 0xCdF7028ceAB81fA0C6971208e83fa7872994beE5 |
| TroveManager (Mezo) | 0xE47c80e8c23f6B4A1aE41c34837a0599D5D16bb0 |
| PriceFeed (Mezo) | 0x86bCF0841622a5dAC14A313a15f96A95421b9366 |
The source of truth is shared/deployments/testnet.json (Keel) and shared/deployments/musd.json (Mezo).
- Keel never holds keys or funds. The bot creates intents, and the user signs every fund-moving transaction in their own wallet. The server marks an intent done only after it has checked the confirmed receipt against the user's linked wallet.
- Identity.
- Every API call is checked with Telegram
initDataHMAC. - A wallet is linked to a Telegram account by signing a single-use nonce, which expires after 10 minutes.
- Each user's data is isolated to their own account (audited).
- Every API call is checked with Telegram
- Guardian.
- Only the owner can take funds out.
protect()can only move reserve funds into the owner's own loan, subject to a cooldown, a tip of at most 1% and a target ratio of at most 500%.- Fund-moving functions are
nonReentrant. - Ownership transfer takes two steps.
- Claim links.
- The claim key exists only in the link. It is 43 characters of base64url so it fits Telegram's
startapplimit, and it goes straight to the Mini App without reaching the bot. - The claim signature binds the destination address, so neither the relayer nor a mempool observer can redirect the funds.
- The sender can cancel at any time, and expired links refund to the sender.
- The claim key exists only in the link. It is 43 characters of base64url so it fits Telegram's
- Payments are only reported once they are final. The block watcher waits 3 confirmations, uses a persisted cursor and a 30-block overlap, and deduplicates by event. Sends and splits are settled only once they are confirmed.
- Secrets (bot token, keeper, relayer and deployer keys, database tokens) exist only as host environment variables. They have never been in git, and the full history was scanned with gitleaks plus an exact-value search.
- Not audited. This is testnet software. Don't put funds you can't afford to lose into a mainnet deployment.
An honest view of what is done. Each testnet row has transaction hashes in docs/STATUS.md.
| Feature | Status |
|---|---|
| Borrow with confirmation card, fee, interest, ratio and liquidation price | ✅ Done, proven on testnet |
| Add collateral, partial repay that respects the Mezo minimum | ✅ Done, proven on testnet |
Guardian reserve and keeper protect() (159.9% → 179.9%) |
✅ Done, proven on testnet |
| Liquidation-risk and redemption-queue alerts with one-tap actions | ✅ Done, delivered live from real testnet data |
| Send MUSD to @username, recipient notice, confirmed-only success | ✅ Done, proven on testnet (17/17 checks) and between two phones |
| Escrowed claim links with gasless claim, cancel and refund | ✅ Done, proven on testnet and claimed on a phone |
| Bill splits and group requests with on-chain payment matching | ✅ Done, proven on testnet (29/29 checks, together with claim links) |
| Live balances, activity and history (SSE) | ✅ Done, running in production |
| x402 premium alerts paid in MUSD | ✅ Done, settlement proven on testnet |
| Natural-language chat (rule-based) | ✅ Done, running in the live bot |
| Recovery Mode alert | 🧪 Built and tested locally (can't be forced on a shared testnet) |
| Close loan (repay everything) | 🧪 Built; covered by contract tests, not yet run on testnet |
| Withdraw-collateral screen, savings in the Stability Pool | 🗓 Planned |
| Multi-keeper network, fiat off-ramp partners, mainnet after an audit | 🗓 Planned |
Requirements: Node 22+ and pnpm 10.
pnpm install
cp .env.example .env # names only; fill in your own values
cp miniapp/.env.example miniapp/.env.localOption A: everything on a local chain, using the real MUSD contracts.
cd contracts && KEEL_SOLCJS=1 pnpm dev:chain # terminal 1: local chain with seeded troves
cd bot && pnpm tsx scripts/e2e-local.ts # terminal 2: borrow → protect → send → claim, end to endOption B: the bot and the Mini App against Mezo testnet.
pnpm --filter @keel/miniapp dev # Mini App on :5173
pnpm --filter @keel/bot dev # bot + API + alerts + keeper + watcher on :8787 (long polling)Then, in BotFather:
- Run
/newbot. - Run
/newappand point it at yourMINIAPP_URL. It must be an HTTPS tunnel or host. - Optionally set a Direct Link short name and use it as
MINIAPP_SHORTNAME.
Every environment variable is listed, without values, in .env.example, bot/.env.example, miniapp/.env.example and contracts/.env.example.
- Contracts: run
cd contracts && pnpm deploy:keel:testnet. It writesshared/deployments/testnet.json. - Bot and API: Render web service (
render.yaml), using the SQLite store mirrored to Turso, with cron-job.org calling the/opstick endpoints. - Mini App: Vercel, using the root
vercel.json, withVITE_API_BASEandVITE_WC_PROJECT_IDset in the project.
The full step-by-step guide is in docs/DEPLOY.md.
pnpm --filter @keel/bot test # 224 tests: handlers, API, alerts, keeper, payments, splits, claims, watcher, NLU
pnpm --filter @keel/shared test # 25 tests: risk maths and readers
pnpm --filter @keel/miniapp test # 12 tests
cd contracts && pnpm test:keel # 24 tests: Keel contracts against the real MUSD stackTestnet end-to-end scripts are in bot/scripts/. They need funded testnet keys in .env.
| Script | What it covers |
|---|---|
payments-testnet.ts |
Sends, receives, history and notifications (17/17 checks) |
claims-splits-testnet.ts |
Claim links and bill splits (29/29 checks) |
claimlink-testnet.ts |
Claim-link escrow, claim, cancel and refund |
x402-testnet.ts |
Premium alerts paid over x402 |
plan-testnet.ts |
Mini App transaction plans |
- A withdraw-collateral screen in the Mini App, and Stability Pool savings.
- An open keeper registry, so any operator can run protection.
- Fiat on- and off-ramp partners for MUSD.
- A third-party audit, then mainnet.
Keel is released under the GPL-3.0 license.
contracts/ builds on the MUSD protocol by Mezo / Thesis (mezo-org/musd, GPL-3.0). Everything under contracts/contracts/ except keel/ is vendored unmodified, so that Keel's tests run against the real protocol.
Wallet logos in the Mini App come from the WalletConnect registry and belong to their owners.
See NOTICE.md for details. Built for the Mezo Buildathon.







