KeelDocs
On this page

Wave 2 checklist

Each point of the Wave 1 judge feedback, with its status and the evidence behind it. Statuses:

  • Done and proven: shown on Mezo testnet or on the live site, with links.
  • Done, not yet proven on-chain: built and tested, but the on-chain run is still outstanding.
  • Not done: with the reason.

Commit links point to github.com/Im-2/keel.

#Judge pointStatus
1A web link judges can open without TelegramDone and proven
2protect() does a partial rescue instead of revertingDone and proven: 17 contract tests, and all three paths (full, partial, insufficient) run on Mezo testnet (see 2d)
3Verify the sources on the Mezo explorerDone and proven (7 of 7)
4A firmer revenue modelDone: protocol share live on-chain; KeelPro deployed; full model documented
5A pitch deckDone
6Real third-party users and protections from actual price movesTracking ready and honest; the users are not there yet
Evidence
Live URLkeel-miniapp.vercel.app: the landing page; Open web app goes to wallet sign-in; demo mode with sample data; docs; stats
How it worksA signed single-use nonce gives a 12-hour session holding only the wallet address; the account is resolved server-side on every request. See Web mode
Testsbot/test/web-auth.test.ts: 15 tests covering nonce message, replay, wrong signer, nonce for another address, expiry, logout, isolation (client-supplied ids and addresses ignored), initData precedence, Telegram-only routes refused, intents for web users, the Telegram link and merge, and unlinking ending sessions. All pass (bot suite: 242/242)
End to endA real wallet signed in through the browser (injected test wallet) and reached the dashboard with its own data. In a fresh incognito Chrome: / shows the landing page, #/app the sign-in, ?demo=1 the demo, /stats the stats, and Telegram launch data still goes straight to the app
Link QAPlaywright clicks every landing link at 375px and 1440px against production; see docs/QA.md
Commits097b340 web mode · 2bb1075 landing and stats · 248fc44 clickable cards · e3f129d docs site

Limitation: alerts are Telegram messages. A web-only account must use Connect Telegram to receive them.

#2. Partial rescue instead of revert

#2a. What Mezo allows (read from the MUSD source)

In BorrowerOperations._adjustTrove:

  • Normal Mode requires the resulting ICR to be at least 110% (and TCR at least 150%) for any adjustment.
  • Recovery Mode allows a pure top-up and/or repayment at any ratio.
  • A repayment must also leave net debt at or above minNetDebt.

So a top-up that can't lift a loan back over 110% is rejected by Mezo itself, not by Keel.

#2b. The change: done

KeelGuardianV2.protect() returns a Result:

  • Full (0) and Partial (1): funds move. Partial adds PartialRescue(…, btcShortfall).
  • Insufficient (2): when nothing Mezo would accept is affordable, it moves nothing, pays no tip, leaves the cooldown free, doesn't revert, and emits InsufficientReserve(icr, btcShortfallToMCR, btcShortfallToTarget, recoveryMode).

BTC and MUSD are applied in one adjustTrove. previewProtect() returns the plan without sending anything. See Guardian protection.

#2c. Proven in tests against the real MUSD stack: done

contracts/test/keel/KeelGuardianV2.test.ts, 17 passing (41 Keel contract tests in total):

  • factory v2 clones a V2 implementation deterministically, once per owner;
  • hard-caps the protocol share at 50% of the tip, and only the admin can change it;
  • transfers admin in two steps;
  • full rescue: restores the target and pays the keeper its tip minus the treasury's 20% share;
  • pays no protocol share when the treasury is unset;
  • partial rescue: applies the whole reserve, emits PartialRescue with the exact shortfall, and the shortfall really completes it;
  • combines BTC and MUSD in one adjustment when BTC alone would leave the loan under 110%;
  • impossible rescue (Normal Mode, below 110%): moves nothing, pays no tip, keeps the cooldown free, and reports the BTC needed to clear 110%. The same test shows Mezo itself rejecting the sub-110% top-up;
  • respects MUSD's minimum net debt: a minimum-size loan can only be repaid down to the minimum;
  • Recovery Mode: allows a partial top-up even if the loan stays under 110%;
  • treats a dust reserve as nothing to do (no revert, no tip);
  • an insufficient result can repeat (no cooldown); a rescue can't (cooldown applies);
  • at the maximum (1% tip, 50% share) the owner pays exactly 1% of what was applied;
  • a keeper contract can't re-enter protect() through its tip;
  • only the owner controls funds and settings;
  • it still reverts when not eligible: disabled, no trove, healthy;
  • KeelPro takes MUSD straight to the treasury, stacks time, and caps the price.

