Indexing and interpretation are separate backend stages. Indexing writes raw encrypted audit rows from chain events. Interpretation decrypts those rows and writes normalized audit_interpretation rows used by cases, reports, and review screens. This page describes portal infrastructure. It is not a partner SDK integration guide.

End-to-end pipeline

Scanner runtime components

ComponentResponsibility
ScannerModule.registerRegisters scanner providers for configured Stellar and Solana chain names
StellarRpcModule.registerLoads a chain row by name, validates type = stellar, creates a Stellar RPC client
LedgerScannerSchedulerServiceRuns bootstrap scan and scheduled scan ticks
StellarLedgerScannerServiceLists contracts for one Stellar chain and scans each contract
performStellarContractScanCalculates ledger span from checkpoint, RPC retention, and scanner config
collectStellarAuditRowsReads ledger pages and collects accepted audit rows
buildAuditFromStellarEventMaps Soroban event and calldata into an audit upsert object
persistScanResultsQueryUpserts audit, writes audit_chain, updates scan checkpoints

Contract selection

The scanner does not scan arbitrary chain activity.
chain -> contracts(chain_id) -> scanner service
applications.association.contract_id links an Arcane application to a registered contract. That link is used later by application-scoped API routes and disclosure workflows.

Ledger span selection

For each contract, the scanner determines:
InputSource
Previous checkpointScan checkpoint for (contract_id, chain_id)
Initial hintChain-specific initial ledger config
RPC lower boundRPC ledger retention metadata
RPC tipLatest ledger sequence
Batch sizeScanner batch configuration
The next scan starts after the previous checkpoint and clamps to the RPC lower bound when needed. If the next ledger is past the RPC tip, the scan is skipped for that contract.

Event parsing

For each ledger page:
  1. Read ledger metadata from RPC.
  2. Extract contract events for the registered contract address.
  3. Ignore events that are not in successful contract calls.
  4. Read transaction calldata from ledger metadata when available.
  5. Map the current audit topic to the raw audit event type.
The current Stellar privacy-pool contract emits AuditEncodedDigest under the audit topic with message_name = "transact".

Raw audit row

The scanner writes one audit row per accepted event.
ColumnSource
contract_idRegistered contracts.id
tx_idEvent transaction hash
soroban_event_idStellar event id; unique with contract_id
event_typetransact for current AuditEncodedDigest events
cyphertextSealed audit payload from the event value
created_atLedger close time
interpretedfalse until interpretation succeeds
signer_accountSigner resolved from transaction calldata, fallback unknown
public_signals_jsonParsed transaction public signals
interpretation_errorLast interpretation error, if any
The scanner also writes audit_chain and advances scan checkpoints. The audit table is unique by (contract_id, soroban_event_id).

Interpretation runner

AuditInterpretationRunnerService runs on a 12-second interval. Batch behavior:
  1. Open a database transaction.
  2. Lock up to 25 uninterpreted audit rows.
  3. Route Solana confidential-token rows to interpretCtAuditRow.
  4. Route all other rows to interpretStellarAuditRow.
  5. Replace interpretation rows for each successfully interpreted audit row.
  6. Store a truncated interpretation error on the source audit row if interpretation fails.

Stellar interpretation path

interpretStellarAuditRow performs:
StepDetail
Decode-key checkRequires audit.contract.decoding_key
Public signal parsingValidates audit.public_signals_json
Payload decodeCalls decryptAndDecodeTransaction(cyphertext, decoding_key)
PlanningCalls planInterpretations(tx, publicSignals)
PersistenceReplaces rows in audit_interpretation for audit_id
Error captureWrites audit.interpretation_error
Interpretation is backend processing. It does not grant auditor visibility by itself.

Interpreted records

audit_interpretation rows are normalized records.
ColumnDescription
audit_idSource raw audit row
seqSequence when one audit row creates multiple interpreted rows
kindNormalized kind: deposit, withdraw, or transfer
payloadJSONB payload used by case review and reports
amount_stroopsParsed amount when available
The unique key is (audit_id, seq).

Event type versus interpretation kind

LevelExampleMeaning
Raw eventtransactContract-call shape indexed from chain
Interpretation kinddeposit, withdraw, transferNormalized branch planned from decrypted transaction data and public signals
A single raw transact event can produce multiple interpreted rows.

Failure behavior

FailureEffect
No contracts for chainScanner logs and returns
RPC retention moved past checkpointStart ledger clamps to the oldest retained ledger
RPC/network failureScan cycle logs an error; checkpoint is not advanced for failed work
Parser returns no audit rowEvent is ignored
Duplicate eventUpsert updates the existing audit row
Missing decoding keyInterpretation error is written to audit.interpretation_error
Decryption or planning failureInterpretation error is written; indexing is not rolled back