Reading the payloads
Custom events below use fixed SorobanSymbol topics. Match each symbol in
order. Standard library events can also place indexed fields in topics.
Payload encoding is one of three shapes:
UpdatePositionBatchEvent uses vec. PoolMarketStateBatchEvent and
PoolMarketParamsBatchEvent use single-value. All other events below use map.
A nested #[contracttype] struct with named fields encodes as an
alphabetically-keyed map; one with unnamed fields (a tuple struct) encodes as
a vector in declaration order.
Field tables use the numeric scales
defined in Data types. Raw asset units use token decimals; read those from the
token contract or PoolMarketParamsBatchEvent. Pool timestamps are milliseconds.
PositionAction values
PositionAction is a unit-only enum with #[repr(u32)], so it appears on the
wire as a plain u32. It tags each leg inside UpdatePositionBatchEvent.
Shared payload types
EventAccountAttributes
A tuple struct — a 3-entry vector: [owner: Address, spoke_id: u32, mode: EventPositionMode].
EventPositionMode is a u32 enum: None = 0 (a normal, non-strategy
position), Multiply = 1, Long = 2, Short = 3. It is the wire form of
PositionMode, with Normal mapped to None.
EventDepositDelta
A tuple struct — a 10-entry vector:
EventBorrowDelta
A tuple struct — a 6-entry vector: [action, hub_id, asset, scaled_amount (RAY), index_ray (RAY), amount (raw asset units)].
Controller events
UpdatePositionBatchEvent
- Topics:
["position", "batch_update"] - Format:
vec— a 4-entry vector, not a map. - Emitted by:
supply,withdraw,borrow,repay,liquidate,multiply,flash_position,swap_debt,swap_collateral,repay_debt_with_collateral,migrate_from_blend,update_account_threshold.
The event is not published when both lists are empty.
One operation can publish more than one batch. A
SeizeMode::Credit
liquidation publishes the liquidated account’s batch first, then a receiving
account batch only if there are net credit updates. Key on account_id; never assume one
operation yields one batch. The receiver’s batch is supply-side only and omits
any leg whose net credit is zero.
LiquidationEvent
- Topics:
["position", "liquidation"]· Format:map
This event carries no seizure or protocol-fee figure. Those live in the
accompanying batch’s
LiqSeize legs (gross) and, in share-credit mode, its
LiqCredit legs (net).
FlashLoanEvent
- Topics:
["position", "flash_loan"]· Format:map - Fields:
hub_id: u32,asset: Address,receiver: Address,caller: Address,amount: i128(raw),fee: i128(raw).
FlashPositionEvent
- Topics:
["position", "flash_position"]· Format:map - Fields:
account_id: u64,hub_id: u32,asset: Address,receiver: Address,caller: Address,amount: i128(requested debt),amount_received: i128(measured receiver receipt),fee: i128(always 0). Amounts use raw token units. The account keeps the minted debt.
FlashPos; collateral
deposited from the callback is tagged Supply, identically to an ordinary
deposit. Do not read FlashPos as the collateral leg.
ClaimRevenueEvent
- Topics:
["revenue", "claim"]· Format:map - Fields:
hub_id: u32,asset: Address,caller: Address,accumulator: Address,amount: i128(raw token units). - Emitted by
claim_revenuewhen the controller receives a positive payout.
amount is the controller’s measured receipt forwarded to the accumulator. It
does not measure the accumulator’s final receipt if that transfer under-delivers.
AccountDelegateEvent
- Topics:
["account", "delegate"]· Format:map - Fields:
account_id: u64,owner: Address,delegate: Address,granted: bool. - Published only when the delegate list actually changed.
CleanBadDebtEvent
- Topics:
["debt", "bad_debt"]· Format:map - Fields:
account_id: u64,total_borrow_usd_wad: i128,total_collateral_usd_wad: i128. Values describe the account before cleanup. - Emitted by
clean_bad_debt,force_socialize_bad_debt, and byliquidatewhen the post-liquidation account still holds eligible bad debt.
UpdatePositionBatchEvent for
the cleanup itself.
Market events
Both are maps. They carry
hub_id: u32, max_borrow_rate: i128,
base_borrow_rate: i128, slope1: i128, slope2: i128, slope3: i128,
mid_utilization: i128, optimal_utilization: i128, max_utilization: i128,
and reserve_factor: u32. Rates and utilization use RAY; reserve factor uses BPS.
CreateMarketEvent identifies its token as base_asset: Address and adds
market_address: Address. UpdateMarketParamsEvent uses asset: Address.
They omit flash-loan settings and asset decimals. Read those fields from
PoolMarketParamsBatchEvent.
Strategy events
Configuration events
EventSpoke is a map with spoke_id: u32, is_deprecated: bool,
liquidation_target_hf_wad: i128, hf_for_max_bonus_wad: i128, and
liquidation_bonus_factor_bps: u32.
UpdateSpokeAssetEvent carries the full post-change config, including paused,
frozen, and no_seize. It does not carry the flags epoch. Read
get_spoke_asset_flags_epoch before proposing RelaxSpokeAssetFlags recovery.
A later flag write invalidates the old recovery operation (#319).
Pool events
PoolMarketStateBatchEvent
- Topics:
["market", "batch_state_update"] - Format:
single-value— the payload is the vector of rows. There is no wrapping map and noupdateskey. - Emitted by:
supply,borrow,withdraw,repay,seize_positions,net_settle,update_indexes,claim_revenue,flash_loan,recapitalize, andcreate_strategy. Not published when the snapshot list is empty.
PoolMarketParamsBatchEvent
- Topics:
["market", "batch_params_update"] - Format:
single-value— the payload is the vector of rows. - Emitted by:
create_marketandupdate_params; both publish exactly one row.
PoolMarketParamsEvent row is a map with hub_id: u32,
asset: Address, and params: MarketParamsRaw — the
market’s full post-change parameters, including flash-loan settings and asset
decimals.
StrategyFeeEvent
- Topics:
["strategy", "fee"]· Format:map - Fields:
hub_id: u32,asset: Address,amount: i128(gross principal),fee: i128(withheld),amount_sent: i128(amount − fee). Amounts use raw token units.amount_sentis the transfer request, not a measured receipt. - Published only when the fee is non-zero.
Governance events
Price-aggregator events
UpdateAssetOracleEvent — topics ["config", "asset_oracle"], format map,
fields key: PriceKey and oracle: AssetOracle. Emitted by set_oracle,
set_sanity_band, and set_tolerance.
DeFindex-strategy events
HarvestEvent — topics ["strategy", "harvest"], format map, fields from: Address, amount: i128 (always 0; harvest moves no funds), and price_per_share: i128 (12 decimals, floor-rescaled from the RAY supply index).
Indexing rules
- Match emitter address and ordered topics. A topic rename requires a decoder update. Persist successful contract events; failed diagnostic events are not committed position changes.
- Store
HubAssetKeyfields together; do not index by token address alone — the same token on two hubs is two markets. - Key
swap_debtopens onSwDebtR, which tags both legs. - Do not assume bad-debt cleanup precedes the position batch: every
UpdatePositionBatchEventis published beforeCleanBadDebtEvent. Credit(0)has no controller-specific account-creation event. Its receiving batch carriesaccount_attributeswhen net credits exist, and the call returns the id. Track standard NFT mint events for token creation and read account metadata when the lending batch is absent.flash_positionrefunds are silent: no refund event exists anywhere, andFlashPositionEventcarries no refund field. The only on-chain trace is the token contract’s owntransferevent, controller to caller.- Position ownership changes are observable only through the position-NFT contract’s standard non-fungible transfer semantics; this protocol defines no event for them.

