Start with scheduled versus settled
Game.market.deal() returning OK means the global deal intent was accepted. It does not guarantee that the requested amount will settle. During processing, the engine can reduce amount to the remaining order balance, seller inventory, buyer Terminal free capacity, or affordable Credit amount.
This guide buys an ordinary resource from one exact ORDER_SELL order. Selling into a buy order and account-bound resources need different transaction direction and resource checks.
Freeze one reviewed deal revision
function buildDealConfirmation(request) {
return [
'BUY_FROM_SELL_ORDER',
request.requestId,
request.revision,
request.orderId,
request.terminalId,
request.resourceType,
request.amount,
request.maximumPrice,
request.creditReserve,
request.energyReserve
].join('|');
}
Require a matching confirmation immediately before submission. A change to amount, price ceiling, reserves, order ID or Terminal ID creates a new operation revision rather than silently reusing old approval.
Refresh the exact sell order immediately before reserving
function evaluateReviewedSellOrder(request) {
if (!request || request.enabled !== true) {
return { ready: false, status: 'request-disabled' };
}
if (
typeof request.requestId !== 'string'
|| !Number.isInteger(request.revision)
|| request.revision < 1
|| typeof request.orderId !== 'string'
|| typeof request.terminalId !== 'string'
|| typeof request.resourceType !== 'string'
|| !Number.isInteger(request.amount)
|| request.amount <= 0
|| !Number.isFinite(request.maximumPrice)
|| request.maximumPrice <= 0
|| request.confirmation
!== buildDealConfirmation(request)
) {
return { ready: false, status: 'request-invalid' };
}
const order = Game.market.getOrderById(request.orderId);
if (!order) {
return { ready: false, status: 'order-unavailable' };
}
if (
order.id !== request.orderId
|| order.type !== ORDER_SELL
|| order.resourceType !== request.resourceType
|| typeof order.roomName !== 'string'
) {
return { ready: false, status: 'order-identity-mismatch' };
}
if (order.price > request.maximumPrice) {
return { ready: false, status: 'price-above-limit' };
}
if (order.amount <= 0) {
return { ready: false, status: 'order-amount-empty' };
}
return { ready: true, status: 'order-ready', order };
}
Do not require the current order.amount to equal or exceed the full requested amount if partial settlement is acceptable. Store the request ceiling and let later evidence report the actual amount. Projects that require all-or-nothing behavior should stop when order.amount < request.amount.
Plan current Credit and Energy reserves
function buildDealBudget(request, terminal, order) {
const requestedAmount = Math.min(
request.amount,
order.amount
);
const energyEstimate =
Game.market.calcTransactionCost(
requestedAmount,
terminal.room.name,
order.roomName
);
const creditEstimate =
requestedAmount * order.price;
if (
Game.market.credits - creditEstimate
< request.creditReserve
) {
return { ready: false, status: 'credit-reserve' };
}
if (
terminal.store.getUsedCapacity(RESOURCE_ENERGY)
- energyEstimate
< request.energyReserve
) {
return { ready: false, status: 'energy-reserve' };
}
return {
ready: true,
status: 'budget-ready',
requestedAmount,
energyEstimate,
creditEstimate
};
}
These are preflight ceilings. Another accepted operation in the same tick can consume the same Credits, Terminal Energy or free capacity unless every producer shares the coordinator below.
Reserve both the deal slot and exact Terminal
function createTerminalMarketDispatcher() {
let dealSlots = 0;
const terminalIds = new Set();
const orderIds = new Set();
return {
reserveDeal(terminalId, orderId) {
if (dealSlots >= 10) {
return { ready: false, status: 'deal-limit' };
}
if (terminalIds.has(terminalId)) {
return {
ready: false,
status: 'terminal-already-reserved'
};
}
if (orderIds.has(orderId)) {
return {
ready: false,
status: 'order-already-reserved'
};
}
dealSlots += 1;
terminalIds.add(terminalId);
orderIds.add(orderId);
return { ready: true, status: 'deal-reserved' };
},
releaseTerminal(terminalId) {
terminalIds.delete(terminalId);
}
};
}
The Terminal reservation must be shared with direct terminal.send() producers. The engine stores send and deal through different intent channels, so a deal-only call counter cannot prevent the same Terminal from being scheduled twice.
Submit one frozen operation
function submitReviewedBuyDeal(request, dispatcher) {
const reviewed = evaluateReviewedSellOrder(request);
if (!reviewed.ready) return reviewed;
const terminal = Game.getObjectById(request.terminalId);
if (
!terminal
|| terminal.structureType !== STRUCTURE_TERMINAL
|| terminal.my !== true
|| terminal.isActive() !== true
|| terminal.cooldown > 0
) {
return { status: 'terminal-not-ready' };
}
const budget = buildDealBudget(
request,
terminal,
reviewed.order
);
if (!budget.ready) return budget;
const reservation = dispatcher.reserveDeal(
terminal.id,
reviewed.order.id
);
if (!reservation.ready) return reservation;
const incomingIdsBefore =
Game.market.incomingTransactions
.map(transaction => transaction.transactionId)
.filter(Boolean);
request.enabled = false;
const pending = {
requestId: request.requestId,
revision: request.revision,
confirmation: request.confirmation,
submittedAt: Game.time,
orderId: reviewed.order.id,
orderType: reviewed.order.type,
orderRoom: reviewed.order.roomName,
terminalId: terminal.id,
targetRoom: terminal.room.name,
resourceType: reviewed.order.resourceType,
requestedAmount: budget.requestedAmount,
price: reviewed.order.price,
incomingIdsBefore
};
const result = Game.market.deal(
pending.orderId,
pending.requestedAmount,
pending.targetRoom
);
request.lastResult = result;
request.lastAttemptAt = Game.time;
request.status = result === OK
? 'accepted-pending-settlement'
: 'deal-rejected';
if (result === OK) {
Memory.pendingMarketDeals ??= {};
Memory.pendingMarketDeals[request.requestId] = pending;
} else {
dispatcher.releaseTerminal(terminal.id);
}
return { status: request.status, result };
}
The local deal slot remains consumed after a real call attempt as a conservative anti-loop policy. A rejected request stays disabled and requires a newly reviewed revision.
Treat the requested amount as a ceiling
During tick processing, actual amount may be reduced by current order balance, seller inventory, receiving Terminal capacity or available Credits. A transaction with 0 < amount < requestedAmount is not necessarily evidence of a wrong order. It is a partial settlement that must be reported explicitly.
Do not infer actual amount from the Terminal Store delta alone. Creeps, Labs, Factories, sends and other market operations can change the same Store in the same tick.
Verify the exact new transaction on the next tick
function verifyBuyDealSettlement(pending) {
if (Game.time < pending.submittedAt + 1) {
return { status: 'waiting-for-next-tick' };
}
if (Game.time > pending.submittedAt + 1) {
return { status: 'verification-window-missed' };
}
const before = new Set(pending.incomingIdsBefore);
const candidates = Game.market.incomingTransactions
.filter(transaction =>
transaction.transactionId
&& !before.has(transaction.transactionId)
&& transaction.time === pending.submittedAt
&& transaction.order?.id === pending.orderId
&& transaction.order?.type === pending.orderType
&& Math.abs(
transaction.order.price - pending.price
) < 0.0005
&& transaction.from === pending.orderRoom
&& transaction.to === pending.targetRoom
&& transaction.resourceType
=== pending.resourceType
&& transaction.amount > 0
&& transaction.amount
<= pending.requestedAmount
);
if (candidates.length === 0) {
return { status: 'accepted-no-transaction-observed' };
}
if (candidates.length > 1) {
return {
status: 'transaction-identity-ambiguous',
candidateIds: candidates.map(
transaction => transaction.transactionId
)
};
}
const transaction = candidates[0];
return {
status: transaction.amount
=== pending.requestedAmount
? 'full-deal-settlement-observed'
: 'partial-deal-settlement-observed',
transactionId: transaction.transactionId,
requestedAmount: pending.requestedAmount,
actualAmount: transaction.amount
};
}
The exact transaction ID, processing tick, order identity, price, direction, resource and bounded amount form the settlement evidence. Credits and Store changes are supporting diagnostics, not unique identity.
Preserve no-record and ambiguity states
| Status | Meaning |
|---|---|
terminal-already-reserved | A deal or send already owns this Terminal for the tick. |
accepted-pending-settlement | The deal intent was accepted; actual settlement is unknown. |
accepted-no-transaction-observed | No exact record appeared in the next snapshot. |
transaction-identity-ambiguous | Several exact new records matched. |
partial-deal-settlement-observed | One exact record settled less than requested. |
full-deal-settlement-observed | One exact record settled the complete request ceiling. |
Production integration boundary
Instantiate one dispatcher per tick and pass it to all market and logistics modules. Reserve the Terminal before the irreversible call, retain one pending record per request revision, verify it exactly on the next tick, and never retry an accepted-but-unverified operation automatically.