Skip to content

Trade Contracts

Trade contracts let players (and NPCs) commit to deliveries with deadlines and rewards. They are distinct from faction missions: missions are reputation-driven offers from a faction; contracts are pure economic exchange between two parties (NPC-to-player or player-to-player).

πŸ“ Design-only β€” no Contract model, service, or routes are committed yet. This page is the target spec.

Overview

Two contract directions:

Direction Issuer Browsed at
NPC-to-player A station, corporation, or colony NPC The station's contract board
Player-to-player Any player who posts an offer One or more chosen stations' boards

Both directions share the same lifecycle, payment model, and dispute rules. Differences live in how the contract is generated and who pays the penalty on failure.

A Contract row carries:

id, issuer_type (npc|player), issuer_id,
contract_type (cargo_delivery, bulk_procurement, express_delivery,
               hazardous_transport, refugee_transport, acquisition_bounty,
               escort),
origin_station_id, destination_station_id,
commodity_type, quantity,
payment, penalty, reputation_reward, reputation_penalty,
deadline, posted_at, accepted_at, completed_at,
status (posted, accepted, in_transit, completed, failed, cancelled),
acceptor_player_id, insurance_premium, insurance_paid,
faction_id   -- for NPC contracts: which faction's standing is affected

Contract boards

πŸ“ Each station exposes a contract board β€” a per-station list of currently-posted contracts. A board is the union of:

  • NPC contracts spawned by the generator at this station.
  • Player-posted contracts whose posting_stations set includes this station.
  • Bounty-style acquisition contracts where this station is the destination.

Boards refresh on the same tick cadence as market prices (see trading.md Β§ Pricing). Players see only contracts they're eligible for: faction-gated NPC contracts hide below the minimum reputation threshold; player contracts hide for parties on the issuer's blocklist.

A station's board capacity is bounded β€” a Class-0 trade hub posts more contracts than a Class-8 black hole. The generator fills up to capacity on each tick, biased toward contract types that match the station's class trading pattern. (Station classes.)

NPC-issued contracts

πŸ“ NPC contracts spawn dynamically on each station's contract board (Station.contract_board). The generator (contract_generator.py) seeds new entries on a tick and prunes expired ones. Generator inputs:

  • Station class and current commodity surplus / deficit.
  • Faction control of the surrounding region.
  • Time of day in the game world (express-delivery rates spike during high-traffic windows).
  • Active galaxy-wide events (war, plague, blockade) that modulate refugee and hazardous demand.

Categories:

Cargo delivery

Pick up commodity X at station A, deliver to station B by time T. Payment on delivery. Cargo is reserved at the origin: accepting the contract grants the player a one-time pickup right at a fixed price (often free or below-market).

Bulk procurement

Gather N units of a commodity from anywhere and deliver to one station. No fixed origin β€” the player sources however they like. Payment is per-unit on delivery; partial deliveries credit pro-rata up to the deadline.

Express delivery

High-priority cargo with a tight deadline. Payment is higher than standard cargo delivery, plus an early-arrival bonus (see Rewards). Express contracts use a stricter penalty on failure.

Hazardous transport

Illegal or contraband cargo routed via black-market channels. Issued by criminal NPCs at black-market terminals. See black-market.md for the goods list and detection mechanics. Hazardous transport pays significantly more, applies a faction penalty if completed, and exposes the carrier to scans during transit.

Refugee transport

Move colonists from one region to another. Interregional only β€” single-region jobs use the standard colonist trade flow described in planets/colonization.md. Refugee contracts are gated behind a passenger-rated ship and pay per surviving colonist on arrival.

Player-issued contracts

πŸ“ Players post offers visible at one or more stations they control or have docking rights at. The poster pays the contract value into escrow at posting time.

Delivery contract

The player has cargo they need moved to a destination they can't easily reach. The player nominates origin, destination, commodity, quantity, deadline, and offered payment. Other players accept and execute the run.

Acquisition bounty

The player offers credits for any cargo of type X delivered to port Y by the deadline. Multiple acceptors can fulfill partial quantities until the bounty is met or the deadline lapses.

Escort contract

The player pays another player to fly with them through a sequence of sectors (combat support during traversal). Escort contracts complete when the protected player arrives at the destination intact, or when a defined number of hostile encounters are survived. Cross-link: ships.md.

Escrow handling

