Rates Engine v0.5.0-rc.80
Pre-release[v0.5.0-rc.80] — 2026-05-26
Fixed
sorobanevents.AsyncSinkcursor-drop incoherence (ADR-0029,
superseding the original buffer-full-drop design). The
2026-05-26 fill walk dropped ~18.86M rows of ~4.66B (~0.40%)
across all 12 parallel chunks becausePushEventdropped rows
on a full channel while the backfill cursor advanced per
produced ledger — so the dropped rows had no recovery path
(-resumeshort-circuited at the "already complete" branch).
The fix:PushEventnow blocks on a full channel (back-pressure
into the dispatcher) so the cursor cannot outrun durable writes.
Stop() closes a newstoppingchannel that unblocks any
in-flight producers (counted as dropped — shutdown-race only).
The backfill driver and live indexer both watch ctx and call
Stop early on cancellation so a hung Postgres can't deadlock the
hot path past SIGTERM. ADR-0029 updated with the post-mortem;
runbook to reset the 12 soroban-events cursors and re-walk is
in follow-up rc.80 ops. Regression-tested via
internal/sources/sorobanevents/dispatcher_adapter_test.go(no
drops under sustained back-pressure; Stop releases blocked
producers; pending rows drain on shutdown without channel-close
panic).
Added
-
Comet Balancer-v1 liquidity events end-to-end (#26). The
decoder previously claimed only(POOL, swap)and silently
dropped every other Comet event under the sharedPOOL
namespace. It now decodes all five events the Soroban port of
Balancer-v1 emits —swapcontinues to land intrades; the
four liquidity-mutating kinds (join_pool,exit_pool,
deposit,withdraw) land in a newcomet_liquidity
hypertable (migration 0042; PK includestokenso multi-token
joins from the same op don't collide). Each row carries the
add/remove direction explicitly so dashboardsSUM(amount) WHERE direction = 'add'without re-encoding the kind mapping.
withdrawrows also carrypool_amount_in— the BPT (pool-
share) token count burned in exchange for the underlying.
Documented ininternal/sources/comet/README.md. Verified
2026-05-26 against upstreamcomet-contracts-v1main: the
EVM-Balancer-v1 admin events (bind/rebind/unbind/
finalize/gulp/set_swap_fee/set_controller/
set_public_swap) are NOT in the Stellar port — either the
function doesn't exist or it's storage-only with no event
publication. BPTtransferevents go through the SEP-41
standard token-event surface and are already claimed by
internal/sources/sep41_supplywhen the pool is in scope.
Historical fill plan: walksoroban_events(migration 0041)
for the pre-rc back-window — aratesengine-ops comet-backfill
subcommand wrappingdecodeLiquidityEventis the cleanest path
and is tracked as a follow-up. Updated wasm audit at
docs/operations/wasm-audits/comet.md. -
Soroswap
skimevent handler +soroswap_skim_eventshypertable
(#28). Closes the "every emitted Soroswap pair-contract topic
gets classified" gap.TopicSymbolSkimhad been declared in
internal/sources/soroswap/events.gosince the package was first
written but was unreachable throughclassify()— the 5th
pair-contract event (alongside swap/sync/deposit/withdraw) was
silently dropped by the dispatcher. The Decoder now decodes
SkimEvent { skimmed_0, skimmed_1 }(tolerant of the
amount_0/amount_1Uniswap-v2-derivative aliases per
contract-schema-evolution.md), pulls an optionaltoAddress
field when a future WASM upgrade adds it, and emits a new
soroswap.SkimEventconsumer.Event the pipeline sink lands as a
row in a newsoroswap_skim_eventshypertable (migration 0043;
PK leads withledger_close_timeper TS103; amounts NUMERIC per
ADR-0003; compression after 7 days segmented bycontract_id).
Skim is not a trade — never feeds VWAP, never lands in the
tradeshypertable. Historical fill is aINSERT … SELECT FROM soroban_events WHERE topic_0_sym = 'skim' AND contract_id IN (<pair set>)query (ADR-0029 raw landing zone) — operator
runbook follow-up after the initial backfill window lands. -
Phoenix liquidity + stake event decoders (#27). Phoenix's pool
contract (volatilecontracts/pool/+ stableswap
contracts/pool_stable/) and per-pool stake contract emit four
N-events-per-action shapes the indexer previously silently dropped:
provide_liquidity(5 events: sender, token_a, token_a-amount,
token_b, token_b-amount),withdraw_liquidity(4 events: sender,
shares_amount, return_amount_a, return_amount_b, plus an optional
5thauto unbonded),bond(3 events: user, token, amount), and
unbond(3 events: same shape as bond). The existing 8-event swap
reassembly is extended to a per-action correlation-buffer fleet —
one map per action so a same-(ledger,tx,op) bond+unbond pair can't
collide on shared field names. Two new TimescaleDB hypertables
back the reads:phoenix_liquidity(provide + withdraw rows,
per-pool / per-sender / per-action indexes) and
phoenix_stake_events(bond + unbond rows, per-contract / per-user
/ per-action indexes; migration 0044). Both partition daily on
ledger_close_time, compress segment-by (pool, action) /
(stake_contract, action) after 7 days. Per ADR-0003, all i128
amounts ride NUMERIC; PKs includeledger_close_timeper
TimescaleDB TS103 (the lesson from migration 0041). Historical
fill follow-up: oncesoroban_events(ADR-0029) covers the
Soroban era, populate both tables viaINSERT … SELECT FROM soroban_events WHERE topic_0_sym IN ('provide_liquidity', 'withdraw_liquidity','bond','unbond')fed through the same
per-action correlation buffer — pending the per-WASM-hash decoder
audit log being extended to enumerate the new field strings. -
Blend money-market decoder (#25, per [[project_every_event_principle]]).
Extendedinternal/sources/blend/decode.go::classify()to handle
the 18 event topics that were silently dropped: supply,
withdraw, supply_collateral, withdraw_collateral, borrow, repay,
flash_loan, gulp, claim, bad_debt, defaulted_debt,
reserve_emission_update, gulp_emissions, set_admin, update_pool,
queue_set_reserve, cancel_set_reserve, set_reserve, set_status,
deploy. New hypertablesblend_positions,blend_emissions,
blend_adminvia migration 0045. Live ingest captures every
event going forward; historical fill viaINSERT … SELECT FROM soroban_events WHERE contract_id IN (<blend pool contracts>) AND topic_0_sym IN (…)once the soroban_events fill walk lands.
Changed
- CCTP + Rozo flipped to
BackfillSafe = trueafter WASM-history
audit (#21). Walk on 2026-05-26:ratesengine-ops wasm-history -from 60000000 -to 62642779 -parallel 4across all 6 mainnet
contracts (3 CCTP + 3 Rozo) — 5h02m wall, 2,642,780 ledgers
scanned, ZERO WASM upgrades observed (output JSONranges=null
per contract). CCTP's three contracts each have their own WASM
(one per role: token messenger / message transmitter / token
minter); Rozo's three contracts share a single WASM hash
b56aedeaf80c3d4b…(per stellar.expert + RozoAI's
internal/sources/rozo/events.goconfirmation that all three
emit identicalPaymentEvent/FlushEventschemas). Decoder
coverage was already complete; with single-WASM-per-contract
confirmed for the audit range, no decoder drift risk for
historical replay.internal/sources/external/registry.go's
BackfillSafeflag flippedfalse → truefor both. Historical
replay now unblocked viaINSERT … SELECT FROM soroban_events
per the canonical query shape in
docs/operations/wasm-audits/cctp.md+rozo.md. Walk evidence
archived to/tmp/wasm-history-bridges.jsonon r1.