Skip to content

feat: Implement Skywire Decentralized Exchange (Exchange Market & Client)#3434

Merged
mrpalide merged 73 commits into
skycoin:developfrom
mrpalide:feat/exchange
Jul 20, 2026
Merged

feat: Implement Skywire Decentralized Exchange (Exchange Market & Client)#3434
mrpalide merged 73 commits into
skycoin:developfrom
mrpalide:feat/exchange

Conversation

@mrpalide

@mrpalide mrpalide commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Did you run make format && make check? Yes

Overview
This PR is the Skywire half of the SkyDEX decentralized-exchange ecosystem: two thin visor apps — skydex-market and skydex-client — that carry the exchange over a Skywire dmsg transport using the visor's app networking. The exchange engine itself — the dmsg protocol, SQLite state, escrow, background jobs, local-signed blockchain settlement, the operator UI, and the React trading client — now lives in the skycoin repo (src/skydex/*, cmd/skydex-*) and carries zero skywire dependency. See the companion PR skycoin/skycoin#. This mirrors how skywire wraps skycoin-web: domain logic in skycoin, networking as a drop-in skywire reconfiguration.

The market sells SKY and any operator-added Skycoin fibercoin — each coin is add/edit/enable/disable/delete-able with its own fullnode + escrow hot wallet — against external UTXO coins (BTC/LTC) or another fibercoin. Users are identified solely by their Skywire public key. Payments are auto-verified against the recipient wallet — no transaction hash required from users: sell-coin/fibercoin transfers by sender address + time window + fixed amount, external UTXO payments (BTC/LTC) by a unique non-round amount + time window. Sell-coin delivery and refunds are signed locally from an operator seed and broadcast via that coin's Skycoin-family node (the node's wallet API is never used).

Repo split

  • skycoin/skycoin (companion PR)src/skydex/{protocol,message,db,chain,jobs,walletaddr,server,market} (the engine) + cmd/skydex-market / cmd/skydex-client (standalone TCP reference commands, operator/auth UI, React client). No skywire import.
  • skywire (this PR)cmd/apps/skydex-{market,client}: the transport wrapper that Listens/Dials over appnet dmsg, injects the authenticated public key as identity, and hands a net.Listener / net.Conn to the engine. Plus app registration in config-gen and the two-visor dmsg-mesh E2E.

Implementation Status

✅ In this PR (skywire — transport wrapper)

  • Project Structure: cmd/apps/skydex-client and cmd/apps/skydex-market, registered in the
    merged skywire binary and emitted in generated visor configs (routing ports 8050/8051; --addr/
    --port on both, --market-pk/--market-port on the client).
  • Transport wrapper (market): Listens on appnet.TypeDmsg via the visor and injects each
    client's authenticated dmsg remote public key as the identity (server.IdentifyFunc), then
    delegates to the engine's Run. No bespoke dmsg server — uses app networking.
  • Transport wrapper (client): dials the market's public key over dmsg (MarketDialer) and
    hands the framed connection to the engine.
  • Visor bridges: internal/skydex-{market,client}/app (the app.Client wrappers) and the
    engine Host / MarketDialer adapters — operator OTP → visor app list, market identity → visor PK.
  • Two-visor dmsg-mesh E2E (internal/skydex-e2e, scripted chain) driving the skycoin engine
    over a real in-process dmsg mesh; plus a guided livenet-tagged live harness.
  • go.mod imports the skycoin engine (see Merge sequencing).

✅ In the companion skycoin PR (the engine — no skywire dependency)

  • Communication Layer (dmsg protocol): JSON message protocol (src/skydex/protocol) — register,
    get_currencies, get_products, create_listing, buy_product, cancel_listing, cancel_order, get_orders,
    get_order_status, get_listings; framed with src/skydex/message (a copy of skywire's
    pkg/skychat/message). The server is transport-agnostic: Accept(ctx, net.Listener, IdentifyFunc)
    • ServeConn(conn, identity); single-session enforcement per identity.
  • Market Backend: standalone (TCP) reference command + SQLite layer (src/skydex/db) with
    auto-migrations — 8 tables + full CRUD, thread-safe; default config (commission, confirmations,
    trade limits, timeouts, ban rules, per-coin explorers, sell-coin registry).
  • Operator UI (served on :8050): configuration (commission rate/floor/cap, confirmations,
    min/max trade size, ban rules), the sell-coin registry (add/edit/enable/disable/delete SKY +
    fibercoins, each with its own node URL, escrow seed/address and confirmation depth), per-coin
    explorer enable/disable, and monitoring (products, orders, bans, commission), behind a localhost
    operator API gated by a one-time code the app publishes to its host (the visor app list over
    skywire). The UI is parameterized by a 3-method Host (Log / PublishOTP / PubKey).
  • Multi sell-coin registry (fibercoins): sell_coins table (SKY is the seeded, editable
    default), a per-symbol chain router, coin-generic wire/DB/UI. One registered address receives
    every fibercoin a user buys.
  • Fibercoin-to-fibercoin trades: the payment currency may itself be a sell coin, verified on
    that coin's node by sender + window + fixed amount; external UTXO coins keep the unique
    non-round amount.
  • Background Jobs — 7 jobs under a Runner: Expiry Handler, Listing Checker, Escrow Checker,
    Return Scheduler (immediate refund), Ban Manager, Cleanup Job, Escrow Audit (per sell coin).
  • Blockchain Integration (src/skydex/chain): local-signed sell-coin settlement (read UTXOs
    /api/v1/outputs, sign in-process via src/coin+src/cipher+src/transaction, broadcast via
    /injectTransaction); deposit & payment verification by address + window + exact amount; pluggable
    per-coin explorer adapters (Esplora live-validated for BTC/LTC). Live-verified on SKY mainnet.
  • Commission (sell coin)clamp(amount × rate%, min, cap), folded into the seller's deposit.
  • Cancel flows — buyer cancel-order (freeze violation) and seller cancel-offer (immediate refund).
  • Client (React + Vite + Bootstrap SPA), Skywire-themed, embedded via embed.FS in
    cmd/skydex-client: connect gate → Market / My Orders / My Listings / Settings; sell-coin +
    payment-currency selection; live get_order_status polling (confirmations/tx-hash, commission
    breakdown, cancel buttons).
  • Hardening — per-coin wallet-address validation (Skycoin/BTC/LTC), session idle timeout,
    escrow/DB consistency audit, operator-configurable confirmations and trade size.
  • Full two-party live E2E (livenet-tagged) — deposit → buy → pay → deliver against the live
    SKY node + Esplora, local-signed SKY delivery, commission retained; on-chain verified.
  • Testing — unit/integration for protocol transport, server handlers, jobs, chain (httptest +
    livenet live checks), the sell-coin registry, fibercoin-payment routing, and both apps' HTTP APIs.

🚧 TODO / Pending

  • More payment coins (post-feedback) — BCH / DOGE / DASH are already wired through the Esplora
    adapter but not yet validated end-to-end; they'll be enabled after feedback + testing.
    Account/token chains (USDT ERC-20/TRC-20, ETH) need per-contract endpoints and are future work.
    (XMR intentionally unsupported — not verifiable by address.)
  • Standalone auth — the skycoin standalone (non-skywire) build currently identifies a peer by
    its TCP remote address; a real standalone auth (TLS client cert / blockchain login) is a follow-up.
    Over skywire the identity is the authenticated dmsg public key (already in place).

Notes

  • The exchange engine lives in skycoin and is imported here as a drop-in; skywire supplies only the
    dmsg transport + identity injection + config-gen registration.
  • Sell-coin spend path signs locally; the coin's node is used only for reads + broadcast, and its
    wallet API is never touched (the escrow seed stays in-process).
  • The market sells SKY and any Skycoin fibercoin side by side; the payment leg can be an external
    UTXO coin (BTC/LTC) or another fibercoin (fixed amount, verified on its node).
  • Commission is charged in the sell coin (a Coin-Hours/SCH fee, and SCH-for-BTC/LTC trading, were
    considered and dropped as low-value/uncontrollable).
  • Database uses modernc.org/sqlite (pure-Go, CGO-free — matches the repo's CGO_ENABLED=0
    static, cross-compiled builds); all timestamps stored as UTC.
  • All UI text is English; privacy-first (no PII; identity is the Skywire public key; trade data
    auto-deleted after 3 days).
  • Payment verification is by amount + address + time window in the recipient wallet's recent
    transactions — no user-submitted transaction hashes.

@0pcom

0pcom commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

@mrpalide note that there are a few repos that you might want to look at which already did a lot in this general direction. I think they were designed for a centralized model, but might still be relevant.

