Skip to main content

Scheduler

oracle/src/services/scheduler/ — drives the swap lifecycle on a cron.

Three crons, one service

CronDefaultChoiceWhat it does
triggerevery 5sTriggerLifecycleByScheduler + CreateFixingEventBySchedulerWalks evolveable instruments, produces Effect contracts for due cashflows
settleNetevery 5sSettleNetBySchedulerAggregates pending effects across the netting set, exchanges margin in one transfer per ccy
matureevery 30sMatureBySchedulerArchives instruments whose final cashflow has settled

Configure under scheduler.cron.*. Crons use 6-field */N * * * * * (with seconds).

Manual override gating

scheduler:
enabled: true
manualOverridesEnabled: true # demo: human can drive
# production: false ⇒ buttons hidden

When manualOverridesEnabled: false:

  • Workspace and CSA pages hide the "Trigger / Settle / Mature" manual buttons.
  • The scheduler remains the only path to lifecycle progress.
  • Sister non-scheduler choices (TriggerLifecycle, SettleVm) are still callable on-chain by the operator — gating is UI-only, not contract-level.

Scheduler authority model

Daml 2.x has no disjunctive controllers (controller a | b doesn't exist). Two patterns make the scheduler authoritative:

1. Dual LifecycleRule

The instrument carries two LifecycleRule contracts — one signed by operator, one by scheduler. Either can Evolve.

2. Sister *ByScheduler choices

Every choice the scheduler needs has a parallel sister:

Operator-signedScheduler-signed
TriggerLifecycleTriggerLifecycleByScheduler
CreateFixingEventCreateFixingEventByScheduler
SettleVmSettleVmByScheduler
PublishMarkPublishMarkByScheduler
MatureInstrumentMatureByScheduler

Both sisters share a module-level body helper, so behaviour is identical — only the controller differs.

This was proven end-to-end in Phase 6 Stage C: the scheduler's JWT alone drives a full lifecycle, zero DAML_AUTHORIZATION_ERROR.

Files

FileRole
index.tsService entrypoint, registers crons
tick.tsOne tick — fetch ACS, decide what to evolve
effect-discovery.tsFinds Effect contracts ready to settle
holdings-resolver.tsMaps effects to backing holdings (with Fungible.Split)
instrument-events.tsWalks instruments for upcoming fixings
instrument-maturity.tsDetects matured instruments
setup-discovery.tsLocates LifecycleRule, Csa, etc. on first tick
trigger-lifecycle.tsTriggerLifecycleByScheduler invocation
settle-net.tsNetting + SettleNetByScheduler
mature.tsMatureByScheduler

Liveness pill

The frontend shows a "Scheduler ON / OFF" pill on the blotter and CSA pages. Liveness is derived on-ledger from the scheduler's own activity — the timestamp of the latest MarkToMarket / netted settlement event (app/src/shared/scheduler/use-last-tick.ts, scheduler-status-pill.tsx). No oracle /health HTTP poll is involved; if the scheduler stops producing on-chain events, the pill goes offline.

On a fresh sandbox the pill can read offline for the first few ticks before the scheduler has produced its first event — known fragility, Phase 7+ follow-up.