> For the complete documentation index, see [llms.txt](https://docs.bsc.lista.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bsc.lista.org/for-developer/lista-lending/events-and-callbacks.md).

# Events & Callbacks

Moolah hands control back to you in two ways:

* **Callbacks** — synchronous, in-transaction hooks that Moolah invokes on the caller mid-execution (before it pulls the tokens it is owed), enabling atomic flows such as leverage loops, flash-liquidations, and position migrations.
* **Events** — the logs Moolah, the IRM, and the vault layer emit for off-chain indexers, subgraphs, and monitoring.

For function signatures, structs, and the `Id`/`MarketParams` types referenced below, see the [Contract & Interface Reference](/for-developer/lista-lending/contract-reference.md). For the higher-level provider/broker model, see [Integration Patterns](/for-developer/lista-lending/integration-patterns.md).

***

## Callbacks

Moolah follows the Morpho-style callback pattern: several core entry points optionally call back into `msg.sender` after updating internal accounting but **before** transferring in the assets the caller owes. This lets an integrator source those assets inside the same transaction — for example, minting/swapping collateral, or repaying a loan out of seized collateral — so the whole operation is atomic and reverts as a unit if any leg fails.

### Firing rule

Each entry point takes a trailing `bytes data` argument. For **supply**, **repay**, **supplyCollateral**, and **liquidate**, the callback fires **only when `data` is non-empty** (`data.length > 0`); pass empty `data` to skip it. **`flashLoan` is the exception**: its callback is always invoked, because the flash loan has no purpose without it.

The callback is always made on `msg.sender`, so the contract that calls Moolah must be the contract that implements the interface.

### Callback interfaces

All five interfaces are declared in `moolah/interfaces/IMoolahCallbacks.sol`.

| Interface                         | Method                                                 | Fired by           | Fires when       |
| --------------------------------- | ------------------------------------------------------ | ------------------ | ---------------- |
| `IMoolahSupplyCallback`           | `onMoolahSupply(uint256 assets, bytes data)`           | `supply`           | `data` non-empty |
| `IMoolahRepayCallback`            | `onMoolahRepay(uint256 assets, bytes data)`            | `repay`            | `data` non-empty |
| `IMoolahSupplyCollateralCallback` | `onMoolahSupplyCollateral(uint256 assets, bytes data)` | `supplyCollateral` | `data` non-empty |
| `IMoolahLiquidateCallback`        | `onMoolahLiquidate(uint256 repaidAssets, bytes data)`  | `liquidate`        | `data` non-empty |
| `IMoolahFlashLoanCallback`        | `onMoolahFlashLoan(uint256 assets, bytes data)`        | `flashLoan`        | always           |

The first argument is the settled amount for that action: assets supplied/repaid/supplied-as-collateral, `repaidAssets` for a liquidation, or the flash-loaned amount. The `data` argument is the same opaque payload you passed into the originating call, so you can encode a route, a target market, or step parameters and decode them inside the callback.

### Execution order (the atomic window)

Within the originating call, Moolah:

1. Updates its own accounting.
2. Emits the corresponding event (`Supply`, `Repay`, `SupplyCollateral`, `Liquidate`, `FlashLoan`).
3. For the actions where Moolah owes **you** assets, transfers them out now — the seized collateral for `liquidate`, the loaned token for `flashLoan`. (`supply`, `repay`, and `supplyCollateral` have no outbound transfer; you are the one paying in.)
4. Calls back into `msg.sender` (subject to the firing rule above), handing you control while the position is mid-update.
5. On return, pulls in the assets **you** owe via `transferFrom` (the supplied/repaid/collateral token, or the flash-loaned amount plus zero fee). If your balance or allowance is short, the whole transaction reverts.

Your callback body runs in step 4, so it must leave `msg.sender` holding enough of the inbound token (and allowance to Moolah) for step 5 to succeed.

### Typical atomic flows

> **Read this before designing a flow.** Moolah inherits a **single global** reentrancy guard, and eight entry points carry it — `supply`, `withdraw`, `borrow`, `repay`, `supplyCollateral`, `withdrawCollateral`, `liquidate` and `liquidateBrokerPosition`. `flashLoan` is the **only** callback entry point without it. So from inside `onMoolahSupply`, `onMoolahRepay`, `onMoolahSupplyCollateral` or `onMoolahLiquidate` you **cannot re-enter any of those eight** — they revert `ReentrancyGuardReentrantCall()`. Views, `accrueInterest` and `flashLoan` carry no guard and remain callable, but no position-mutating composition is possible. Multi-step Moolah composition is possible only from `onMoolahFlashLoan`. This is a deliberate divergence from upstream Morpho Blue, where the equivalent flows do work, so do not carry a Morpho design over unchanged.

| Flow                          | Callback used       | Sketch                                                                                                                                                                                                                                        |
| ----------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Leverage loop**             | `onMoolahFlashLoan` | Flash-loan the loan token; in the callback swap it to collateral, `supplyCollateral`, then `borrow` enough to repay the flash loan. Must be built on `flashLoan` — the `supplyCollateral` callback cannot borrow.                             |
| **Flash-liquidation**         | `onMoolahLiquidate` | Call `liquidate` with `data`; Moolah sends you the seized collateral first, then calls back. Swap that collateral on an **external** venue to raise the repayment. No Moolah call is needed inside the callback, which is why this one works. |
| **Debt / position migration** | `onMoolahFlashLoan` | Flash-loan the loan token, use it to repay a position elsewhere, withdraw the freed collateral, move it, and re-borrow to repay the flash loan.                                                                                               |
| **Zero-capital deleverage**   | `onMoolahFlashLoan` | Flash-loan the loan token, `repay` with it, `withdrawCollateral`, swap part of the collateral back to the loan token and repay the flash loan. Not available via `onMoolahRepay`, which cannot call `withdrawCollateral`.                     |

> Callbacks are a low-level primitive. If you are integrating in TypeScript and do not need custom atomic routing, the [Moolah Lending SDK](/for-developer/lista-lending/sdk.md) builds standard supply/borrow/repay transactions for you.

***

## Events

### Core market events (`Moolah`)

Signatures and indexed parameters are in `moolah/libraries/EventsLib.sol` — read them from there or from the ABI. `Id` is the market identifier (the keccak hash of `MarketParams`). Five things will catch you out when reconstructing state from these logs:

* **Position reconstruction.** A supplier's balance evolves through `Supply`/`Withdraw`; a borrower's through `Borrow`/`Repay`/`Liquidate`; collateral through `SupplyCollateral`/`WithdrawCollateral`. All are keyed by the indexed `Id` and the indexed `onBehalf` (or `borrower`) address.
* **Interest.** `AccrueInterest` carries the `prevBorrowRate` used for the elapsed period, the `interest` added to the market, and the `feeShares` minted to the fee recipient. Note that the fee recipient can receive shares during accrual **without** a `Supply` event, so reconstruct fee-recipient balances from `AccrueInterest`, not `Supply`.
* **Repay/Liquidate rounding.** The repaid amount — `assets` on `Repay`, `repaidAssets` on `Liquidate` — may exceed the market's `totalBorrowAssets` by 1 due to rounding. Account for this when reconciling.
* **Bad debt.** On `Liquidate`, non-zero `badDebtAssets`/`badDebtShares` mean the position was underwater and the loss was socialized to suppliers of that market.
* **Authorization.** Watch `SetAuthorization` (and `IncrementNonce` for signature-based authorizations) to track which managers can act on a position via the `authorized`/`onBehalf` model.

Lista-specific administrative events (provider/broker wiring, whitelists, blacklists, min-loan, default fee) are also declared in the same `EventsLib` and documented alongside the functions that emit them in the [Contract & Interface Reference](/for-developer/lista-lending/contract-reference.md).

### Interest-rate model events (`InterestRateModel`)

The IRMs emit their own events, and **three of them are emitted by both IRMs with byte-identical signatures**, so they share a `topic0` — `BorrowRateCapUpdate`, `BorrowRateFloorUpdate` and `MinCapUpdate` come from either `InterestRateModel` or `FixedRateIrm`. Filter by the emitting address, not by topic alone, or you will file fixed-rate broker-market logs as adaptive-curve events. Only `BorrowRateUpdate` is exclusive to the adaptive curve (`FixedRateIrm` declares it but never emits it, since its `borrowRate` is `view`). `BorrowRateUpdate` is emitted each time Moolah accrues interest against a market that uses this IRM (Moolah calls `IIrm.borrowRate` during `_accrueInterest`), so it is the finest-grained rate signal available to indexers.

`avgBorrowRate` is the average per-second rate (scaled by WAD) applied over the accrual interval; `rateAtTarget` is the model's rate at target utilization after the update. Fixed-rate broker markets use a separate `FixedRateIrm`, which emits `SetBorrowRate(Id indexed id, int256 newBorrowRate)` when its rate is set.

### Vault events (`MoolahVault`)

The ERC-4626 curator vault layer emits the standard `Deposit`/`Withdraw`/`Transfer` events plus its own lifecycle and configuration set, declared in `moolah-vault/libraries/EventsLib.sol`. Two declared event groups require special handling:

**Do not index the vault's `Submit*` / `Revoke*` events.** `SubmitCap`, `SubmitTimelock`, `SetTimelock`, `SubmitGuardian`, `SetGuardian`, `SubmitMarketRemoval` and the `RevokePending*` family are all declared in `EventsLib` but emitted **nowhere**. They are inherited declarations; Lista uses two external OpenZeppelin `TimelockController`s per vault instead of the in-contract pending/timelock mechanism. Their addresses appear in `CreateMoolahVault` as `managerTimeLock` and `curatorTimeLock`; watch those for `CallScheduled` / `CallExecuted` / `Cancelled`. `SetCap` is emitted directly by the vault, with no preceding `SubmitCap`.

`SetCurator` and `SetIsAllocator` are declared in the same library and likewise never emitted. The vault uses OpenZeppelin `AccessControl`, so role changes surface as `RoleGranted` / `RoleRevoked` filtered on the `CURATOR` / `ALLOCATOR` role hashes.

To reconstruct how a vault allocates deposits across underlying Moolah markets, track `SetSupplyQueue`/`SetWithdrawQueue` for ordering, `SetCap` for per-market limits, and `ReallocateSupply`/`ReallocateWithdraw` for actual moves.

### Vault allocator events (`VaultAllocator`)

The public reallocation helper (`src/vault-allocator/VaultAllocator.sol`) declares its events in `vault-allocator/libraries/EventsLib.sol`.

A public reallocation emits one `PublicWithdrawal` per market it pulls from and a `PublicReallocateTo` for the market it supplies into, all correlated by the indexed `vault`.

***

## See also

* [Smart Contract](/for-developer/lista-lending/smart-contract.md) — deployed contract addresses.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.bsc.lista.org/for-developer/lista-lending/events-and-callbacks.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
