Skip to content

feat(agents): validated config, chaos tests, event triggers and cross-market arbitrage (#105 #106 #107 #108) - #247

Open
ExcelDsigN-tech wants to merge 20 commits into
OracleDesk:developfrom
ExcelDsigN-tech:feat/agent-config-chaos-events-arbitrage
Open

ExcelDsigN-tech wants to merge 20 commits into
OracleDesk:developfrom
ExcelDsigN-tech:feat/agent-config-chaos-events-arbitrage

Conversation

@ExcelDsigN-tech

Copy link
Copy Markdown

Closes #105
Closes #106
Closes #107
Closes #108

develop repair (first commits)

develop does not build: agents/package.json and package-lock.json were invalid JSON and bin/market-maker.ts / bin/trader.ts / tsconfig.json held spliced copies from the #188 merge (restored from #188's own 5833afb^1). Two test bugs it was hiding are fixed (signing.test.ts hash decoding, fpmm vector arg parsing). Left for maintainers: treasury error codes 44-48 assigned twice (#182), contracts/oracledesk-types missing imports, spec-hash Cargo.lock needing rustc 1.91 vs the pinned 1.90, and #188's runtime tasks missing the #185/#186 features.

#107 Typed, validated configuration

  • agents/core/config.ts: loadAgentConfig, EnvSchema, DeploymentsSchema, describeConfig, NetworkProbe; zod pinned 4.6.5
  • strict int/bps/stroop parsing, every error reported at once; canonical passphrase + RPC per network; deployment schema with StrKey checks, network match, DEPLOYMENTS_FILE and *_CONTRACT_ID overrides (logged); live key must match the deployment's agent
  • mainnet interlock lists every unmet condition; probes mandatory live, best effort dry-run, --offline (dry-run only); missing vs archived contracts reported distinctly
  • --print-config redacts the secret; trader, market-maker and agentd no longer read process.env; adapter mode handling exhaustive (assertNever, mode-only message) and dry-run refuses a secret
  • tests: core/config.test.ts (offline, fake probe), adapter constructor and run() invalid-mode cases

#106 Fake RPC and chaos tests

  • agents/test/fake-rpc: JSON-RPC 2.0 with real XDR, real WASM served from ledger entries, fake ledger clock (Date.now follows it), sequence/signature/time-bound enforcement, FPMM contract model, scripted and seeded faults
  • agents/test/chaos: real live adapter and trader/maker flows; invariants: no double submission, traced or journaled-pending trades, circuit halts repeated failures; npm run test:chaos in the CI Node job
  • fixed (found by the harness): adapter never set allowHttp; quoteBuy returned the Result wrapper
  • TODO for the lifecycle issue (): FAILED-on-chain transactions resolve as success; trader re-trades after a lost response; maker never reconciles an ambiguous create. Interface drift: vendored reasoning_registry WASM and the registry binding take publish_trace(action: String), the adapter and source use TraceAction.

#105 Event triggers

  • core/events: decode.ts (binding specs via Spec.parseEvent, nullable MarketResolved outcome, Unknown variant), subscriber.ts (cursor pagination, ack-before-checkpoint, start at tip, retention-gap resync, idle advance, failed-call filter, RPC filter limits), dispatch.ts (per-market order, bounded concurrency, persisted LRU, retries, dead-letter JSONL), handlers.ts (TradeReevaluate, ProposalWatch with ledger-time dispute deadline, FinalityTracker, Resync)
  • adapter rpcServer() / contractIds(); trader --follow (runtime/follow.ts); checkpoints in EVENTS_STATE_DIR keyed by network and contracts; .events/ gitignored; docs/architecture.md separates triggers from the indexer

#108 Cross-market arbitrage

  • collateralForShares in core/fpmm.ts and model/fpmm_model.py, shared fixtures
  • core/strategies/arbitrage.ts: implies/complement/exclusive, compatibility validation, void-aware payoff tables, ternary search + ±64 scan within max_trade, max_market headroom, max_daily, ARB_MAX_NOTIONAL, profit thresholds
  • core/risk.ts checkArbPlan; core/strategies/arb-executor.ts: sequential legs, re-quote, ARB_MAX_LEG_LOSS unwind via agent_sell, journal + crash recovery, trace with relation, graph hash, snapshots, plan and results
  • ARB_ENABLED in the trader; configuration and risks in docs/architecture.md

Verification

cd oracledesk-stellar/agents
npm run build -> clean
npm test -> 210/211 (pre-existing spec-hash rustc 1.91 failure)
npm run test:chaos -> 36 pass, 5 todo, 0 fail
cd .. && python3 model/fpmm_model.py -> passes
make test / make demo -> blocked by develop's contract breakage (see above)

The OracleDesk#188 merge left two scripts blocks and a missing comma, so npm could not
parse the file. Keep one block: test also runs runtime/ and sim/, agentd and
coverage scripts are kept, and coverage covers the same test globs.
- bin/market-maker.ts and bin/trader.ts: restore the runtime-wrapper
  versions from the agentd branch (5833afb^1); the merge kept three spliced
  copies of each, which did not parse
- tsconfig.json: one include list covering runtime/ and sim/
- signing.test.ts: hashTrace returns hex, so decode it instead of building
  an empty Uint8Array from the string
- fpmm.test.ts: settle_value vectors carry an outcome name, so only parse
  numeric args as BigInt
A merge dropped the closing brace, so the lockfile was not valid JSON and
npm ci could not run.
…locks

- core/config.ts: loadAgentConfig(), EnvSchema, DeploymentsSchema,
  describeConfig() and NetworkProbe (zod pinned at 4.6.5)
  - strict integer, bps and stroop parsing; every error reported together
  - canonical passphrase and default RPC per network, mismatches rejected
  - deployment record schema with StrKey checks, network match (absent =
    testnet), DEPLOYMENTS_FILE and *_CONTRACT_ID overrides (logged)
  - live mode needs a key matching the role's deployment address
  - mainnet interlock lists every unmet condition
  - RPC passphrase and contract-instance probes: mandatory live, best effort
    dry-run, skipped by --offline (dry-run only) and --print-config;
    missing and archived instances get distinct, actionable errors
- trader, market-maker and agentd load config instead of reading
  process.env; --print-config prints the redacted config and exits
- StellarAdapter: discriminated options, exhaustive mode switches with
  assertNever (mode only in the message), dry-run refuses a secret
- wiring.ts builds adapters and deps from the validated config
- tests: core/config.test.ts (offline, fake probe) and adapter mode cases

Closes OracleDesk#107
…terlock in .env.example

Drops TRADER_MARKET_ID / TRADER_MARKET_YES_BPS / TRADER_FAIR_VALUE_BPS and
POLYMARKET_READ_ONLY_API_URL, which no agent code reads.
The OracleDesk#188 merge left two scripts blocks and a missing comma, so npm could not
parse the file. Keep one block: test also runs runtime/ and sim/, agentd and
coverage scripts are kept, and coverage covers the same test globs.
- bin/market-maker.ts and bin/trader.ts: restore the runtime-wrapper
  versions from the agentd branch (5833afb^1); the merge kept three spliced
  copies of each, which did not parse
- tsconfig.json: one include list covering runtime/ and sim/
- signing.test.ts: hashTrace returns hex, so decode it instead of building
  an empty Uint8Array from the string
- fpmm.test.ts: settle_value vectors carry an outcome name, so only parse
  numeric args as BigInt
A merge dropped the closing brace, so the lockfile was not valid JSON and
npm ci could not run.
Client.from and the resolver ledger read rejected any http:// RPC because
allowHttp was never set, so live mode could not talk to a local network
(http://localhost:8000/soroban/rpc). Allow it exactly when the URL is http.
quote_buy returns Result<i128, Error>, so the client hands back Ok { value }.
quoteBuy cast that object to bigint and the trader's min_shares_out math then
threw before any trade could be sent. Unwrap it, and throw on Err.
- JSON-RPC 2.0 over HTTP: getHealth, getNetwork, getLatestLedger,
  getLedgerEntries, simulateTransaction, sendTransaction, getTransaction,
  getEvents, all answering with SDK shapes and real base64 XDR
- serves real WASM from ledger entries so the SDK's Client.from builds the
  client from the contract spec; compiled build preferred, vendored copy as
  fallback, with a per-contract report of which path fits the adapter
- fake ledger clock; sequence numbers, signatures and time bounds enforced
- contract model for agent_buy(_v2), agent_create_market, quote_buy and
  publish_trace using core/fpmm.ts, with error codes from each WASM's spec
- scriptable and seeded fault plans: latency, timeouts, HTTP 5xx, JSON-RPC
  errors, TRY_AGAIN_LATER, DUPLICATE, txBAD_SEQ/txTOO_LATE/
  txINSUFFICIENT_FEE, delayed NOT_FOUND, contract errors, sim/apply
  divergence, connection reset after acceptance
- adapter: every fault kind through the real live StellarAdapter
- flows: no double submission, every executed trade has a published or
  journaled pending trace, repeated failures open the circuit and halt
- seeded chaos runs are reproducible and keep the trace invariant
- npm run test:chaos; test/ is now typechecked by npm run build
- behaviour that needs the separate transaction-lifecycle work is marked
  todo: failed-on-chain transactions resolving as success, re-trading after
  a lost response, reconciling an ambiguous create

Closes OracleDesk#106
…-events-arbitrage

# Conflicts:
#	oracledesk-stellar/agents/bin/market-maker.ts
#	oracledesk-stellar/agents/bin/trader.ts
#	oracledesk-stellar/agents/package.json
#	oracledesk-stellar/agents/tsconfig.json
The SDK's transaction time bounds and buildTrace timestamps read Date.now(),
so a seed's tx hashes, and whether two ticks shared a trade journal key,
depended on the wall clock and made the reproducibility test flaky under
load. Also check the trace invariant for seeds 7 and 9, which hit a lost
response.
- core/events/decode.ts: typed OracleDeskEvent decoded from the binding
  specs (Spec.parseEvent) for market-core, resolver, treasury and
  reasoning-registry; MarketResolved has a nullable outcome, anything else
  is Unknown
- core/events/subscriber.ts: getEvents polling with cursor pagination,
  {cursor, lastLedger} saved only after the batch is acknowledged, start at
  the tip, retention-gap resync, idle checkpoint advance, failed contract
  calls dropped, RPC filter limits enforced
- core/events/dispatch.ts: per-market order, bounded concurrency across
  markets, persisted LRU of processed ids, retries with backoff,
  dead-letter JSONL
- core/events/handlers.ts: TradeReevaluate, ProposalWatch (exposure and
  live-proposal check, ledger-time dispute deadline), FinalityTracker,
  Resync
- StellarAdapter: rpcServer(), contractIds(), treasuryPosition(),
  resolutionState(); getMarket also returns status and reserves
- trader --follow (runtime/follow.ts) re-evaluates only the markets events
  point at; without it the trader is unchanged
- EVENTS_* settings validated in core/config.ts

Closes OracleDesk#105
The minimum collateral_in for which calcBuy returns at least a target share
count: a closed-form constant-product estimate, grossed up for the fee, then
a bounded binary search against calcBuy so integer rounding cannot make it
wrong. Mirrored in model/fpmm_model.py; both sides replay the same fixtures
(scripts/fpmm-diff/collateral_for_shares.jsonl, generated with
`python3 model/fpmm_model.py --write-collateral-fixtures`). Kept out of the
shared vectors.jsonl, which math.rs also replays, since it is off-chain only.
…rnaled execution

- core/strategies/arbitrage.ts: implies / complement / exclusive relations
  from ARB_RELATIONS_FILE (validated: markets open, close-time skew, matching
  resolution mechanism and source, optional on-chain resolution_hash check;
  canonical graph hash); payoff tables over allowed outcomes and joint or
  independent void scenarios on top of existing holdings; exact FPMM costs;
  integer ternary search plus a +/-64 scan inside treasury and ARB caps;
  minimum absolute and percentage profit
- core/risk.ts: checkArbPlan for treasury caps, exposure, market headroom,
  unhedged first leg, open legs and unwind retries
- core/strategies/arb-executor.ts: less liquid leg first with
  min_shares_out, re-read and ARB_MAX_LEG_LOSS check before leg two, unwind
  with agent_sell (exact sellable collateral), every step journaled before
  and after submission, crash recovery from the journal and positions, trace
  with relation, graph hash, snapshots, plan and results
- runtime/tasks/arbitrage.ts + trader ARB_ENABLED; adapter riskCaps,
  marketSpent and market fee; ARB_* validated in core/config.ts

Closes OracleDesk#108
@drips-wave

drips-wave Bot commented Oct 21, 2026

Copy link
Copy Markdown

@ExcelDsigN-tech Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant