# solisting ## Vision Today's marketplaces are gatekept: a company can delist a product, freeze a seller's payout, or shut down the platform outright, and buyers/sellers have no recourse. `solisting` is built on the belief that product listings and the payments behind them should live on-chain instead — where no single operator can censor a listing, and no middleman sits between buyer and seller taking a cut or holding custody. - 🌍 **Decentralized marketplaces** — listings and orders live as on-chain accounts, not rows in a company's database, so there's no central operator who can arbitrarily delist products or gatekeep who gets to sell. - 🚫 **No middleman** — buyers and sellers interact directly through the protocol; there's no platform sitting between them extracting fees, controlling access, or acting as a single point of failure. - 🧱 **Censorship-resistant listings** — as long as the underlying blockchain is live, a listing exists and stays visible; it can't be quietly taken down by a platform decision. - ✅ **Final, auditable payments** — every payment flows through smart contract escrow, so settlement is final and every step of it is publicly verifiable on-chain, not hidden inside a company's internal ledger. - 🔮 **Predictable by construction** — the rules for how funds move (when they're locked, when they're released, when they're refunded) are enforced by code, not discretionary platform policy, so outcomes are known in advance rather than decided case-by-case. ## What is solisting? `solisting` is a single-program Solana protocol that implements a **coordination and discovery layer for decentralized product listings and bilateral order consent**. It sits on top of a separate escrow program, [`descro`](../descro), and never custodies funds itself — it CPIs into `descro` to create, confirm, and cancel escrows. Built using [Anchor](https://www.anchor-lang.com/). The repository also contains a TypeScript SDK and a Next.js explorer app built on top of the on-chain program. ## Concept Marketplaces on Solana usually force a choice: either the marketplace program custodies funds itself (more trust assumptions, more surface area to audit), or every integration reinvents escrow from scratch. `solisting` takes a different approach — it is a thin coordination layer that only handles *discovery* (listings) and *consent* (orders), and delegates all fund custody to a dedicated, independently auditable escrow program (`descro`) via CPI. - 🔐 **Non-custodial by design** — solisting never holds buyer or seller funds; every escrow lifecycle event is a CPI into `descro`, so the fund-custody logic lives in one focused, auditable program instead of being duplicated per marketplace. - 🧩 **Composable, not monolithic** — separating listings/orders from escrow means `descro` can be reused by other marketplace front-ends, and `solisting` can evolve its discovery/consent UX independently of settlement logic. - 💱 **Multi-currency listings from one source of truth** — a seller sets a single canonical price; buyers can pay in alternate currencies (including SOL or stablecoins) and the program converts on the fly via Pyth oracles, so there's no need to maintain N parallel prices per listing. - 🛡️ **Slippage-protected conversion** — buyers supply an expected amount and max slippage tolerance, so oracle-based conversion can't silently overcharge them between quote and execution. - 🤝 **Bilateral consent, defensively handled** — orders require explicit seller acceptance, and abort paths (`reject_order`/`cancel_order`) tolerate the counterparty already having cancelled directly on `descro`, avoiding stuck states. ## Repository layout ``` programs/solisting/ # Anchor program (Rust) sdk/ # TypeScript client SDK (Codama-generated + hand-written helpers) app/ # Next.js app for browsing listings/orders docs/ # Implementation plans and design docs vendor/ # Vendored/patched Pyth crates (see "Toolchain gotchas" below) ``` ## Architecture ### Canonical price model `ListingAccount` stores one canonical price (`canonical_currency` + `price`) as the sole source of truth: - `alt_currencies` — other currencies a buyer may pay in, each with an optional Pyth `TOKEN/USD` oracle (`None` = treated as a $1.00 stablecoin). - `canonical_oracle` — the Pyth feed for the canonical currency (`None` = canonical is itself a $1 stablecoin). At `create_order` time, if the buyer pays in an alt currency, the program converts the canonical price into the target currency using USD as a pivot through up to two Pyth feeds, and enforces a buyer-supplied slippage tolerance. ### Accounts & PDAs - `ListingAccount` — seeds `[b"listing", seller, listing_id]`. Tracks quantity, reservations, accepted resolvers, active state, and metadata URI. - `OrderAccount` — seeds `[b"order", listing_account, buyer, order_id]`. Records the buyer's chosen payment currency, the oracle-resolved amount, and a pointer to the corresponding `descro` `EscrowAccount`. ### Instructions | Instruction | Purpose | |---|---| | `create_listing` / `update_listing` / `close_listing` | Manage a listing (seller-only) | | `create_order` | Buyer flow: validates currency/resolver, performs oracle conversion, CPIs into `descro` to create an escrow | | `accept_order` | Seller flow: confirms the escrow and settles the listing's reserved quantity | | `reject_order` / `cancel_order` | Seller/buyer-initiated abort, defensive against out-of-band cancellation on `descro` | | `close_stale_order` | Permissionless cleanup of orders whose escrow has already reached a terminal state | ## Prerequisites - Rust toolchain `1.89.0` (pinned via `rust-toolchain.toml`) - Solana CLI / `cargo build-sbf` - The sibling [`descro`](../descro) repository checked out at `../descro` relative to this repo — solisting path-depends on it and the tests embed its compiled `.so` files. - Node.js + Yarn (for the SDK and app) ## Building `descro` (and its dependency `descro_ext_resolvers`) must be built first: ```bash # 1. Build dependency programs (sibling repo) cd descro && anchor build # 2. Build solisting anchor build ``` ## Testing Requires steps 1 + 2 above to have been run first, since the LiteSVM test harness embeds both `.so` files via `include_bytes!`: ```bash cargo test --manifest-path programs/solisting/Cargo.toml # Run one test file / one test by name cargo test --manifest-path programs/solisting/Cargo.toml --test test_orders cargo test --manifest-path programs/solisting/Cargo.toml -- seller_can_create_sol_only_listing ``` ## Lint / format ```bash cargo clippy --manifest-path programs/solisting/Cargo.toml cargo fmt --manifest-path programs/solisting/Cargo.toml ``` Note: `Anchor.toml` declares `test = "cargo test"`, but `anchor test` is not the normal workflow here — use the `cargo` commands above directly. ## SDK & app The `sdk/` and `app/` workspaces are managed with Yarn: ```bash yarn install yarn workspace @solisting/sdk ... # SDK-specific commands ``` See their respective `package.json` files and `docs/superpowers/` for details on the SDK and explorer app. ## Toolchain gotchas - The Rust toolchain is pinned to `1.89.0` via `rust-toolchain.toml`. - The workspace root `Cargo.toml` patches `pyth-solana-receiver-sdk` and `pythnet-sdk` to local copies under `vendor/` because the crates.io versions don't compile against this toolchain's dependency graph (an `anchor-lang`/`borsh` version conflict). If you touch oracle code, the vendored crates are the source of truth for `PriceUpdateV2`'s shape.