Introduction
Limit and stop orders for SaucerSwap V2 on Hedera, with no keeper bot. A Scaffold-HBAR template: Foundry contracts and a Next.js frontend for Hedera testnet.

Live on testnet: a 0.1 DAI stop-loss placed in the browser, filled by the Schedule Service with no bot, and its trail on the order page — every step links to HashScan.
You escrow tokens in OrderVault together with a small HBAR budget. The vault mints you an HTS NFT that is the order, and asks the Hedera Schedule Service to call it back. From then on the network itself runs a sweep over each market. Each sweep waits as long as the price needs to reach the nearest trigger, and fills orders whose trigger is met, but only while a guard confirms the pool's TWAP agrees with Chainlink. Nothing off-chain has to stay online.
| Hedera service | What it does here |
|---|---|
Schedule Service, 0x16b (HIP-1215) | The vault schedules each market's next sweep itself and pays for it from order budgets. No bot, no cron. |
Token Service, 0x167 | Each order is an NFT in a collection the vault controls. Whoever holds the NFT can cancel and receives the fill; the vault wipes it at settlement. |
Exchange rate, 0x168 | Converts gas priced in USD cents to tinybar, so budgets follow the live HBAR rate. |
| Mirror node | The frontend reads order history, NFT holdings and associations from it; no indexer to run. |
See it work on testnet
The vault is 0.0.10792085 (0xba9c496d229b9868a804c2bcfbeac97e2bad1b1d) and its order NFTs are 0.0.10792086. Every row below was executed on testnet and is checked on the mirror node by node scripts/gate-check.mjs --proofs-only. "Scheduled" means the transaction was run by the Schedule Service, not sent by anyone.
| What happened | Transaction |
|---|---|
| Stop-loss filled by the Schedule Service. Order #5 (sell 0.015 DAI at or below 1.0000) placed, then filled on the next scheduled sweep: 0.015026 USDC out against a 0.014849 minimum, Chainlink 0.99999, pool TWAP 1.00230 | placed 1790771452.123758104, filled (scheduled) 1790771751.009135530 |
| The same through the frontend. Order #7 placed on the Trade page with a browser wallet (0.1 DAI stop-loss), filled by the next scheduled sweep: 0.100175 USDC out against a 0.099498 minimum | placed 1790772299.588606025, filled (scheduled) 1790772599.101466208 |
| Guard refuses a manipulated-looking pool. Order #6's trigger was met, but the HBAR/USDC pool TWAP (2.0179) was ~18x Chainlink (0.1087), so the fill was held; the retry backed off from 5 to 10 min | held (scheduled) 1790771913.055173208 and 1790772511.027870104 |
| Orders share a sweep. One scheduled sweep charged three open orders (#1, #2 and #5) in a single run, filling #5 and checking #1 and #2, so each paid a fraction of the fixed scheduling cost | (scheduled) 1790771751.009135530 |
| Budget runs out, top-up resumes checks. Order #8 was checked until its budget reached the reserve; a scheduled sweep parked it and scheduled nothing further. A 5 HBAR top-up revived it, and the next scheduled sweep charged it again | parked (scheduled) 1790775946.023710208, top-up 1790776029.788091104, resumed (scheduled) 1790776544.055239569 |
| Cancel by the NFT holder. Order #6 cancelled by its holder; 1 HBAR of escrow and 9.24 HBAR of unused budget refunded | 1790772562.437845316 |
The order follows its NFT. Order #4's NFT was transferred to another account; the original owner's cancel would revert with NotHolder, and the new holder cancels and receives the refund | transfer 1790771563.811651187, cancel by the new holder 1790771572.723588640 |
Keeperless recovery when the network is congested. A third party saturated the Schedule Service's per-second gas capacity, so a scheduled sweep could not rebook and the market stalled; anyone can heal it, and a third account's permissionless restartSweep resumed the chain | stalled (scheduled) 1790777291.060748804, restarted by a third account 1790777389.880901398 |
The frontend ships pointed at this vault, so yarn next:dev works without deploying anything. It runs the exact contracts in this repo — every fix included — and was endowed with a payer float so its keeper stays funded; a residual stall is recoverable by anyone with restartSweep.
From README.md at v1.1 (6ddd9a3) · View source