This document provides a reference for indexers and block explorers to consume events emitted by the Liquifact Escrow contract.
All events follow the Soroban contractevent format. Key fields like invoice_id and investor are marked as topics to enable efficient filtering by indexers.
- Topic 0: Contract ID (provided by Soroban host).
- Topic 1: Event Name (Symbol, e.g.,
funded,escrow_sd). - Topic 2:
invoice_id(Symbol) — present in most events. - Topic 3:
investor(Address) — present in funding and claim events.
Emitted once by init(). Carries the escrow snapshot plus immutable bound references so
indexers can register funding_token, treasury, and optional registry without follow-up reads.
Topics:
escrow_ii(Symbol)
Data Payload:
escrow(InvoiceEscrow)funding_token(Address) — equalsDataKey::FundingTokentreasury(Address) — equalsDataKey::Treasuryregistry(Option<Address>) — equalsDataKey::RegistryRefhas_maturity_lock(bool) — false whenmaturity == 0, meaning settlement has no maturity time lock
Example (JSON Decoded):
{
"topics": ["escrow_ii"],
"data": {
"escrow": { "invoice_id": "INV_001", "status": 0 },
"funding_token": "CTOKEN...",
"treasury": "GTREAS...",
"registry": "GREG...",
"has_maturity_lock": true
}
}Emitted when admin calls lower_max_unique_investors while the escrow is open.
Topics:
inv_cap(Symbol)invoice_id(Symbol)
Data Payload:
old_cap(u32)new_cap(u32)
Emitted when an investor deposits principal.
Topics:
funded(Symbol)invoice_id(Symbol)investor(Address)
Data Payload:
amount(i128)funded_amount(i128)status(u32)investor_effective_yield_bps(i64)
Example (JSON Decoded):
{
"topics": ["funded", "INV_001", "G...INVESTOR"],
"data": {
"amount": "1000000000",
"funded_amount": "5000000000",
"status": 0,
"investor_effective_yield_bps": 500
}
}Emitted when the SME finalizes the escrow after maturity.
Topics:
escrow_sd(Symbol)invoice_id(Symbol)
Data Payload:
funded_amount(i128)yield_bps(i64)maturity(u64)settled_at_ledger_timestamp(u64) — the ledger timestamp whensettlewas calledsettle_pool(i128) — realized settlement pool:total_principal + floor(total_principal × yield_bps / 10_000). Computed fromFundingCloseSnapshot.total_principalusing the same checked arithmetic ascompute_investor_payout. Zero on legacy escrows that pre-date the snapshot key.
Example (JSON Decoded):
{
"topics": ["escrow_sd", "INV_001"],
"data": {
"funded_amount": "10000000000",
"yield_bps": 500,
"maturity": 1714184400,
"settled_at_ledger_timestamp": 1714184400,
"settle_pool": "10500000000"
}
}Emitted when an investor records their payout claim.
Topics:
inv_claim(Symbol)invoice_id(Symbol)investor(Address)
Example (JSON Decoded):
{
"topics": ["inv_claim", "INV_001", "G...INVESTOR"],
"data": null
}Emitted when the admin extends an existing funding deadline while the escrow is still open.
Topics:
fund_ext(Symbol)invoice_id(Symbol)
Data Payload:
old_deadline(u64): prior funding deadline ledger timestamp.new_deadline(u64): strictly later funding deadline ledger timestamp.
Notes:
- This event is additive; existing funding and settlement events are unchanged.
- The entrypoint rejects equal or earlier deadlines, elapsed funding windows, closed escrows, and deadlines at or after maturity.
Example (JSON Decoded):
{
"topics": ["fund_ext", "INV_001"],
"data": {
"old_deadline": 1714180000,
"new_deadline": 1714183600
}
}Emitted when an admin adds or removes an investor from the allowlist. This event is
emitted per-address even when the change is performed via the batch entrypoint
set_investors_allowlisted, so indexers receive one InvestorAllowlistChanged event
for each address in the batch.
Topics:
al_set(Symbol)invoice_id(Symbol)investor(Address)
Data Payload:
allowed(u32):1for allowed,0for blocked.
Notes:
- Batch mutations via
set_investors_allowlistedemit oneal_setevent per affected investor to preserve parity with individualset_investor_allowlistedcalls. - Existing indexers that only consume
al_setdo not need to change — the per-investor event sequence is identical regardless of whether it originated from a single call or a batch call.
Emitted once per set_investors_allowlisted call, after all per-investor
InvestorAllowlistChanged events have been emitted in the same transaction.
Topics:
al_batch(Symbol)
Data Payload:
invoice_id(Symbol): the escrow invoice identifier.batch_size(u32): total number of investors processed in this batch.allowed(u32):1if the batch allowed investors,0if it blocked them.
Why al_batch exists:
The per-investor al_set events emitted by set_investors_allowlisted are
structurally identical to those emitted by the single-address entrypoint
set_investor_allowlisted. Without an additional marker, an indexer cannot
determine whether a run of al_set events in a transaction came from one
batch call or many individual calls. al_batch provides that disambiguation
in a single event.
Relationship to al_set:
al_batch supplements — it does not replace — the per-investor al_set events.
Both are emitted for every set_investors_allowlisted call:
- N ×
al_set(one per address, in input order) - 1 ×
al_batch(after the loop, carrying the full count)
Backward compatibility:
Existing indexers that only subscribe to al_set remain fully compatible. The
al_batch event is purely additive and is never emitted by the single-address
set_investor_allowlisted entrypoint.
Auditor usage:
Auditors can cross-check batch_size against the number of al_set events in
the same transaction to verify that no per-investor events were dropped or
duplicated during a batch operation.
Emitted when the admin calls rebind_registry_ref (including via the clear_registry_ref
convenience wrapper). Signals that the off-chain registry hint stored at DataKey::RegistryRef
has changed. This event carries no settlement authority — it exists purely so off-chain
indexers can re-sync their local pointer without polling the contract.
Topics:
reg_rebind(Symbol)invoice_id(Symbol)
Data Payload:
registry(Option<Address>): new hint value.Nonemeans the pointer was cleared (unbound state).
Non-authority guarantee:
The emitted address is a discoverability hint only. No on-chain logic in the escrow contract
reads DataKey::RegistryRef when moving funds, settling, or authorizing any call. Presence of
a Some(addr) value does not imply registry membership — query the registry contract directly
to verify on-chain state.
Integrator guidance:
None(unbound): no off-chain registry is currently associated with this escrow. Treat as "not registered" for UI/UX purposes.Some(addr)(bound): an off-chain indexer hint is set; verify membership with the registry ataddrif authoritative state is required.- On receiving this event, re-sync any cached pointer and do not infer fund-flow changes.
Example (JSON Decoded):
{
"topics": ["reg_rebind", "INV_001"],
"data": {
"registry": "CREG..."
}
}Related entrypoints: rebind_registry_ref, clear_registry_ref, get_registry_ref.
See also: docs/escrow-registry-ref.md.
Emitted when an admin toggles the compliance hold.
Topics:
legalhld(Symbol)invoice_id(Symbol)
Data Payload:
active(u32):1for enabled,0for cleared.
Emitted when an admin toggles the lightweight operational pause via set_paused.
Orthogonal to LegalHoldChanged — it signals the incident-response circuit
breaker (no compliance semantics, no clear delay), not the compliance hold.
Topics:
paused(Symbol)invoice_id(Symbol)
Data Payload:
active(u32):1for enabled,0for cleared.
Emitted when the SME clears the metadata-only collateral commitment recorded under
DataKey::SmeCollateralPledge.
Topics:
coll_clr(Symbol)invoice_id(Symbol)
Data Payload:
asset(Symbol): SME-reported off-chain asset label from the stored commitment.amount(i128): SME-reported amount from the stored commitment.recorded_at(u64): ledger timestamp from the original commitment record.
Indexer guidance:
Use coll_clr to remove or mark retired the active collateral commitment for the
invoice. This event is metadata-only and does not prove custody, asset movement,
or enforceable collateral.
To track all activity for a specific invoice, indexers should filter for events where Topic 2 matches the invoice_id.
To track an investor's portfolio, filter for events where Topic 3 matches the investor's Address. This applies to EscrowFunded and InvestorPayoutClaimed.
Payloads are XDR-encoded. Use the liquifact_escrow WASM/interface or the Stellar SDK to decode the data field into the corresponding Rust structs.