Run your own non-custodial market-maker bot on Cardano. Fill GeniusYield SLV orders by arbing against DexHunter and other external AMMs.
You hold the wallet seed. GeniusYield never sees your key.
License: MIT.
The fastest path from "zero" to a running bot — pick a cloud and click.
| Host | Setup time | Monthly cost |
|---|---|---|
| Railway (button above) | 2 min | $5–10 (free trial) |
Fly.io (fly launch) |
5 min | $0–5 |
| DigitalOcean App Platform | 5 min | $5+ |
| Self-hosted Docker | bring your own | bring your own |
Full deploy instructions for each: deploy/README.md.
You'll need:
- A fresh Cardano wallet (generate via the GY Bot Console or any wallet app)
- A free Blockfrost API key
- A DexHunter Partner API key
- A small amount of ADA + your trading-pair token to start (~$50 minimum recommended)
- Polls
GET /v1/orderbook/:base/:quoteevery N seconds. - Polls a pluggable AMM source for the current mid-price (Minswap V2 / SundaeSwap / WingRiders / DexHunter aggregate — your choice).
- For each order in the book, computes the spread vs the AMM mid.
- If any spread exceeds
MIN_PROFIT_BPSAND the size is above the order'sminFillAmount:- Calls
POST /v1/orderbook/fill-quotewith the wallet context + fill size. - Signs the returned unsigned tx CBOR with the local wallet.
- Calls
POST /v1/orderbook/fill-submitwith the signed witness + quote ID.
- Calls
- On 409
ORDER_UTXO_CONSUMED, drops the order and re-polls on the next iteration.
That's the whole arb. The complexity is in the AMM-side execution (which you also need to do, separately).
# 1. Clone + install
git clone <repo-url> genius-yield-fill-bot
cd genius-yield-fill-bot
yarn install
# 2. Copy and fill .env
cp .env.example .env
# Set WALLET_SEED, BLOCKFROST_API_KEY, BASE_ASSET_ID, QUOTE_ASSET_ID
# 3. Swap the AMM source for a real one
# Edit src/index.ts — replace MockAmmSource with your implementation.
# 4. Smoke-test (no on-chain submissions, just logs)
yarn build && yarn smoke
# 5. Go live
yarn build && yarn startsrc/ammSource.ts defines a 1-method interface:
export interface AmmSource {
getMidPrice(baseAssetId: string, quoteAssetId: string): Promise<AmmMidPrice>;
}You return { midPrice, source, timestamp }, where midPrice is a decimal string in quote per base direction (same as the v1 order-book price field). Three common ways to implement:
import type {AmmSource, AmmMidPrice} from './ammSource.js';
export class DexHunterAmmSource implements AmmSource {
constructor(private apiKey: string) {}
async getMidPrice(base: string, quote: string): Promise<AmmMidPrice> {
const res = await fetch('https://api-us.dexhunterv3.app/swap/estimate', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'X-Partner-Id': this.apiKey},
body: JSON.stringify({token_in: '' /* ADA */, token_out: assetIdToHex(quote), amount_in: 1, slippage: 1}),
});
const data = await res.json();
return {pair: {baseAssetId: base, quoteAssetId: quote}, midPrice: String(data.net_price), source: 'dexhunter', timestamp: new Date().toISOString()};
}
}import {MinswapAdapter} from '@minswap/sdk';
// Pull pool reserves, compute mid = reserveB / reserveA.class AvgAmmSource implements AmmSource {
constructor(private sources: AmmSource[]) {}
async getMidPrice(base: string, quote: string) {
const ps = await Promise.all(this.sources.map(s => s.getMidPrice(base, quote)));
const avg = ps.reduce((a, p) => a + Number(p.midPrice), 0) / ps.length;
return {pair: {baseAssetId: base, quoteAssetId: quote}, midPrice: String(avg), source: 'avg', timestamp: new Date().toISOString()};
}
}- Hot wallet only. Never put treasury funds or signing keys for serious capital in this bot. Use a wallet that holds only working capital you can afford to lose to bugs, RPC issues, or operator error.
- Cardano block time is ~20 s. Arb opportunities live for blocks, not seconds. Polling tighter than
POLL_INTERVAL_MS=5000mostly wastes API calls. - Slippage on the AMM side wipes out spreads. Always quote the AMM round-trip BEFORE quoting GY. Sample size matters: the AMM mid for
1 ADAis not the AMM mid for100 ADA. Production strategies model both sides. - Cancellations are common during volatile markets. Bot is built to handle 409 from both
fill-quoteandfill-submit— they're not errors, they're race signals. - Quote expires in 30 s. Wallet-signature latency must stay well under that. If you use a hardware wallet, expect quote expirations and code defensively.
- Per-fill costs are real. ~3–5 ADA fixed per fill across network fee, min-UTxO bonds, GY protocol fee, AMM batcher fee. Below ~4 ADA notional spread, no fill is profitable on Cardano regardless of pair.
- Same-block UTxO consumption is possible. Multiple takers race for the same order — Cardano's eUTxO model means only one wins per block. The loser gets a clean 409 and refunds the AMM side of their arb leg (if they sequenced correctly).
- Front-running. Cardano's mempool model is harder to front-run than EVM, but not impossible. If you see consistent 409s on your best opportunities, suspect a competing bot and consider a faster polling cadence + a smaller spread floor.
genius-yield-fill-bot/
├── src/
│ ├── index.ts # Entry: load config, wire deps, run loop
│ ├── config.ts # Env loading + validation
│ ├── gyClient.ts # v1 API client (orderbook + fill-quote + fill-submit)
│ ├── wallet.ts # Lucid Evolution wallet wrapper
│ ├── ammSource.ts # Pluggable AMM-mid-price interface + mock impl
│ ├── arb.ts # Arb math: decideAskFill / decideBidFill
│ └── loop.ts # Main poll → decide → fill loop
├── .env.example
├── package.json
├── tsconfig.json
├── LICENSE # MIT
└── README.md
- Doesn't manage the AMM side. Your bot needs to actually source the counter-asset on Minswap/SundaeSwap/etc. before submitting the GY fill. The example above only shows the GY-side mechanics.
- Doesn't include a strategy. The reference logic is "if spread > N bps, take maximum cap." Real fillers tier their size with profit curves, account for slippage, watch for cancellation cascades, etc.
- Doesn't include MEV protection. Standard Cardano caveats apply — see notes.
- Doesn't handle multiple pairs. Run one bot per pair as separate processes. Or fork and add a pair-router.
- The full architectural audit lives in
../AUDIT-autonomous-limit-order-crossfill.md. - v1 API contract:
../docs/limit-order-crossfill/v1-api-contract.md. - Min-fill economics:
../docs/limit-order-crossfill/min-fill-economics.md.