For the complete documentation index, see llms.txt. This page is also available as Markdown.

Common Types and Events

Two things every contract in the protocol shares: a single crate of cross-contract types, and one consistent event schema. Both exist to make the indexer (and any other off-chain consumer) buildable against a frozen, unambiguous contract surface.

The common-types crate

Soroban has no built-in way for one contract to "import" another contract's #[contracttype] definitions — each contract only ships its own compiled WASM plus an XDR spec. Before this crate existed, every contract that needed to decode a cross-contract call result (e.g. the Factory calling Globals::get_global_settings) hand-copied the relevant struct definitions into its own source. Soroban's struct encoding is a map keyed by field name, and it traps at runtime — with no compiler help — the moment a caller's hand-copied struct has a different set of fields than the real one.

That is exactly what happened on testnet: the Factory's hand-mirrored GlobalSettings drifted from the real, growing struct in Globals, and every call that decoded it (including the Factory's own upgrade) began trapping. See Testnet Deployment for the incident and Governance for the upgrade-path rule that came out of it.

common-types now holds the single, canonical definition of every cross-contract type — GlobalSettings, Manager, ManagerCollateral, Pledge/PledgeState, VaultParams — plus #[contractclient] traits (GlobalsClient, CollateralEscrowClient, ComplianceGateClient) so Factory, Vault, and CollateralEscrow all call each other through the same generated client code rather than hand-declared, driftable copies. A CI check (scripts/check-type-drift.sh) guards against a hand-mirrored composite type reappearing anywhere in the workspace.

Event schema

Every state-changing function across every contract emits exactly one event, generated from that contract's events.rs. All events share the same topic shape:

("trilo", <event_name: Symbol>, <schema_version: u32>, ...any other #[topic] fields)

schema_version starts at 1 and increments only on a breaking change to that event's data struct — an indexer can therefore key its parser on (event_name, schema_version) and know exactly what shape to expect, even across a contract upgrade.

globals

Event
Topic fields
Data fields

RoleUpdated

role

new_address

ProtocolPaused

—

paused

ManagerApproved

manager

—

ManagerRemoved

manager

—

AssetWhitelisted

asset

—

AssetRemoved

asset

—

CollateralAssetWhitelisted

asset

—

CollateralAssetRemoved

asset

—

FeeUpdated

—

new_fee

SettingsUpdated

caller

protocol_fee, min_loan_amount, max_loan_amount

CollateralDeposited

manager

token, amount, total_staked

CollateralWithdrawn

manager

amount, total_staked

CollateralLocked

manager

amount, total_locked

CollateralReleased

manager

amount, total_locked

CollateralSlashed

manager

amount, remaining_staked

ManagerMarkedDelinquent

manager

delinquency_count

ManagerDelinquencyCleared

manager

—

ManagerCreditLimitSet

manager

max_credit

ManagerOutstandingUpdated

manager

outstanding_principal

BorrowerOutstandingUpdated

borrower

outstanding_principal, total_outstanding

ComplianceGateSet

—

gate: Option<Address>

factory

Event
Topic fields
Data fields

VaultDeployed

vault, manager, borrower

principal, loan_term, interest_rate, leverage_ratio, related_party

WasmHashUpdated

—

old_hash, new_hash

vault

Event
Topic fields
Data fields

VaultCreated

borrower

principal, loan_term, interest_rate, leverage_ratio, junior_target, senior_target, related_party

InvestorDeposited

investor

tranche, amount, junior_raised, senior_raised

InvestorWithdrew

investor

tranche, amount

JuniorLocked

—

junior_raised, junior_target

VaultFullyFunded

—

total_funded

LoanDisbursed

borrower

amount

PhaseTransition

—

from, to

PaymentReceived

payer

amount, reserve_paid, senior_interest_paid, senior_principal_paid, junior_interest_paid, junior_principal_paid, residual, outstanding_s, outstanding_j, schedule_advanced

YieldClaimed

investor

tranche, amount

CashWithdrawn

borrower

amount

LateFeeApplied

—

fee, fee_senior, fee_junior, total_late_fees, missed_payments

VaultDefaulted

borrower

outstanding_s, outstanding_j, missed_payments

RecoveryDistributed

—

senior_recovery_pool, junior_recovery_pool, manager_residual, borrower_residual

RecoveryClaimed

investor

tranche, amount

RenegotiationProposed

manager

new_interest_rate, new_loan_term

RenegotiationFinalized

—

manager, borrower, new_interest_rate, new_loan_term, new_installment

RenegotiationRejected

caller

—

ProtocolFeeCollected

treasury

fee_amount, source

ProtocolFeeClamped

—

configured_fee, charged_fee

InvestorAllowlisted

investor

—

InvestorRemovedFromAllowlist

investor

—

VaultFundingExpired

—

deadline, pending_refund

VaultApprovalExpired

—

deadline, pending_refund

RefundClaimed

investor

tranche, amount

ReportReceived

—

reported_at

ReportingOverdue

—

last_report_at, checked_at

collateral-escrow

Event
Topic fields
Data fields

PledgeLocked

borrower

pledge_id, token, amount, controller

PledgeReleased

—

pledge_id, amount

PledgeSeized

to

pledge_id, amount

compliance-allowlist

Event
Topic fields
Data fields

AllowlistUpdated

who

action, allowed

Every event's schema_version field is also present in the data payload, not just the topic tuple — so a consumer that only inspects data (not topics) can still branch on it.

As of September 2026.

Last updated