https://github.com/skycoin/skycoin-exchange
https://github.com/SkycoinPro/coin-hour-bank
https://github.com/SkycoinPro/coin-hour-bank-wallet

mrpalide and others added 25 commits July 12, 2026 06:37
mrpalide and others added 10 commits July 16, 2026 01:28
… white-text badges, operator unban + clear-block controls, configurable buy-cancel limit (min 2)
…join, per-item delete, and clear-all (localStorage)
… instead of stuck on connecting (no idle skywire connection to infer status from)
…der and recent-markets list — piggybacked on get_currencies, no new round-trip
… and hide the My-Listings Cancel button once a buyer has selected the product (backend now reports cancelable/product_status)
mrpalide and others added 13 commits July 18, 2026 22:52
  amount (BIP-21 for BTC/LTC/DOGE/DASH/BCH/SKY, address-only
  fallback), shown in both buy and sell flows
… app list, and make escrow secrets

  write-only
…ed attempt, and give session tokens their own alphabet
  buildTransitDmsg added all of deployment.Prod's dmsg servers to the transit
  client unconditionally, so a private or e2e deployment opened sessions to the
  nine public production servers on every start. Those dials sit on the
  cold-start path and competed with the registration the function exists to
  enable — visible in CI e2e logs as sessions to prod PKs and a connection
  refused to 143.42.59.213:30088.

  Seed per discovery PK instead, via a new EmbeddedServersForDiscoveryPK that
  the existing URL-keyed EmbeddedServersForDiscoveryDmsg now delegates to, so
  both paths share one coupling guarantee. Production servers are unaffected;
  only foreign deployments stop dialing the fleet. Adds the deployment package's
  first tests, pinning that the e2e discovery gets nil.
  Binaries and app names become skydex-market / skydex-client, display names
  "SkyDEX - Market" / "SkyDEX - Client". Renames the cmd/apps and internal
  trees, skyenv constants, Makefile targets and the CI job, and rebuilds both
  UIs — the Angular manager hardcodes the app name for the OTP column, so its
  bundle had to be regenerated too.

  The operator OTP is now five digits instead of five alphanumerics, and any
  wrong entry burns the code rather than only the tenth. That shrinks the space
  to 100_000, so globalLoginRefill goes 6s -> 6m: 240 attempts/day puts covering
  the space at ~417 days, and a single operator is still covered by the burst of
  ten. Session tokens get their own alphabet so OTP ergonomics can no longer
  weaken them.
The SkyDEX engine now lives in github.com/skycoin/skycoin (src/skydex/*, cmd/skydex-*) with no skywire dependency, mirroring skycoin-web. Skywire keeps only the transport wrapper (~600 LOC, down from ~14k):

- cmd/apps/skydex-{market,client}: supply the appnet/dmsg transport and delegate to the skycoin engine. The market Listens on appnet.TypeDmsg and injects each client's authenticated dmsg public key as its identity (via IdentifyFunc); the client dials the market's public key over dmsg (via MarketDialer). Business logic is reached through the engine's Host / MarketDialer seams.
- internal/skydex-{market,client}/app: the visor app.Client wrappers (kept).
- internal/skydex-e2e: the real dmsg wire test, now driving the skycoin engine over an in-process dmsg mesh — still passes.
- Removed the engine packages, operator/trading UI, and React sources that moved to skycoin.
- protocol.Port dropped its routing.Port alias (now uint16) so the engine has no skywire tie.
@mrpalide mrpalide changed the title [WIP] feat: Implement Skywire Decentralized Exchange (Exchange Market & Client) feat: Implement Skywire Decentralized Exchange (Exchange Market & Client) Jul 20, 2026
@mrpalide
mrpalide marked this pull request as ready for review July 20, 2026 15:31
@mrpalide
mrpalide merged commit 734d28b into skycoin:develop Jul 20, 2026
22 of 23 checks passed
@mrpalide
mrpalide deleted the feat/exchange branch July 20, 2026 17:12
0pcom added a commit to 0pcom/skywire that referenced this pull request Jul 20, 2026
Refresh vendored github.com/skycoin/skycoin to develop HEAD. Rebased onto
current skywire develop (post exchange/skydex merge skycoin#3434).
0pcom added a commit that referenced this pull request Jul 20, 2026
Refresh vendored github.com/skycoin/skycoin to develop HEAD. Rebased onto
current skywire develop (post exchange/skydex merge #3434).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants