Skip to content

Repository files navigation

ORD/swap — Micro-Liquidity Swap: DOGE/LTC → BSV

Self-hostable swap service that lets anyone exchange Dogecoin or Litecoin for BSV — published open source so that anyone, anywhere, can run their own on-ramp. BSV is hard to buy in large parts of the world; one hosted swap cannot fix that, but a swap that anyone can operate in their own jurisdiction can.

No hosted instance. ORDnet does not operate a public swap service. Offering crypto exchange to the public is a licensed activity in the EU under MiCA — regardless of how small the amounts are — so this repository publishes the software only. Running it is up to you, in your own jurisdiction, under your own compliance. See Operator responsibility below.

⚠️ Upgrade from v3.0.0 immediately

v3.0.0 contained two critical, fund-affecting vulnerabilities: an unauthenticated SQL injection in order creation, and payouts released with zero confirmations. Both are fixed in v3.1.0. Do not run v3.0.0. Details: SECURITY-FIXES-v3.1.0.md.

v3.1.0 introduces two breaking changes — ORDSWAP_ADMIN_KEY is now mandatory, and the setup endpoints are loopback-only by default.

How it works

  1. A user creates a swap order and receives a unique DOGE or LTC deposit address — HD-derived per order (BIP32/BIP44), so concurrent payments can never be confused.
  2. The monitor watches the deposit address. Once the payment reaches the required number of confirmations and the received amount covers what is owed, the server pays out BSV from the operator's liquidity wallet.
  3. Orders persist in SQLite with refreshable /swap/:orderId URLs.

Security properties

  • Payouts wait for confirmations. Default 2 for both DOGE and LTC. An unconfirmed payment can still be replaced; releasing irreversible BSV against one is a double-spend invitation.
  • The received amount is verified against the amount owed, in integer atomic units, with a 1% tolerance for rounding. Short payments are held for review rather than paid out or discarded.
  • Every SQL statement is parameterised. No request data and no third-party API response is ever concatenated into SQL.
  • No plaintext keys at rest. The BSV liquidity WIF is stored AES-256-GCM encrypted (ORDSWAP_ENCRYPTION_KEY); deposit addresses are derived watch-only from xpubs — the deposit-side mnemonic can stay offline entirely (xpub-only setup, option C in the wizard).
  • Admin panel behind a mandatory key, compared in constant time, rate limited with lockout, and never exposed via CORS.
  • Payouts are claimed atomically. Two monitor runs cannot pay the same order twice, and a payout that fails halfway is never retried automatically — it waits for a human.

Run your own

npm install
export ORDSWAP_ENCRYPTION_KEY="$(openssl rand -hex 32)"export ORDSWAP_ADMIN_KEY="$(openssl rand -hex 32)"
npm start
# open http://localhost:3460/admin

The setup endpoints are loopback-only by default because they mint and accept seed phrases. To configure from another machine, tunnel:

ssh -L 3460:localhost:3460 your-server

Setup wizard: generate a wallet (write the seed phrase down!), import an existing mnemonic, or — recommended — enter xpubs only and keep keys offline. Then configure the BSV liquidity wallet, put a TLS proxy in front, and you are an on-ramp.

Configuration

Required:

VariablePurpose
ORDSWAP_ENCRYPTION_KEYAES key for WIF-at-rest encryption
ORDSWAP_ADMIN_KEYAdmin panel key, minimum 32 characters

Optional:

VariableDefaultPurpose
ORDSWAP_PORT3460Listen port
ORDSWAP_DB./ordswap.sqliteSQLite path
ORDSWAP_MIN_CONF_DOGE2Confirmations before a DOGE-funded payout
ORDSWAP_MIN_CONF_LTC2Confirmations before an LTC-funded payout
ORDSWAP_AMOUNT_TOLERANCE0.01Accepted underpayment fraction
ORDSWAP_MIN_USD / ORDSWAP_MAX_USD0.25 / 2.00Order size range
ORDSWAP_MARKUP2BSV price markup
ORDSWAP_EXPIRY_MINUTES30Order lifetime
ORDSWAP_ALLOW_REMOTE_SETUPunset1 opens setup endpoints beyond loopback
ORDSWAP_CORS_ORIGIN*CORS origin for public endpoints only
ORDSWAP_CREATE_LIMIT10Orders per IP per 10 minutes
ORDSWAP_READ_LIMIT240Public reads per IP per minute
ORDSWAP_ADMIN_FAIL_LIMIT5Failed admin auths per IP per 15 minutes
ORDSWAP_RATE_MAX_AGE900Seconds before exchange rates count as stale
ORDSWAP_MAX_BODY_BYTES65536Request body cap

API

Public:

  • GET /health
  • GET /rates
  • POST /swap/create{ amount_usd, pay_coin, bsv_wallet }
  • GET /api/swap/:orderId

Admin (X-Admin-Key header): /admin/config, /admin/liquidity, /admin/orders, /admin/stats.

Setup (X-Admin-Key header, loopback-only by default): /admin/setup/{status,generate,import,configure}.

HD derivation: DOGE m/44'/3'/0'/0/x · LTC m/44'/2'/0'/0/x.

Order statuses

statusmeaning
pendingwaiting for payment
seenpayment in the mempool, waiting for confirmations
processingconfirmed, payout being built
broadcastingpayout signed, txid recorded, broadcast in flight
completeddone
underpaidconfirmed but short — held for review, not paid
payout_failedpayout errored — held for review, not retried
expiredno payment in time

/admin/stats exposes a needs_review counter covering the four states that wait for a human. Watch it.

Tests

node test/security-test.mjs # 74 assertions
node --experimental-sqlite test/injection-poc.mjs # reproduces and closes K1

Both run on plain Node (>= 22.5 for the PoC) with nothing installed.

Operator responsibility

Running a swap service for the public is a licensed activity in many jurisdictions — money transmission, MiCA in the EU, and comparable regimes elsewhere. Thresholds are lower than people expect: in the EU there is no de-minimis exemption that makes a one-dollar swap unregulated.

This repository publishes software. Whether and how you may operate it is your own responsibility. Find out what applies to you before you point a domain at it — not after.

Security reporting: SECURITY.md.

Related

License

MIT © ORDnet / ODNCA — see LICENSE.

About

ORD/swap — self-hostable DOGE/LTC → BSV swap: HD-derived deposit address per order, encrypted keys at rest, xpub-only watch-only setup. Run your own on-ramp.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages