MARKET · REVIEWED ONE-TIME DEAL

How to Execute a Reviewed Market Deal Safely

Execute one specified sell order with a one-time request, refresh the current order snapshot, use amount rather than remainingAmount, enforce a price ceiling, Credit and Terminal Energy reserves, estimate transaction cost, disable before deal(), and verify the transaction afterward.

Verification statusChinese source article: Reviewed in full · Official docs: Checked — deal(), getOrderById(), calcTransactionCost(), amount, remainingAmount, 10-deal limit and transaction priority · Market boundary: The complete example buys a normal resource from one specified sell order; account-bound resources and selling to buy orders are excluded

VERIFICATION

Evidence and test status

Chinese source article
Reviewed in full
Official docs
Checked — deal(), getOrderById(), calcTransactionCost(), amount, remainingAmount, 10-deal limit and transaction priority
Market boundary
The complete example buys a normal resource from one specified sell order; account-bound resources and selling to buy orders are excluded
Execution boundary
OK means the operation was scheduled; Credits, Store and transaction records require later verification
JavaScript syntax
Passed
Offline deal review
Passed — stale order, direction, resource, price, current amount, Credit reserve, Energy reserve, cooldown and ready states
Screeps Console test
Pending
Live order race, settlement and transaction-record test
Pending
Last verified
July 26, 2026

Quick answer

Use Game.market.deal() only for one explicitly reviewed order ID. Fetch the current order again, verify it is the expected sell order and resource, cap the current price, limit the amount to order.amount, estimate transfer Energy, preserve Credit and Terminal Energy reserves, disable the request before calling the API, and verify the later transaction record instead of printing “bought” after OK.

The deal executor pays transfer Energy

The official market assigns transfer Energy and Terminal cooldown to the player who calls deal(). This remains true when buying from another player's sell order.

function estimateDealEnergy(
  amount,
  yourRoomName,
  orderRoomName
) {
  if (
    !Number.isInteger(amount)
    || amount <= 0
    || typeof yourRoomName !== 'string'
    || typeof orderRoomName !== 'string'
  ) {
    return null;
  }

  return Game.market.calcTransactionCost(
    amount,
    yourRoomName,
    orderRoomName
  );
}

The estimate uses amount and room names. Applied Terminal power effects can change actual transfer cost, so later Store and transaction records remain the settlement evidence.

Use amount, not remainingAmount

Order fieldMeaningDeal use
amountCurrent amount available to dealHard execution ceiling
remainingAmountRemaining planned order capacityOrder lifecycle information
totalAmountOriginal or extended total capacityOrder history and maintenance

An inactive order can have remaining capacity while its current amount is zero.

Refresh the exact order before execution

function getReviewedSellOrder(request) {
  if (
    !request
    || typeof request.orderId !== 'string'
  ) {
    return {
      ready: false,
      reason: 'order-id-invalid',
      order: null
    };
  }

  const order = Game.market.getOrderById(
    request.orderId
  );

  if (!order) {
    return {
      ready: false,
      reason: 'order-unavailable',
      order: null
    };
  }

  if (order.type !== ORDER_SELL) {
    return {
      ready: false,
      reason: 'order-not-sell',
      order
    };
  }

  if (order.resourceType !== request.resourceType) {
    return {
      ready: false,
      reason: 'resource-mismatch',
      order
    };
  }

  return {
    ready: true,
    reason: 'order-reviewed',
    order
  };
}

Never execute a copied ID without rechecking its current direction, resource, price, room, and available amount.

Calculate Credit and Energy budgets

credit cost = order.price × amount
credits after = current credits - credit cost
terminal Energy after = current Energy - transaction Energy

For a normal resource purchase, the incoming resource is not included in the preflight Energy reserve. If the resource being purchased is Energy, this is deliberately conservative because the Terminal still needs enough Energy to execute the transaction before incoming Energy is settled.

Build a testable deal plan

function evaluateBuyDeal(input) {
  const { request, order } = input;

  if (!request || request.enabled !== true) {
    return { ready: false, reason: 'disabled' };
  }

  if (!order) {
    return { ready: false, reason: 'order-unavailable' };
  }

  if (
    order.type !== ORDER_SELL
    || order.resourceType !== request.resourceType
  ) {
    return { ready: false, reason: 'order-mismatch' };
  }

  if (
    !Number.isInteger(request.amount)
    || request.amount <= 0
    || request.amount > order.amount
  ) {
    return { ready: false, reason: 'amount-unavailable' };
  }

  if (
    !Number.isFinite(request.maximumPrice)
    || order.price > request.maximumPrice
  ) {
    return { ready: false, reason: 'price-above-limit' };
  }

  if (!Number.isFinite(input.transactionEnergy)) {
    return { ready: false, reason: 'energy-cost-invalid' };
  }

  const creditCost = order.price * request.amount;
  const creditReserve = Number.isFinite(
    request.creditReserve
  )
    ? request.creditReserve
    : 0;
  const energyReserve = Number.isFinite(
    request.energyReserve
  )
    ? request.energyReserve
    : 0;

  if (input.credits - creditCost < creditReserve) {
    return {
      ready: false,
      reason: 'credit-reserve',
      creditCost
    };
  }

  if (
    input.terminalEnergy - input.transactionEnergy
    < energyReserve
  ) {
    return {
      ready: false,
      reason: 'energy-reserve',
      creditCost,
      transactionEnergy: input.transactionEnergy
    };
  }

  return {
    ready: true,
    reason: 'ready',
    creditCost,
    transactionEnergy: input.transactionEnergy,
    creditsAfter: input.credits - creditCost,
    terminalEnergyAfter:
      input.terminalEnergy - input.transactionEnergy
  };
}

Complete one-time buy example

State impact: this code may execute one real market deal. It writes a request snapshot and never retries automatically.

module.exports.loop = function () {
  const request = Memory.market?.dealRequest;

  if (!request || request.enabled !== true) {
    return;
  }

  const terminal = typeof request.terminalId === 'string'
    ? Game.getObjectById(request.terminalId)
    : null;

  if (
    !terminal
    || terminal.structureType !== STRUCTURE_TERMINAL
    || terminal.my !== true
    || terminal.isActive() !== true
    || terminal.cooldown > 0
  ) {
    request.lastStatus = 'terminal-not-ready';
    request.lastCheckedAt = Game.time;
    return;
  }

  const reviewed = getReviewedSellOrder(request);
  if (!reviewed.ready) {
    request.lastStatus = reviewed.reason;
    request.lastCheckedAt = Game.time;
    return;
  }

  const order = reviewed.order;
  const transactionEnergy =
    Game.market.calcTransactionCost(
      request.amount,
      terminal.room.name,
      order.roomName
    );
  const plan = evaluateBuyDeal({
    request,
    order,
    credits: Game.market.credits,
    terminalEnergy:
      terminal.store.getUsedCapacity(RESOURCE_ENERGY),
    transactionEnergy
  });

  request.lastStatus = plan.reason;
  request.lastCheckedAt = Game.time;

  if (!plan.ready) {
    request.preview = {
      currentPrice: order.price,
      currentAmount: order.amount,
      remainingAmount: order.remainingAmount,
      creditCost: plan.creditCost ?? null,
      transactionEnergy:
        plan.transactionEnergy ?? transactionEnergy
    };
    return;
  }

  request.enabled = false;
  request.status = 'submitted';
  request.submittedAt = Game.time;
  request.snapshot = {
    orderId: order.id,
    orderType: order.type,
    resourceType: order.resourceType,
    orderRoomName: order.roomName,
    yourRoomName: terminal.room.name,
    amount: request.amount,
    price: order.price,
    orderAmountBefore: order.amount,
    remainingAmountBefore: order.remainingAmount,
    creditsBefore: Game.market.credits,
    terminalEnergyBefore:
      terminal.store.getUsedCapacity(RESOURCE_ENERGY),
    creditCost: plan.creditCost,
    transactionEnergy: plan.transactionEnergy
  };

  const result = Game.market.deal(
    order.id,
    request.amount,
    terminal.room.name
  );

  request.result = result;
  request.resultAt = Game.time;
  request.status = result === OK
    ? 'accepted-pending-settlement'
    : 'failed-review-required';

  console.log(JSON.stringify({
    type: 'market-deal-result',
    orderId: order.id,
    roomName: terminal.room.name,
    resourceType: order.resourceType,
    amount: request.amount,
    price: order.price,
    creditCost: plan.creditCost,
    transactionEnergy: plan.transactionEnergy,
    result
  }));
};

Disable before calling deal

Market conditions can change between ticks. A failed call must not repeat automatically with an old order snapshot, old price ceiling, or old budget. Re-enable only after reviewing the current order and request.

The market can change after preflight

Another player may consume the order, and the official documentation says the closer player takes precedence when multiple players try the same deal. Your Terminal or Credits can also change because of another module. Preflight reduces mistakes but cannot reserve the order. The API return code remains authoritative.

Centralize the 10-deal account limit

function reserveMarketDealSlot() {
  Memory.market ??= {};

  if (Memory.market.dealSlotTick !== Game.time) {
    Memory.market.dealSlotTick = Game.time;
    Memory.market.dealSlotsUsed = 0;
  }

  if (Memory.market.dealSlotsUsed >= 10) {
    return false;
  }

  Memory.market.dealSlotsUsed += 1;
  return true;
}

This local counter coordinates your modules, but only the actual API result proves whether the account-wide limit is available. Reserve the slot immediately before the call and do not run independent deal loops.

Verify settlement on a later tick

function findMatchingIncomingTransaction(snapshot) {
  return Game.market.incomingTransactions.find(tx =>
    tx.time >= snapshot.submittedAt
    && tx.order?.id === snapshot.orderId
    && tx.resourceType === snapshot.resourceType
    && tx.amount === snapshot.amount
    && tx.to === snapshot.yourRoomName
  ) || null;
}

Also compare Credits, Terminal Store, Energy, order amount, and the saved snapshot. Transaction arrays are finite recent records, so verify promptly and avoid treating a missing old record as definitive proof of failure.

Handle return codes

CodeInterpretationReview
OKDeal scheduledTransactions, Credits and Store later
ERR_NOT_OWNERNo usable Terminal in the selected roomRoom and Terminal ownership
ERR_NOT_ENOUGH_RESOURCESCredits, resource, or transaction Energy insufficientDirection-specific budgets
ERR_FULLMore than 10 deals attempted this tickGlobal market scheduler
ERR_INVALID_ARGSOrder, amount, or room invalidFresh order snapshot
ERR_TIREDTerminal cooling downterminal.cooldown

Debugging checklist

  • Use one reviewed order ID.
  • Fetch the order again immediately before execution.
  • Verify direction and resource type.
  • Use order.amount as the current ceiling.
  • Enforce a maximum price.
  • Calculate Credit cost and reserve.
  • Calculate Terminal Energy cost and reserve.
  • Check Terminal ownership, activity, and cooldown.
  • Coordinate the 10-deal account limit.
  • Disable before calling.
  • Save snapshot and return code.
  • Verify transactions and balances later.

Scope and next steps

This guide does not automatically select orders, predict prices, split deals, sell to buy orders, support account-bound resources, optimize multiple Terminals, or retry after market races. Continue with direct Terminal transfers.

Frequently asked questions

Is the cheapest order always best?

No. Transfer Energy, distance, amount, urgency, reserves, and current availability also matter.

Why not use remainingAmount?

It can include capacity that is not currently active or available.

Can OK still require later verification?

Yes. OK schedules the operation; settlement evidence appears in later state.

Should an error retry automatically?

No. Refresh the order and require a new review.

Official documentation

SOURCE AND SCOPE

Review the source, evidence, or next system

This English guide is rewritten for a focused search intent while preserving the technical scope and verification boundaries of its Chinese source. Live-room evidence is claimed only when the verification record says it exists.