πŸ“ Player-issued contracts use server-held escrow:

  • At posting time, the issuer's account is debited by payment + insurance_pool_reserve. The funds are held by the contract row, not in the issuer's wallet.
  • On completed, escrow pays the acceptor.
  • On failed, escrow is split per the failure rule: penalty fraction returns to the issuer (compensation), the rest is forfeit to a sink.
  • On cancelled before acceptance, escrow returns to the issuer minus a small posting fee.

Escrow is never directly transferable between players β€” all settlement runs through the contract.

Contract lifecycle

Status Trigger Effect
posted Issuer creates the contract Visible on contract board(s); awaiting acceptance
accepted A player accepts Acceptance fee charged; cargo (if delivery) reserved at origin; clock starts
in_transit Cargo loaded into the acceptor's ship Deadline timer running; player carries the load
completed Cargo delivered at destination station before deadline Payment + reputation reward issued; insurance refunded if held
failed Deadline expires or cargo lost in transit Penalty applied; reputation penalty; escrow paid to issuer
cancelled Cancellation before acceptance, or by mutual agreement after Partial penalty (kill-fee) β€” see Anti-griefing

Status transitions are one-way except posted β†’ cancelled. Once accepted, the only exits are completed, failed, or cancelled (with kill-fee).

posted ──accept──▢ accepted ──load──▢ in_transit ──deliver──▢ completed
   β”‚                  β”‚                    β”‚
   β”‚                  β”‚                    └──deadline_expired──▢ failed
   β”‚                  β”‚                    └──cargo_destroyed───▢ failed
   β”‚                  └──mutual cancel──▢ cancelled (kill-fee)
   └──issuer withdraw──▢ cancelled (no fee)

API surface

πŸ“ Target endpoints under /api/v1/contracts/:

Method Path Purpose
GET /api/v1/contracts/board?station_id=... List contracts visible at a station
GET /api/v1/contracts/mine List the caller's posted + accepted contracts
GET /api/v1/contracts/{id} Detail for a single contract
POST /api/v1/contracts Post a new player-issued contract (escrow check)
POST /api/v1/contracts/{id}/accept Accept a posted contract (charges acceptance fee)
POST /api/v1/contracts/{id}/insure Buy insurance on an accepted contract
POST /api/v1/contracts/{id}/complete Mark delivered (server verifies cargo at destination)
POST /api/v1/contracts/{id}/cancel Cancel β€” kill-fee applied per state
POST /api/v1/contracts/{id}/dispute File a dispute on a failed contract

WebSocket events fire on every status transition for the issuer, acceptor, and any subscribed faction-management clients.

Rewards

Base payment is a function of:

payment = base_rate
        Γ— commodity_value(commodity_type, quantity)
        Γ— distance_factor(origin, destination)
        Γ— urgency_factor(deadline_tightness)
        Γ— contract_type_multiplier

commodity_value derives from the live midpoint price (see trading.md Β§ Pricing). distance_factor uses warp-jump count between origin and destination. urgency_factor rises as deadline tightness increases (express deliveries pay roughly 1.5–2.0Γ— their non-express equivalents).

Bonuses

  • Early-completion bonus β€” up to +25% of payment if delivered with greater than 50% of the time window remaining. Linear scale between 0–25% above the 50% threshold.
  • Reputation reward β€” completion grants reputation with the issuing faction (NPC contracts) or a small mutual reputation bump between poster and acceptor (player contracts).
  • Insurance refund β€” if the player paid an insurance premium and completed cleanly, the unused premium is not refunded β€” see Risk & insurance for why.

Penalties

On failure (deadline expired or cargo lost):

  • Forfeit reserved cargo (if a delivery contract).
  • Reputation penalty with the issuing faction (NPC) or the posting player.
  • Cooldown on contract eligibility from that issuer (default 24 game-hours).
  • Acceptance fee is not refunded.
  • Penalty credits are debited from the acceptor's account; if insufficient, the deficit is recorded as a debt that must be cleared before posting new contracts.

Worked example

πŸ“ A Class-2 station posts a cargo_delivery for 150 units of organics to a Class-3 station 8 jumps away, deadline 90 minutes:

base_rate                   = 1.0
commodity_value             = 150 Γ— midpoint(organics) β‰ˆ 150 Γ— 16.5 = 2,475 cr
distance_factor             = 1.0 + 0.05 Γ— 8 = 1.40
urgency_factor (90 min)     = 1.10
contract_type_multiplier    = 1.0  (standard cargo_delivery)
payment                     β‰ˆ 2,475 Γ— 1.40 Γ— 1.10 β‰ˆ 3,810 cr
acceptance_fee              β‰ˆ 76 cr (2%, refundable)
early-completion bonus cap  β‰ˆ +953 cr (25%) if delivered with > 45 min left
penalty on failure          = forfeit reserved cargo + 1Γ— payment debit

