Sidecars

Arena

Runs repeatable payment and position scenarios, including response loss, without a network.

0.3.0-alpha.1Alpha capabilityDeclared targets: Windows, Androiduk.capp.sidecar.solana-arena

What it does

Arena is an offline execution simulator for payments and position lifecycles. Use it to prove that your interface handles success, rejection, expiry and a lost reply before involving an RPC service or wallet.

It retains outcomes by operation ID and can model opening, increasing, reducing and closing a position. It does not place orders, discover market prices, settle assets or create a real exchange position. Every result is labelled simulated: true and fundsMoved: false.

Use independently

Select uk.capp.sidecar.solana-arena in deterministic-local mode. No peers or network are needed. The owning API is window.capp.solana.arena; the portable client is an optional thin wrapper.

Choose a disposable test workspace and keep each scenario's operation and position IDs. Reusing the exact request returns the retained outcome; changing an existing operation's payload is an identity conflict, not another trade.

Provider dependencies

UseRequired componentsBoundary
Direct simulationArena onlyNo network transaction occurs.
Exercise submission/recoveryDispatch in arena-peer mode; optionally Witness in arena-peer modeDispatch retains the uncertain attempt; Witness observes Arena, not the chain.
Exercise a position's allocationOptional Circuit and SwitchyardYour application links simulation evidence to their local records. Arena does not advance those records automatically.
Publish a small devnet evidence transferA separately configured devnet Edge compositionIts wallet approval and actual transfer are separate from the simulated outcome.

First successful scenario

  1. Start with failureMode: none and finality: simulated-finalised; these are build settings, not arguments to execute().
  2. Execute one transaction or an open with a position ID, positive size and entry price. Inspect the returned state and simulation flags.
  3. Repeat the exact request and verify the same outcome, not a second position increase.
  4. Close the position using a new operation ID but the same position ID. Read position() to see the retained closed lifecycle.
  5. In a separate fixture, choose drop-response. Catch the error and call read() for the saved operation instead of inventing another operation.

A repeatable position step

Copy the client module beside this application module. Call the helper from an explicit rehearsal action; it does nothing on module load. Supply the same inputs when resuming the same step.

js
import {createSolanaClient} from './solana-client.mjs';
const client = createSolanaClient(() => window.capp?.solana);

export async function runArenaStep({operationId, clientPositionId, action, size, price, reduceSize}) {
  const status = await client.call('arena', 'status');
  if (!status.ready || status.providerMode !== 'deterministic-local') {
    throw new Error('A ready local Arena is required');
  }
  const request = {operationId, clientPositionId, action, network: 'demo'};
  if (size !== undefined) request.size = size;
  if (price !== undefined) request.price = price;
  if (reduceSize !== undefined) request.reduceSize = reduceSize;
  const bytes = new TextEncoder().encode(JSON.stringify(request));
  const hash = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));
  request.messageDigest = [...hash].map(n => n.toString(16).padStart(2, '0')).join('');
  let outcome;
  try {
    outcome = await client.call('arena', 'execute', request);
  } catch (error) {
    if (error.code !== 'CAPP_SOLANA_ARENA_RESPONSE_DROPPED') throw error;
    outcome = await client.call('arena', 'read', {operationId, messageDigest: request.messageDigest});
  }
  if (outcome.simulated !== true || outcome.fundsMoved !== false) {
    throw new Error('Unexpected simulation boundary');
  }
  return outcome;
}

An opening call can use {operationId: 'docs-open-1', clientPositionId: 'docs-position-1', action: 'open', size: 2, price: 100}. Close it with {operationId: 'docs-close-1', clientPositionId: 'docs-position-1', action: 'close'}. These are synthetic numbers, not a trading recommendation or market observation. Always inspect the returned state: a resolved promise can contain rejected or expired.

Failure and finality choices

SettingObservable resultRecovery
noneA retained confirmed/finalised simulationRead or replay the exact request.
rejectA retained rejected result, without position executionDisplay rejection; a new scenario needs a new ID.
expireA retained expired resultKeep the outcome as evidence of this test.
drop-responseThe outcome is saved, then RESPONSE_DROPPED is thrownRead the same operation; do not execute another identity.

simulated-confirmed stays a configured simulated result; repeated reads do not turn it into simulated-finalised. A latency setting delays the simulated work, not a real network.

Scope and limitations

Its trades, P&L and settlement are simulations. A 1/10,000 devnet evidence transfer proves only that small transfer and its memo. Arena itself does not calculate a trusted P&L or price feed. Close/sweep flags describe the synthetic lifecycle, not an on-chain fund return.

