Search docs

Find a documentation page

Earnings and pricing

Price your exclusive and shared offerings, get paid atomically in the trader's own transaction, and understand revocation and ratings.

What you sell

Prices are set in SOL per epoch (one Solana epoch is 432,000 slots, roughly 2 days). Traders book 1 to 30 epochs per lease. You sell up to two offerings on the same listing:

ExclusiveShared
AvailabilityAlways on for every live listingOpt-in
Traders per epochExactly oneUp to your seat cap (>= 1)
BandwidthFull capacitySplit across co-tenants
Handshake quota per leaseFull budget (default 100/min)max(1, floor(budget / seats))
PriceYour exclusive priceSeparate, usually lower, price

Exclusive offering

Exclusive is the default mode and every listing must have an exclusive price to go live. One trader holds your validator for the booked epoch window with the full bandwidth and the full handshake budget. While an exclusive lease is pending payment or active, no other lease of either mode can overlap those epochs.

Shared offering

Shared is opt-in. You set a shared price per epoch and a seat cap (max_concurrent_shared_leases, at least 1). Up to that many traders can hold shared leases over the same epoch, each paying your shared price.

Each shared lease's JWT carries a per-seat handshake quota of max(1, floor(budget / seats)), where the budget is the platform default of 100 handshakes per minute. With 4 seats that is 25/min per trader. The host-proxy enforces the quota per lease, so one co-tenant cannot crowd out the others.

A trader pays less for shared because they get less: co-tenants on the same epochs, a fraction of the handshake quota, and no exclusivity. For you it is a way to monetize epochs no exclusive trader booked, with a revenue ceiling of seats x shared price per epoch instead of one exclusive price.

Admission is enforced in Postgres, not in application code. Exclusive and shared leases mutually exclude each other on overlapping epochs: an exclusive booking against any taken epoch fails with 409 lease-slot-taken, a shared booking when every seat is sold fails with 409 shared-capacity-full, and a shared booking against a listing with the shared offering disabled fails with 422 shared-not-offered. See error reference.

Configure at registration

validator-cli register takes the exclusive price as the required --price flag. The shared offering is the optional --shared-price / --shared-seats pair, and each flag requires the other:

validator-cli register \
  --identity-keypair /path/to/validator-keypair.json \
  --endpoint-url https://tee.example.com:443 \
  --price 2.5 \
  --shared-price 0.8 \
  --shared-seats 4 \
  --bio 'Frankfurt validator, 1.2M SOL stake' \
  --payout-wallet <BASE58_ADDRESS>

If only one of the pair reaches the API, registration fails with 422 register-shared-config-incomplete. A non-positive shared price is 422 register-shared-price-invalid and a seat cap below 1 is 422 register-shared-seats-invalid. Omit both flags to launch exclusive-only. You can add the shared offering later.

Change it later

Two ways, both owner-only:

  • Dashboard quick toggle: the Offering card on /validator shows your exclusive price, shared price, and seat cap, with a one-click checkbox to enable or disable shared bookings. The toggle only appears once a shared price and seat cap are stored. An unconfigured listing shows a link to the Edit listing form instead.
  • Edit listing form at /validator/listing/edit: change the exclusive price, shared price, seat cap, bio, and endpoint URL.

Both drive the same API call:

curl -X PATCH https://api.swqos.dev/v1/validators/{id} \
  -H "Authorization: Bearer <api key>" \
  -H "Content-Type: application/json" \
  -d '{"shared_enabled": false}'

PATCH semantics:

PatchResult
{"shared_enabled": false}Stops new shared bookings. The stored shared price and seat cap are kept, so re-enabling later is one toggle. Active shared leases are unaffected.
{"shared_enabled": true}Works only if a shared price and seat cap are stored or supplied in the same patch, otherwise 422 shared-config-incomplete.
{"shared_enabled": true, "shared_price_sol": 0.8, "max_concurrent_shared_leases": 4}Configures and enables in one call.
{"price_sol": 3.0}New exclusive price. Must be greater than zero.
{}422 patch-validator-empty.

When price changes apply

Price changes apply to new bookings only. At booking time the per-epoch price is frozen onto the lease row (price_per_epoch_at_book_lamports), and everything derived from it stays frozen for that lease's lifetime:

  • Existing leases are never repriced, up or down.
  • Lease extensions are charged at the lease's frozen per-epoch price, not your current listing price.
  • The platform fee percentage and your payout wallet are also stamped on the lease at booking and never change afterwards.

How you get paid

There is no custodial holding and no payout schedule. The trader pays through a Solana Pay transaction request, and that single transaction contains two transfer legs:

  • 90% to your payout wallet (validator_share = total - fee)
  • 10% to the platform wallet (fee = floor(total x 1000 / 10000), the 10% take-rate frozen at booking)

The settlement worker confirms the finalized transaction on-chain and verifies both legs before activating the lease. Your share lands in your wallet the moment the trader's payment finalizes, up front for the whole lease, not spread over the epochs. The dashboard's Revenue received card lists each settlement with an explorer link.

Payout wallet

The payout wallet defaults to your validator identity pubkey. Set a different one at registration with --payout-wallet, or rotate it later:

validator-cli set-payout-wallet \
  --identity-keypair /path/to/validator-keypair.json \
  --payout-wallet <BASE58_ADDRESS>

The CLI signs a challenge with your identity keypair and calls PUT /v1/validators/payout-wallet. The change applies to future bookings only: the wallet is frozen onto each lease at booking, so leases already booked keep paying the wallet they were booked with. The dashboard's Payout wallet card shows the current address and a prefilled command.

Revoking a lease

You can cut off a trader (abuse, security concern, capacity emergency) from the dashboard: each active lease in the Capacity card has a Revoke button that opens a dialog with a required free-text reason and a preview of the refund the platform will book. The API equivalent is POST /v1/validators/{id}/leases/{lease_id}/revoke with an Idempotency-Key header.

What happens:

  • The lease flips to revoked immediately. The marketplace stops issuing JWTs for it, and the trader's current JWT dies at its exp (at most 10 minutes).
  • The trader gets a prorated refund: floor(remaining_epochs x total_amount / total_epochs), where remaining epochs run from the current epoch (inclusive) through the lease end. Before the lease starts that is a full refund, after it ends it is zero.
  • The refund is paid from the platform-held SOL buffer (funded by the 10% fee leg), processed manually within 24 hours. Your already-received 90% share is not clawed back.
  • Only active leases can be revoked (422 lease-not-active). Revoking twice returns 409 lease-already-revoked with the original refund unchanged.

Treat revocation as a last resort. The reason you enter is permanently audit-logged, the trader loses service they paid for, and the reputational damage is yours, not theirs. The platform absorbs the refund, you absorb the trust hit.

Ratings and reputation

Your reputation score drives your ranking on the browse page. It is recomputed daily as:

reputation = 0.50 x uptime_score + 0.30 x latency_score + 0.20 x ratings_score
  • Uptime (50%): 30-day rolling probe uptime, mapped linearly from 95% (score 0) to 99.9% (score 100).
  • Sign latency (30%): 30-day p50 probe latency, mapped linearly from 200ms (score 0) down to 30ms (score 100).
  • Trader ratings (20%): average stars over 5. Zero until you have ratings.

Rating rules:

  • One rating per lease, and only for leases that actually ran: the lease window must have elapsed and the lease must have served at least 10,000 TEE signatures (real signing activity, not token mints). Anything else is 422 rating-not-yet-eligible.
  • You get one public reply per rating, via the dashboard ratings panel or POST /v1/ratings/{rating_id}/reply.