# Solisting Explorer — Frontend Design Brief Build a dark, polished blockchain explorer frontend for the **Solisting** Solana program ecosystem — similar in style to Solscan or Jupiter (dark backgrounds, colored state badges, card-based layout). The primary accent color is Solana purple (`#9945FF`) with green (`#14F195`) for active/success states. The app is called **Solisting Explorer**. It's a debug/inspection tool like mempool.space or Etherscan, but specifically for the solisting + descro program ecosystem. Users can browse all on-chain data and, if they connect their wallet, execute instructions they're authorized for. --- ## What Solisting Is Solisting is a Solana on-chain program (Anchor framework) that provides a coordination layer for product listings and order consent. It sits on top of a separate escrow program called **descro**. - Sellers create **Listings** advertising goods or services with a price in SOL (or SPL tokens). - Buyers create **Orders** against a listing. This triggers the creation of a **descro Escrow** — a trustless SOL vault holding the buyer's funds until the seller confirms. - The seller then accepts or rejects. Disputes are resolved by a **Resolver** (a registered third party). - Solisting never holds funds itself — all money flows through descro's vault accounts. --- ## Programs and IDs | Program | Role | Program ID | |---|---|---| | `solisting` | Listings + Orders | `DzwUAbpRvqcbA8QsEkRbZeXG4TEho5782cMySodrUHBU` | | `descro` | Escrow vault engine | `DjVR4EuYV6USMJFfsGZwhZ3y8rtWsmG8EvDY96GTqqi3` | | `descro_ext_resolvers` | Resolver registry | `GwUPAKs3HHzCpj8uhet4NAnxk9GWNwfrYbpihu5DyFp` | The app needs a **network selector dropdown** in the top navigation: **localnet / devnet / mainnet-beta**. The selected network determines the RPC endpoint for all account fetches and transaction submissions. --- ## On-Chain Accounts ### ListingAccount (solisting program) PDA seeds: `["listing", seller_pubkey_bytes, listing_id_as_u64_le_bytes]` ``` seller: Pubkey // wallet that created the listing listing_id: u64 // unique ID chosen by seller canonical_currency: Currency // Sol | Spl { mint: Pubkey, decimals: u8 } price: u64 // in smallest unit of canonical_currency canonical_oracle: Option // Pyth V2 feed (TOKEN/USD). None = stablecoin ($1) alt_currencies: Vec // max 3. each has { currency, usd_oracle: Option } accepted_resolvers: Vec // max 4. empty = any resolver is accepted quantity: u32 // total units (includes reserved) quantity_reserved: u32 // locked by pending orders metadata_uri: String // max 256 chars. IPFS or HTTPS link to listing metadata JSON is_active: bool // whether new orders are accepted bump: u8 ``` Derived: `available = quantity − quantity_reserved` ### OrderAccount (solisting program) PDA seeds: `["order", listing_account_pubkey_bytes, buyer_pubkey_bytes, order_id_as_u64_le_bytes]` ``` listing: Pubkey // the ListingAccount this order belongs to buyer: Pubkey seller: Pubkey // copied from listing at creation resolver: Pubkey // chosen dispute resolver payment_currency: Currency amount: u64 // final payment amount (oracle-converted if paying in alt currency) escrow_account: Pubkey // linked descro EscrowAccount PDA escrow_id: u64 // first 8 bytes of this order's PDA address as u64 little-endian order_id: u64 created_at: i64 // unix timestamp bump: u8 ``` ### EscrowAccount (descro program) PDA seeds: `["escrow", seller_pubkey_bytes, escrow_id_as_u64_le_bytes]` ``` seller: Pubkey buyer: Pubkey amount: u64 // lamports held in the vault dispute_resolver: Option state: EscrowState // see lifecycle below escrow_id: u64 dispute_raised_at: Option // unix timestamp, set when dispute is raised bump: u8 vault_bump: u8 ``` The **vault** is a separate bare system account (no data, just holds SOL): PDA seeds: `["vault", escrow_account_pubkey_bytes]` (in the descro program) ### EscrowState lifecycle ``` AwaitingSellerConfirm → buyer deposited, waiting for seller Active → seller confirmed, delivery in progress Disputed → dispute raised by buyer or seller Complete → funds released to seller Cancelled → funds refunded to buyer ``` State transitions: - `AwaitingSellerConfirm` → `Active`: seller accepts (via solisting `accept_order`) - `AwaitingSellerConfirm` → `Cancelled`: seller rejects, buyer cancels (via solisting or directly on descro) - `Active` → `Complete`: buyer calls `complete` on descro (marks delivery received, vault goes to seller) - `Active` → `Disputed`: buyer or seller raises dispute on descro - `Disputed` → `Complete` or `Cancelled`: resolver calls `resolve(Winner)` on descro State badge color suggestions: `AwaitingSellerConfirm` = amber, `Active` = green, `Disputed` = red/orange, `Complete` = blue/teal, `Cancelled` = gray. ### ResolverEntry (descro_ext_resolvers program) PDA seeds: `["resolver", resolver_pubkey_bytes]` ``` authority: Pubkey resolver_type: ResolverType // CentralAuthority | JuryDAO | MAD | Algorithmic | Multisig acceptance_policy: AcceptancePolicy // Open | SignatureGated | ProgramGated name: String // max 64 chars description: String // max 256 chars fee_bps: u16 // fee in basis points (100 bps = 1%) fee_recipient: Pubkey metadata_uri: String // max 256 chars total_resolved: u64 ruled_for_buyer: u64 ruled_for_seller: u64 registered_at: i64 ``` --- ## Instructions ### Solisting: create_listing Signer: seller Creates a new `ListingAccount`. Params: `listing_id`, `canonical_currency`, `price`, `canonical_oracle`, `alt_currencies` (max 3), `accepted_resolvers` (max 4), `quantity`, `metadata_uri` ### Solisting: update_listing Signer: seller (must own the listing, listing must be active) Updates all mutable fields on an existing `ListingAccount` (not the listing_id). ### Solisting: close_listing Signer: seller Closes the `ListingAccount`. Rent returned to seller. ### Solisting: create_order Signer: buyer Creates an `OrderAccount` and CPIs into descro to create + fund an escrow vault with the buyer's SOL. Params: `order_id`, `escrow_id` (= first 8 bytes of order PDA as u64 LE), `resolver`, `payment_currency`, `expected_amount`, `max_slippage_bps` Constraints: listing must be active, must have available quantity, currency must be in listing's accepted currencies, resolver must be in listing's accepted resolvers (if list is non-empty). Note: only `Currency::Sol` works currently. SPL token payment is not yet implemented (`SplNotImplemented` error). ### Solisting: accept_order Signer: seller CPIs `descro::seller_confirm` → escrow moves to `Active`. Decrements `quantity_reserved` and `quantity`. Closes `OrderAccount` (rent to seller). ### Solisting: reject_order Signer: seller Defensive: reads escrow state first; only CPIs `descro::cancel` if escrow is still `AwaitingSellerConfirm`. Decrements `quantity_reserved`. Closes `OrderAccount`. Buyer gets vault refund. ### Solisting: cancel_order Signer: buyer Same as reject_order but initiated by the buyer. Closes `OrderAccount` (rent to buyer). ### Solisting: close_stale_order Signer: anyone (permissionless cleanup) Closes an `OrderAccount` whose linked descro escrow is already in a terminal state (`Complete` or `Cancelled`) or has already been closed (account data empty). Rent goes to the caller. ### Descro: complete Signer: buyer Constraint: escrow must be `Active`. Releases vault funds to seller. Escrow → `Complete`. Closes escrow account. ### Descro: dispute Signer: buyer or seller Constraint: escrow must be `Active`. Escrow → `Disputed`. Records `dispute_raised_at` timestamp. ### Descro: resolve Signer: resolver (must match `escrow.dispute_resolver`) Constraint: escrow must be `Disputed`. Param: `winner: Winner` (Buyer or Seller). Releases vault to winner. Escrow → `Complete`. ### Descro: cancel (direct) Signer: buyer or seller Constraint: escrow must be `AwaitingDeposit` or `AwaitingSellerConfirm`. Refunds vault to buyer. Closes escrow. --- ## Error Codes (Solisting) | Name | Message | |---|---| | `ListingNotActive` | Listing is not active | | `OutOfStock` | No quantity available | | `Unauthorized` | Signer is not authorized | | `CurrencyNotAccepted` | Currency not accepted by this listing | | `ResolverNotAccepted` | Resolver not accepted by this listing | | `OracleRequired` | Oracle account required but not provided | | `OracleMismatch` | Oracle key doesn't match listing | | `OraclePriceUnavailable` | Oracle price stale or confidence too wide | | `SlippageExceeded` | Oracle rate moved unfavorably | | `SplNotImplemented` | SPL payment not yet implemented | | `EscrowStateUnexpected` | Escrow not in expected state | | `InvalidDescroProgram` | Descro program address mismatch | --- ## Oracle / Price Conversion Solisting uses **Pyth V2** price feeds for multi-currency support. - Prices must be no older than 60 seconds. - Conversion formula (canonical currency → alt currency): `target = price × canonical_price_raw / target_price_raw × 10^(canonical_exponent − target_exponent + target_decimals − canonical_decimals)` - A currency with `usd_oracle = None` is treated as a $1.00 USD stablecoin. - Slippage check: `|computed − expected| ≤ expected × max_slippage_bps / 10_000` --- ## Relationships Between Accounts ``` ResolverEntry (descro_ext_resolvers) ↑ pubkey referenced in ListingAccount.accepted_resolvers[] ←── one listing has many orders ↑ pubkey stored in OrderAccount.listing OrderAccount.resolver → can look up ResolverEntry for display OrderAccount.escrow_account → EscrowAccount (descro) EscrowAccount → Vault (bare SOL account, descro) ``` --- ## Pages and Features ### Listings page (no wallet required) Browse all `ListingAccount` PDAs on-chain. Show: listing ID, seller (abbreviated address), canonical price + currency, available qty / total qty, number of pending orders, active/inactive badge. Filterable by active status and seller address. Clicking a row goes to the listing detail page. ### Listing detail page (no wallet required) Show all fields of the `ListingAccount`. Quantity displayed as a gauge or three numbers (available / reserved / total). Alt currencies listed with their oracle addresses. Accepted resolvers listed — if a `ResolverEntry` exists for that pubkey, show the resolver name. Metadata URI shown with a link and optional fetch of the JSON to show name/description/image. Below: table of all `OrderAccount`s for this listing with their escrow state. If connected wallet = seller: show "Update Listing" and "Close Listing" buttons. If connected wallet is not the seller and has no open order: show "Place Order" button. ### Order detail page (no wallet required) Show all fields of the `OrderAccount`. Show the linked `EscrowAccount` data inline: state badge, vault balance in SOL (raw lamports on hover), `dispute_raised_at` if applicable. Show the escrow state machine with the current state highlighted. Action buttons depend on the connected wallet's role: - Seller: "Accept Order" and "Reject Order" (when escrow is `AwaitingSellerConfirm`) - Buyer: "Cancel Order" (when escrow is `AwaitingSellerConfirm`), "Complete" (when `Active`), "Dispute" (when `Active`) - Anyone: "Close Stale Order" (when escrow is terminal or closed) ### All escrows page (no wallet required) Browse all `descro::EscrowAccount` PDAs. Filterable by state. Columns: escrow ID, seller, buyer, amount in SOL, state badge, resolver (if any). ### Resolver registry page (no wallet required) Browse all `ResolverEntry` PDAs. Show name, type, acceptance policy, fee %, total disputes, buyer win %, seller win %. Clicking goes to resolver detail page. ### Resolver detail page (no wallet required) All fields of a `ResolverEntry`. Show dispute stats as numbers and ratios. ### My Dashboard (wallet required) Two tabs: "My Listings" and "My Orders (as Buyer)". My Listings: all `ListingAccount`s where `seller == connected_wallet`. Quick stats per listing. Buttons: Update, Close, + a link to its orders. My Orders: all `OrderAccount`s where `buyer == connected_wallet`. Show current escrow state per order with available action buttons. Prominent "Create Listing" button on this page. ### Create / Edit Listing form (wallet required) Fields: canonical currency (SOL or SPL mint address), price, canonical oracle pubkey (optional), up to 3 alt currencies (each: currency + oracle pubkey), up to 4 accepted resolver pubkeys, quantity, metadata URI. ### Global search Search box in the top nav. Input any pubkey. The frontend determines what account type it is and redirects to the relevant detail page (listing, order, escrow, resolver, or wallet). --- ## Display Conventions - **Addresses:** always show as `ABCD...WXYZ` (first 4 + last 4 chars). Copy-to-clipboard button. Link to the relevant detail page within the explorer. - **SOL amounts:** show as SOL (divide lamports by 1,000,000,000). Show raw lamport value on hover. - **SPL token amounts:** show in token units using the `decimals` stored in the `Currency::Spl` variant. Optionally show token symbol if resolvable via token metadata. - **Timestamps:** show as relative time ("2 hours ago"). Show exact UTC datetime on hover. - **`quantity_reserved`:** always show all three numbers — available / reserved / total. - **Resolver pubkeys in listings:** if a `ResolverEntry` exists, show name. Otherwise show abbreviated pubkey. - **`metadata_uri`:** show as a clickable link. Optionally fetch the JSON and show name/description/image from it.