evidenceTransfer() requires a retained finalised outcome. It uses integer floor division of the original SOL lamports by the divisor, reports the remainder and refuses a result below one lamport. requiresSeparateTransfer: true means exactly that: it neither signs nor sends. Token evidence scaling is not this interface.

Local outcomes and positions are application/origin-scoped. Full stores stop admitting new records rather than evicting unresolved evidence. Keep scenario IDs stable after a reload; clearing storage is not a recovery strategy for a real payment.

Application API

The owning interface is window.capp.solana.arena. The portable client calls the same methods. This table describes main arguments, not a stricter typed guarantee than the current provider implements. Exact transaction/provider bindings must be preserved; unsupported state or arguments are rejected.

js
import {createSolanaClient} from './solana-client.mjs';
const client = createSolanaClient(() => window.capp?.solana);
const status = await client.call('arena', 'status');

status

client.call('arena', 'status', input)

Main input: No arguments required.

Read the deterministic simulator configuration. Network is demo and simulation is explicit.

execute

client.call('arena', 'execute', input)

Main input: operationId, messageDigest; optional attemptId, network: demo, action, clientPositionId, market, side, size, price, reduceSize.

Run the configured simulated outcome, including rejection, expiry or a lost-response scenario. Never performs a network transaction.

read

client.call('arena', 'read', input)

Main input: operationId.

Read the retained simulated outcome instead of executing a new operation.

position

client.call('arena', 'position', input)

Main input: clientPositionId or positionId.

Read the saved simulated position lifecycle.

evidenceTransfer

client.call('arena', 'evidenceTransfer', input)

Main input: operationId, originalAmountLamports; optional attemptId, divisor.

Propose a scaled SOL evidence amount from a successful simulated outcome. Requires a separately authorized transfer; it is not live settlement.

Troubleshooting

Symptom or errorNext step
DEMO_ONLYPass network: demo; Arena cannot execute devnet or mainnet transactions.
ID_CONFLICTCompare the complete request, including action, size and price. Keep the saved request for retries.
POSITION_EXISTSUse increase for an existing open position, or a new position ID for a new lifecycle.
POSITION_NOT_FOUNDRead the saved position; a close/reduction requires an open position.
RESPONSE_DROPPEDRecover with read() using the same operation and binding.
SCALING or evidence STATERequire a finalised simulation and an integer scaled amount of at least one lamport.

Verify your integration

Exercise all four failure settings, replay each accepted request and verify that a changed request is refused. Test open, partial reduction, full close and a repeated close. Assert simulated, fundsMoved and the absence of real network calls, not just the presence of a signature-shaped string.

Integration recipes

Composition · Devnet payment · Recovery · Typed client · Schemas

These schemas describe payload shape, not evidence of approval or successful execution. Match the version in your payload; older versions remain listed for compatibility.

Configuration reference

These are the selected package's configuration fields. String credential values and installation-specific defaults are never published here. A field's presence does not bypass its runtime policy.

FieldTypeMeaning and constraints
failureModeselectDeterministic laboratory outcome used to exercise recovery paths.. Choices: none, drop-response, reject, expire
latencyMsnumberArtificial delay before Arena returns its deterministic outcome.. Bounds: 0 to 30000
feeLamportsnumberSynthetic fee exposed to policy/UI tests; never charged on-chain.. Bounds: 0 to 1000000000
finalityselectSynthetic finality label returned by Arena; always remains explicitly simulated.. Choices: simulated-confirmed, simulated-finalised
maxOperationsnumberMaximum local records. At the limit, new records stop; existing history is preserved. Increase the limit or archive and reconcile the workspace.. Bounds: 1 to 2048
syntheticEntryPricenumberSynthetic position entry price used only by the local position lifecycle model.. Bounds: 1e-06 to 1000000000
maxPositionsnumberMaximum local records. At the limit, new records stop; existing history is preserved. Increase the limit or archive and reconcile the workspace.. Bounds: 1 to 1024

Agent interfaces

These names are declared by the package for host/Agent integration, not JavaScript methods to invoke directly. The host must register a tool, grant its scope and enforce its approval policy. A manifest declaration alone does not prove availability in every Agent catalogue. Prefer the application API above when integrating your own app.

ToolDeclared riskApproval requirement
solana.arena.statuslowmode-policy

Component changelog

baseline0.3.0-alpha.1Permalink

Arena

Runs repeatable payment and position scenarios, including response loss, without a network.

Documentation snapshot, not a historical release date.
View component notes →
↑ ↓ to navigate · Enter to open · Esc to close. Search stays in your browser.