Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ jobs:

- name: Set up pnpm
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
version: 8

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
Expand Down
22 changes: 22 additions & 0 deletions examples/market-maker-bot/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
VITE_NETWORK=testnet
VITE_API_URL=
VITE_API_KEY=

# Testnet/demo only. Values in VITE_* env vars are exposed to browser code.
VITE_WALLET_SEED=

VITE_PAIR=HUG/ML
# Use the Mintlayer token id for tokens. Use Coin only for ML.
VITE_BASE_TOKEN=token_id_for_HUG
VITE_QUOTE_TOKEN=Coin
VITE_ORDER_SIZE=0.01
VITE_SPREAD_BPS=20
VITE_INVENTORY_TARGET=0.5
VITE_REBALANCE_THRESHOLD=0.1
VITE_MAX_POSITION=1.0
VITE_MAX_ORDERS=10
VITE_POLL_INTERVAL_MS=15000
VITE_MAX_UNCONFIRMED_BRANCH_DEPTH=24
VITE_ALLOW_MAINNET_BROADCAST=false
VITE_ENABLE_FILL_TRADING=true
VITE_ALLOW_SELF_FILLS=false
1 change: 1 addition & 0 deletions examples/market-maker-bot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
47 changes: 47 additions & 0 deletions examples/market-maker-bot/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Market Maker SPA Architecture

## Overview

The app is a browser-run market-maker control panel and execution surface. It uses React for visualization and control, while the SDK performs all wallet, transaction, signing, broadcast, and chain/order queries.

```mermaid
flowchart LR
user["User"] --> ui["React SPA"]
ui --> hook["useMarketMakerBot"]
hook --> sdkClient["SDK Client"]
hook --> strategy["Strategy Engine"]
hook --> execution["Execution Queue"]
execution --> walletState["WalletState"]
execution --> branches["UTXO Branch Manager"]
sdkClient --> api["Mintlayer API"]
walletState --> graph["Chain Visualizer"]
branches --> graph
```

## State Model

- SDK state: client instance, network, addresses, balances, available orders, and account orders.
- Strategy state: configured pair, spread, order size, max orders, inventory target, max position, and circuit breaker status.
- Execution state: draft, signed, broadcasted, mempool, confirmed, rejected, and rebuild-required transaction records.
- Wallet state: local transaction log persisted in the browser and interpreted by SDK `WalletState`.
- Branch state: branch labels over wallet UTXOs, unconfirmed depth, remaining depth budget, and reservation status.

## Transaction Lifecycle

1. A strategy or branch action creates an execution request.
2. The execution queue signs/builds the transaction through the SDK.
3. The transaction is inserted into `WalletState` as local so its inputs are reserved.
4. If broadcasting is enabled, `broadcastTx()` submits it and the local record moves to mempool.
5. If broadcast fails, the transaction is marked rejected and dependent local transactions require rebuild.
6. Chain polling reconciles visible orders, balances, and transaction state where the SDK exposes enough information.

## UTXO Branch Policy

- The app warns before any branch approaches the network mempool chain limit of 30.
- The default branch spend policy allows only wallet-created unconfirmed outputs and caps depth below 30.
- Branch preparation creates multiple self-transfer outputs from a selected funding asset and address.
- Strategy execution prefers the branch with the most remaining depth budget and no local reservation.

## Browser Storage

The first implementation uses a localStorage-backed `WalletTxStore` because it is dependency-free and easy to inspect. The storage adapter is isolated so it can be replaced with IndexedDB without changing wallet logic.
38 changes: 38 additions & 0 deletions examples/market-maker-bot/PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Market Maker SPA Implementation Plan

This file is the workspace handoff copy of the approved plan. Keep it current when the implementation changes so another agent can continue without reading the chat.

## Scope

- Build `examples/market-maker-bot` as a Vite React TypeScript SPA.
- Use `@mintlayer/sdk` from the pnpm workspace.
- Run the first bot implementation in the browser with `MnemonicAccountProvider`.
- Treat browser mnemonics as testnet/demo-only and keep mainnet broadcasting disabled by default.
- Visualize wallet state, orders, proposed strategy actions, pending transactions, broadcasted transactions, rejected transactions, and UTXO transaction chains.
- Include a guarded special-purpose UTXO preparation workflow so order transactions can be spread across branches instead of extending one mempool chain toward the 30-transaction limit.

## SDK Constraints

- Use existing order methods: `getAvailableOrders()`, `getAccountOrders()`, `createOrder()`, `fillOrder()`, `concludeOrder()`, and `broadcastTx()`.
- The SDK exposes a flat order list, so the app builds a synthetic book for the configured pair.
- Use exported wallet-state primitives from `@mintlayer/sdk` for local transaction lifecycle, balance derivation, and unconfirmed chain depth.
- The browser app can only recover state from local transaction persistence plus chain polling available through the SDK.

## Milestones