Numbers are illustrative; the actual coefficients live in contract_service.py config.

Risk & insurance

πŸ“ Optional contract insurance is a per-contract add-on, purchased at acceptance time:

Coverage tier Premium Covers
Basic 5% of contract value Ship loss in low-security space during in-transit
Standard 10% Ship loss anywhere + 50% cargo replacement
Hazard 15% Ship loss anywhere + 100% cargo replacement + extended deadline grace

If the insured ship is destroyed during in_transit, the policy pays out the contract penalty for the player, less a deductible. Insurance vs deductible model parallels the ship insurance system. Insurance does not cover wilful abandonment, deadline-only failures (the cargo arrived but late), or smuggling losses on hazardous-transport contracts.

Reputation effects

Completing NPC contracts boosts faction standing with the issuing faction. Failing damages it. The reward/penalty values are stored on the contract row at posting time, so they're stable across the lifecycle even if the faction's general standing thresholds shift.

Persistent failure β€” 3 or more failures in a row with the same faction β€” bans the player from accepting that faction's contracts for a cooldown (default 7 game-days). The cooldown clears either by waiting it out or by completing a non-contract faction mission to repair standing.

Player-to-player contracts also feed a lightweight trader-reputation stat (separate from faction reputation). Persistent contract reliability on the trader side becomes a public visible badge on the player's profile.

Anti-griefing

πŸ“ Rules to keep contracts from being weaponised:

  • Contracts cannot be accepted from players the acceptor has active hostility with (negative direct-relationship reputation between the two parties).
  • An acceptance fee (small, fixed percentage of contract value, default 2%) is charged at accept time and refunded on completion. This discourages frivolous picks that lock the contract for the deadline window without intent to complete.
  • The issuer cannot cancel a contract after it has been accepted without paying a kill-fee equal to the acceptance fee plus 10% of the contract value, paid to the acceptor.
  • A player cannot post a contract whose escrow they cannot afford. Escrow is held server-side at posting time.
  • Contract boards rate-limit per-player postings to prevent spam (default 10 active postings per player per region).

Disputes

πŸ“ If a player believes a contract was completed but the system marked it failed (e.g. the destination station went offline mid-delivery), the player can file a dispute within 48 game-hours of the failure timestamp. Disputes pause the penalty until resolved. Resolution is automated where possible:

  • Cargo manifest evidence β€” server-side cargo log shows correct cargo arrived at destination β†’ completion is honoured retroactively.
  • Destination unreachable β€” if the destination station was destroyed or made inaccessible after acceptance, the contract is voided and the acceptance fee is refunded.
  • Issuer abandoned the destination β€” counts as cancellation by the issuer, full kill-fee owed.

Unresolvable disputes escalate to admin review.

Black-market contracts

Hazardous-transport NPC contracts and certain illegal-goods player contracts route through the black-market system. They pay 2–4Γ— standard rates, carry detection risk during transit, and apply a faction penalty on completion (the law-side faction loses standing). See black-market.md for the full mechanics, terminal locations, and detection model.

Source map

πŸ“ None of the following exist yet β€” these are target paths for the implementation:

Concern Target path
Contract model services/gameserver/src/models/contract.py
Contract service services/gameserver/src/services/contract_service.py
Contract API routes services/gameserver/src/api/routes/contracts.py
NPC contract generator services/gameserver/src/services/contract_generator.py
Insurance hooks extension to services/gameserver/src/services/insurance_service.py
Contract board UI new admin/player-UI surface (TBD)

Status

πŸ“ Design-only. The entire trade-contract system is unimplemented. No Contract model, no contract service, no API routes, no UI. This spec defines the target shape; implementation order should be:

  1. Contract model + database migration.
  2. NPC cargo_delivery generator (simplest type) β€” proves the lifecycle end-to-end.
  3. API routes (/api/v1/contracts/...) β€” list, accept, complete, abandon, post.
  4. Player-issued posting flow + escrow handling.
  5. Remaining contract types (bulk, express, hazardous, refugee, escort, acquisition bounty).
  6. Insurance integration.
  7. Anti-griefing limits and reputation cooldowns.