The bot side is also tested: the "reserve can't fully protect your loan" and "your guardian can't act yet" alerts (with the exact BTC plus 2%), and the partial-rescue notice (bot/test/alerts-keeper-api.test.ts).

#2d. v2 on Mezo testnet: done and proven (full, partial, insufficient)

Deployed and verified (deploy transactions):

The run: bot/scripts/guardian-v2-testnet.ts, with a fresh wallet 0xf44c91FF…3001 (a team test wallet, so /stats doesn't count it as a user). It went through the production code paths:

  • The loan, reserve and settings use the Mini App's own transaction plans.
  • protect() is signed by the production keeper wallet's key (the script calls it directly, the same transaction the running keeper sends; the running keeper process itself did not make these calls).
  • The "add X BTC" message comes from the bot's real alert engine reading the real chain.

The guardian is 0xE7f4B337…B3D6, a KeelGuardianV2 clone made by factory v2, with a loan of 1,800 MUSD (BTC at $64,230 on Mezo's oracle).

StepTransaction
Create the guardian (factory v2)0xf1cf33a6…
Open the loan (1,800 MUSD at 150%)0x0b1a3186…
Protection settings (trigger 165%, target 175%, no cooldown for the test)0xc0376e37…
RescueTransactionWhat the chain shows
(c) Insufficient (reserve empty, ratio 150.06% under the 165% trigger)0xaf122e65…protect() did not revert and returned Insufficient. It emitted InsufficientReserve(icr 150.06%, btcShortfallToMCR 0, btcShortfallToTarget 0.007785 BTC, recoveryMode false). No Protected event; no funds, tip or fee moved.
(b) Partial (reserve ≈ 15% of the shortfall: 0xf2dcd323…)0x4dd138e6…Returned Partial. Protected added 0.001153 BTC: ratio 150.14% → 153.84%. PartialRescue reports 0.006601 BTC still missing to the 175% target. Tip paid and ProtocolFeePaid 0.00000046 BTC to the treasury
(a) Full (trigger and target lowered to 156% and 157% so the reserve could finish the job: settings, then the reserve topped up by exactly the reported shortfall +2%: fundReserve)0x00f038c1…Returned Full. Protected added 0.000855 BTC: ratio 154.25% → 157.00%, exactly the target. No PartialRescue. ProtocolFeePaid 0.00000034 BTC to the treasury

The owner is told how much to add (the bot's real alert engine reading this loan after step (c)):

🚨 Your guardian can't act yet. Your ratio is 150%, under your 165% trigger, and the reserve (0 BTC) is too small for a top-up Mezo would accept. Add 0.00795 BTC to the reserve and the next keeper check rescues it.

The BTC figure is the reported shortfall plus 2%, rounded up. The alert's own code path (message plus a one-tap fund-reserve button) is covered by bot/test/alerts-keeper-api.test.ts.

The first protocol revenue: the two rescues paid 0.0000008 BTC to the treasury through ProtocolFeePaid, exactly 20% of each keeper tip (tip 0.2% of the amount applied). It's tiny, because this is a test with a few thousandths of a BTC, but it shows the on-chain split working.

On /stats (tick "include the Keel team's own and test wallets"): all three runs appear, with the ratio before and after, and the revenue panel shows the protocol fees.

What this does not show. The Insufficient run happened above 110%, because the wallet could afford only a loan at 150%. It proves the no-revert path, the event and the shortfall, but not the sub-110% case where Mezo would reject a top-up. That case is proven in the contract tests (which show Mezo rejecting the sub-110% top-up), because forcing a real loan below 110% on a shared testnet means moving the oracle price.

#2e. What testnet already shows (v1): proven

v1 guardians on testnet, read on-chain by /stats. All four are team wallets:

TransactionResultRatioBTC priceTrigger
0x297323ed…full160.0% → 180.0%$81,168owner (trigger set above the ratio)
0x50e028be…full160.0% → 180.0%$80,500owner
0x343eaafc…partial169.9% → 170.2%$76,248BTC price move
0xabba0640…partial146.3% → 146.4%$65,477BTC price move

The two price-triggered protections were real market moves, and they were partial because those reserves were nearly empty. That is exactly the case v2 now reports explicitly, with the BTC to add (see 2d for the v2 runs).

#3. Verified sources on the Mezo explorer: done and proven (7 of 7)

Each page shows "Contract source code verified (exact match)": solc 0.8.24+commit.e11b9ed9, optimizer on with 100 runs, EVM london, GPL-3.0. Each was confirmed through the explorer API (is_verified: true).

ContractVerified pageScreenshot
KeelGuardianFactoryV20x85579beE…79E1factory-v2.png
KeelGuardianV2 implementation0xCCF76173…E8eFguardian-v2-impl.png
KeelPay v20x3777b89a…740dkeelpay-v2.png
KeelPro0x59b1CEB9…8654keelpro.png
KeelGuardianFactory v10x44AA5CBb…2E49factory-v1.png
KeelGuardian v1 implementation0x07F411F2…445Dguardian-v1-impl.png
KeelPay v10xa0C53049…EE29keelpay-v1.png

How: contracts/scripts/verify-blockscout.py, which submits the standard-JSON input to Blockscout's verification API. Commit 297535e.

#4. A firmer revenue model: done

  • On-chain today: a protocol share of every keeper tip goes to the treasury.

    • It is 20% now, hard-capped at 50% in both KeelGuardianFactoryV2 and KeelGuardianV2.
    • The owner never pays more than the tip, which is itself capped at 1%.
    • Every payment is a ProtocolFeePaid event, shown in the /stats revenue panel.
    • Collected so far: 0.0000008 BTC, from the two v2 rescues in 2d (20% of each tip). It's tiny because it's a test.
  • KeelPro subscription contract: deployed and verified. It pays MUSD straight to the treasury and the price is hard-capped. The Pro features in the app aren't built yet.

  • Documented: Business model covers:

    • target users;
    • market sizing with stated assumptions;
    • unit economics and a sensitivity table;
    • five streams: tip share, Pro, merchant checkout at 0.3–0.5% (P2P stays free), team and DAO plans, and Guardian API licensing.

    It is on slide 10 of the deck.

  • Commits: 8c91e7f · 77d887b.

#5. A pitch deck: done

  • 12 slides: problem, solution, live screenshots, architecture, the Mezo/MUSD integration (contracts and functions), the Guardian and keeper explained visually, payments, traction (real numbers from /api/stats), business model, roadmap, team.
  • Where: PDF · PPTX, also in submission/ and docs/keel-deck.pdf.
  • Source: submission/deck/ (deck.html, build.mjs, build-pptx.py). Commit d20601b.

#6. Real third-party users and price-triggered protections: tracking ready; users not there yet

  • /stats reads everything on-chain:

    • guardians on every factory;
    • owners and open loans;
    • every protection, with the BTC price at that block, the ratio before and after, and whether a price move or the owner's own change triggered it;
    • revenue.

    Team and test wallets (listed in shared/deployments/testnet.json → teamWallets, plus the keeper and treasury) are excluded by default, with a toggle to include them.

  • Today: 0 guardians from outside the team. All 3 guardians, all 7 recorded protection attempts (4 v1, 3 v2) and the v2 test wallet belong to the team.

  • For testers:

    • the web app and demo mode remove the Telegram requirement;
    • Set to test on the Protection screen puts the trigger one point under the current ratio, so a real oracle dip triggers the keeper;
    • Quick start walks through it.
  • Recruiting testers is the team's job and isn't done yet.

Edit this page on GitHub · Keel is testnet software, not audited.