1. Scaffold the Vite React app and workspace package.
2. Implement SDK mnemonic initialization and testnet guardrails.
3. Add browser transaction persistence and wallet-state derived views.
4. Implement pair order polling and synthetic book visualization.
5. Implement dry-run strategy proposals.
6. Add guarded transaction execution and broadcast tracking.
7. Add UTXO branch preparation and branch-aware transaction selection.
8. Run builds/tests and update `PROGRESS.md`.

## Safety Defaults

- `VITE_NETWORK=testnet`.
- Broadcasts are disabled until explicitly enabled in the UI.
- Mainnet auto-broadcast requires an explicit environment override.
- Max unconfirmed branch depth defaults below 30 to preserve recovery room.
- Mnemonics are never logged or persisted by the app.
40 changes: 40 additions & 0 deletions examples/market-maker-bot/PROGRESS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Market Maker SPA Progress

## Current Status

- The Vite React TypeScript SPA implementation is complete for the approved first version.
- The app builds successfully with `pnpm --filter market-maker-bot build`.
- The repository now pins `pnpm@10.15.0` for Node 20 compatibility.
- Token balances are shown by SDK `token_id`; configured token values must use token ids rather than tickers.

## Completed

- Created persistent planning memory in `PLAN.md`.
- Created this progress log.
- Added `ARCHITECTURE.md` with the app state model and UTXO branch policy.
- Updated `Readme.md` with installation, env vars, usage, structure, and safety notes.
- Scaffolded a workspace Vite React app with `@mintlayer/sdk`.
- Implemented browser mnemonic SDK initialization with default testnet guardrails.
- Implemented localStorage-backed `WalletState` persistence and wallet snapshots.
- Implemented synthetic orderbook construction from SDK order lists.
- Implemented inventory-aware dry-run strategy proposals.
- Implemented a transaction execution queue for build, sign, local reserve, broadcast, and rejection tracking.
- Implemented UTXO branch visualization and guarded branch preparation requests.

## In Progress

- No implementation task is currently in progress.

## Next

- Manual test with a funded testnet mnemonic and broadcast disabled first.
- Fix the existing SDK TypeScript error in `packages/sdk/src/mintlayer-connect-sdk.ts` before relying on `pnpm build:sdk` as a green verification step.

## Verification Log

- `pnpm --filter market-maker-bot build` passed.
- `pnpm install` passed and repaired workspace dependency links.
- `pnpm build:sdk` failed in existing SDK code at `packages/sdk/src/mintlayer-connect-sdk.ts` where `params.token_id` is `string | undefined` but the HTLC build params require `string`.
- User-reported pnpm 11 failure on Node 20 is an environment/toolchain mismatch; pnpm 11 requires Node 22.13+ and imports `node:sqlite`.
- Token balance display was clarified: the SDK returns `sdkBalances.token` as `Record<token_id, number>`, so `VITE_BASE_TOKEN=HUG` will display zero unless `HUG` is the actual token id.
- Order book fetching now uses `/order/pair/{tokenId}_TML` for token/ML pairs and ignores zero-balance orders when building bids/asks.
217 changes: 217 additions & 0 deletions examples/market-maker-bot/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# Market Maker Bot (UTXO Network)

## Overview

This project is an automated market-making bot designed for a UTXO-based blockchain network.
Unlike account-based systems (e.g. Ethereum), this bot operates on a UTXO model, meaning liquidity management, order tracking, and state reconstruction are derived from unspent transaction outputs.

The bot relies on an existing blockchain SDK to interact with the network, construct transactions, and query on-chain state.

Its primary goal is to provide continuous liquidity on selected trading pairs by maintaining bid/ask orders and dynamically rebalancing inventory.

---

## Key Features

- Automated market making on UTXO-based DEX / orderbook
- Inventory-aware quoting (risk-adjusted spread)
- Real-time order book tracking via SDK
- UTXO-aware balance management (no account abstraction)
- Auto rebalancing between assets in a pair
- Configurable spread, depth, and order size
- Fail-safe order cancellation / replacement logic
- Stateless recovery from blockchain data (rebuild from UTXO set)

---

## Architecture

### 1. Chain Layer (SDK)
Responsible for all blockchain interactions:
- Fetching UTXOs
- Broadcasting transactions
- Querying order book / DEX state
- Address management

> All interactions go strictly through the provided SDK.

### 2. Strategy Layer
Implements market-making logic:
- Spread calculation
- Mid-price estimation
- Inventory exposure control
- Order placement / cancellation decisions

### 3. Execution Layer
Handles:
- Transaction construction via SDK
- Signing & broadcasting
- Retry logic
- Conflict resolution (UTXO double-spend handling)

---

## Requirements

- Node.js >= 20
- pnpm 10.x when using Node 20. pnpm 11 requires Node 22.13+ because it uses `node:sqlite`.
- Access to the UTXO network SDK
- Running API server
- Valid wallet keys for signing transactions

---

## Installation

From the repository root:

```bash
corepack prepare pnpm@10.15.0 --activate
pnpm install
pnpm build:sdk
pnpm --filter market-maker-bot dev
```

The app is a Vite React SPA and runs entirely in the browser. It consumes `@mintlayer/sdk` from the current pnpm workspace.

If your shell still resolves to pnpm 11 on Node 20, run the command through the pinned version:

```bash
npx pnpm@10.15.0 --filter market-maker-bot build
```

---

## Configuration

Create a `.env` file:

```env
VITE_NETWORK=testnet

VITE_API_URL=
VITE_BATCH_API_URL=
VITE_API_KEY=

VITE_WALLET_SEED=your-testnet-mnemonic

VITE_PAIR=HUG/ML
VITE_BASE_TOKEN=token_id_for_HUG
VITE_QUOTE_TOKEN=Coin
VITE_ORDER_SIZE=0.01
VITE_REFERENCE_PRICE=1
VITE_SPREAD_BPS=20
VITE_INVENTORY_TARGET=0.5
VITE_REBALANCE_THRESHOLD=0.1

VITE_MAX_POSITION=1.0
VITE_MAX_ORDERS=10
VITE_MAX_UNCONFIRMED_BRANCH_DEPTH=24
VITE_ALLOW_MAINNET_BROADCAST=false
VITE_SIMULATE_OWN_FILLS=false
VITE_SIMULATION_TRADE_TIMEOUT_MS=60000
```

Important: a `VITE_WALLET_SEED` value is bundled into browser code. Use this only for testnet/demo wallets. Production unattended bots should keep signing keys outside the browser.

Token configuration uses SDK currency ids. Set `VITE_BASE_TOKEN` / `VITE_QUOTE_TOKEN` to `Coin` for ML or to the actual Mintlayer token id for tokens. Tickers such as `HUG` are display labels only and will not match balances returned by `client.getBalances()`.

`VITE_API_URL` supplies balances and market data. `VITE_BATCH_API_URL` must point to the Mojito-compatible `/batch` service for the *same chain/indexer*, because it supplies the UTXOs used to fund transactions. When using a custom API server, set both; otherwise the bot can display a large balance from one source while assembling transactions from another source's small UTXO set.

---

## Usage

### Start bot

```bash
pnpm --filter market-maker-bot start
```

### Development mode

```bash
pnpm --filter market-maker-bot dev
```

---

## Market Making Logic

The bot continuously:

1. Fetches latest order book via SDK
2. Computes mid price
3. Calculates bid/ask spread
4. Evaluates current inventory (UTXO-based balances)
5. Places or updates orders accordingly
6. Cancels stale or unfilled orders
7. Rebalances assets if exposure exceeds threshold

### UTXO-specific behavior

- Balance is derived from UTXO aggregation, not account balance
- Orders consume UTXOs when executed
- Partial fills may result in fragmented UTXO sets
- The bot maintains a local UTXO index
- State reconciliation happens on every cycle

---

## Safety Mechanisms

- Duplicate UTXO detection before transaction broadcast
- Idempotent order placement
- Automatic recovery after crash
- Circuit breaker for abnormal spreads
- Max exposure limits per asset
- Browser transaction state is persisted locally and interpreted through SDK `WalletState`
- UTXO branch depth is visualized and capped below the 30-transaction mempool chain limit
- Mainnet auto-broadcast is disabled unless explicitly overridden

---

## Current Implementation Status

- React SPA scaffold, SDK initialization, and testnet mnemonic mode are implemented.
- Wallet, balance, local UTXO, orderbook, own order, strategy proposal, transaction queue, and UTXO branch panels are implemented.
- Strategy proposals run as dry-run by default. Signing a preview does not reserve UTXOs; only a broadcast attempt reserves its selected inputs while it is pending.
- The Liquidity Simulation panel can drive a testnet lifecycle of quote, self-fill, and conclude/requote. It is disabled by default and runs only while the loop is active, Dry run is off, and broadcasting is enabled.
- UTXO branch preparation is guarded and warns when multiple branch preparation transactions should be prepared one at a time.
- Production unattended signing should move out of the browser before mainnet use.

---

## Project Structure

```text
examples/market-maker-bot/
├── PLAN.md
├── PROGRESS.md
├── ARCHITECTURE.md
├── package.json
├── vite.config.ts
├── index.html
├── src/
│ ├── App.tsx
│ ├── main.tsx
│ ├── components/
│ ├── hooks/
│ └── lib/
```

The bot logic is split into SDK client setup, strategy calculation, execution queue, wallet transaction state, and UTXO branch management so it can later be moved to a headless runner if needed.

Pair orders are fetched from `/order/pair/{tokenId}_TML` for token/ML markets instead of filtering the global `/order` list client-side.

---

## Disclaimer

Use at your own risk. Test on testnet first.

---

## License

MIT
12 changes: 12 additions & 0 deletions examples/market-maker-bot/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Mintlayer Market Maker Bot</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading
Loading