> ## Documentation Index
> Fetch the complete documentation index at: https://docs.canopy.deal/llms.txt
> Use this file to discover all available pages before exploring further.

# Canopy Protocol Events: Complete Reference for Integrators

<Tip>
  All `indexed` parameters can be used as filter topics in `eth_getLogs` calls, enabling efficient server-side filtering without downloading and scanning every event. For high-volume deployments, combine multiple indexed filters to narrow queries to the exact offer, series, or participant you care about.
</Tip>

***

## OfferCreated

Emitted when a seller successfully deposits collateral and registers a new offer. An `unlockTime` of `0` indicates a spot offer; any non-zero value indicates a deferred forward offer.

```solidity theme={null}
event OfferCreated(
    uint256 indexed offerId,
    address indexed seller,
    address indexed asset,
    address quote,
    uint256 requestedAssetAmount,
    uint256 escrowedAssetAmount,
    uint256 pricePerUnit,
    uint64 expiry,
    uint64 unlockTime,
    address allowedBuyer
);
```

<ResponseField name="offerId" type="uint256" required>
  Sequential identifier for the newly created offer. Indexed — use as a topic filter to track a specific offer across all subsequent events.
</ResponseField>

<ResponseField name="seller" type="address" required>
  The address that created the offer and deposited the collateral. Indexed — filter by seller to retrieve all offers from a specific wallet.
</ResponseField>

<ResponseField name="asset" type="address" required>
  The ERC-20 token address of the collateral asset being sold. Indexed — filter by asset to retrieve all offers for a specific token.
</ResponseField>

<ResponseField name="quote" type="address">
  The ERC-20 token accepted as payment. Not indexed.
</ResponseField>

<ResponseField name="requestedAssetAmount" type="uint256">
  The amount the seller requested to deposit. For standard tokens this equals `escrowedAssetAmount`; for fee-on-transfer tokens it may be higher.
</ResponseField>

<ResponseField name="escrowedAssetAmount" type="uint256">
  The actual amount of collateral received and escrowed by the venue, measured by balance delta. This is the true starting `assetRemaining` for the offer.
</ResponseField>

<ResponseField name="pricePerUnit" type="uint256">
  The quote-token cost per one whole unit (`10^assetDecimals`) of the asset token.
</ResponseField>

<ResponseField name="expiry" type="uint64">
  Unix timestamp after which the offer can no longer be filled.
</ResponseField>

<ResponseField name="unlockTime" type="uint64">
  Unix timestamp at which claim tokens become redeemable. A value of `0` identifies this as a spot offer.
</ResponseField>

<ResponseField name="allowedBuyer" type="address">
  The address permitted to fill the offer, or `address(0)` for a public offer with no fill restriction.
</ResponseField>

***

## OfferFilled

Emitted on every fill, whether partial or complete. A single offer may emit multiple `OfferFilled` events as it is filled incrementally by one or more buyers.

```solidity theme={null}
event OfferFilled(
    uint256 indexed offerId,
    address indexed buyer,
    uint256 assetUnits,
    uint256 assetReceived,
    uint256 quoteCost,
    uint256 sellerQuoteReceived,
    uint256 feeCharged,
    uint256 feeReceived,
    uint256 buyerQuoteDebited
);
```

<ResponseField name="offerId" type="uint256" required>
  Identifier of the offer that was filled. Indexed — join with `OfferCreated` on this field to associate fills with their originating offer.
</ResponseField>

<ResponseField name="buyer" type="address" required>
  The address that executed the fill and paid the quote tokens. For deferred offers, this is also the address that receives the minted claim tokens. Indexed.
</ResponseField>

<ResponseField name="assetUnits" type="uint256">
  The number of asset units requested in this fill, in base units.
</ResponseField>

<ResponseField name="assetReceived" type="uint256">
  The net asset tokens actually received by the buyer (spot) or credited as claim tokens (deferred). May differ from `assetUnits` for fee-on-transfer asset tokens in spot fills.
</ResponseField>

<ResponseField name="quoteCost" type="uint256">
  The base quote cost for this fill before fees, computed via the cumulative `mulDiv` formula.
</ResponseField>

<ResponseField name="sellerQuoteReceived" type="uint256">
  The net quote tokens actually received by the seller after any transfer fees.
</ResponseField>

<ResponseField name="feeCharged" type="uint256">
  The nominal protocol fee computed as `quoteCost * feeBps / 10000`.
</ResponseField>

<ResponseField name="feeReceived" type="uint256">
  The net fee amount actually received by the fee recipient after any transfer fees.
</ResponseField>

<ResponseField name="buyerQuoteDebited" type="uint256">
  The total amount of quote tokens debited from the buyer's balance (cost + fee + any transfer taxes). This is the buyer's true total outlay.
</ResponseField>

***

## OfferCancelled

Emitted when the seller cancels the remaining unfilled portion of an offer. After this event, the offer accepts no further fills, and the escrowed remainder is returned to the seller.

```solidity theme={null}
event OfferCancelled(
    uint256 indexed offerId,
    uint256 assetDebited,
    uint256 assetReceived
);
```

<ResponseField name="offerId" type="uint256" required>
  Identifier of the cancelled offer. Indexed.
</ResponseField>

<ResponseField name="assetDebited" type="uint256">
  The amount of asset collateral debited from the venue's escrow (the full unfilled remainder).
</ResponseField>

<ResponseField name="assetReceived" type="uint256">
  The net amount of asset tokens actually received by the seller. For fee-on-transfer tokens, this may be less than `assetDebited`.
</ResponseField>

***

## SeriesCreated

Emitted the first time a deferred offer is filled for a unique `(asset, unlockTime)` pair. This event signals that a new claim token contract has been deployed and that the series is now active. Subsequent deferred fills sharing the same pair do not re-emit this event — they silently join the existing series.

```solidity theme={null}
event SeriesCreated(
    bytes32 indexed seriesId,
    address indexed asset,
    uint64 unlockTime,
    address claimToken
);
```

<ResponseField name="seriesId" type="bytes32" required>
  The deterministic series identifier, derived as `keccak256(abi.encode(asset, unlockTime))`. Indexed.
</ResponseField>

<ResponseField name="asset" type="address" required>
  The ERC-20 asset token address for this series. Indexed.
</ResponseField>

<ResponseField name="unlockTime" type="uint64">
  The Unix timestamp at which claim tokens for this series become redeemable.
</ResponseField>

<ResponseField name="claimToken" type="address">
  The address of the newly deployed ERC-20 claim token contract for this series. Store this address in your indexer — it is the token that buyers receive when filling deferred offers in this series.
</ResponseField>

***

## Redeemed

Emitted when a claim token holder burns their tokens after the unlock time and receives the underlying collateral. Redemption is permissionless — any holder can redeem any amount of their claim tokens once `block.timestamp >= unlockTime`.

```solidity theme={null}
event Redeemed(
    bytes32 indexed seriesId,
    address indexed holder,
    uint256 amount,
    uint256 assetReceived
);
```

<ResponseField name="seriesId" type="bytes32" required>
  The series identifier from which collateral was redeemed. Indexed.
</ResponseField>

<ResponseField name="holder" type="address" required>
  The address that burned claim tokens and received the collateral. Indexed.
</ResponseField>

<ResponseField name="amount" type="uint256">
  The number of claim tokens burned in this redemption.
</ResponseField>

<ResponseField name="assetReceived" type="uint256">
  The net amount of asset collateral received by the redeemer. For standard tokens this equals `amount` (1:1 backing). For fee-on-transfer asset tokens, this may be less than `amount` due to the transfer fee on the outbound transfer.
</ResponseField>

***

## FeeUpdated

Emitted when the admin changes the protocol fee rate or fee recipient.

```solidity theme={null}
event FeeUpdated(uint16 feeBps, address feeRecipient);
```

<ResponseField name="feeBps" type="uint16">
  The new fee rate in basis points. Capped at `MAX_FEE_BPS` (100 bps = 1%).
</ResponseField>

<ResponseField name="feeRecipient" type="address">
  The new address that will receive protocol fees on future offers.
</ResponseField>

***

## EntryPauseUpdated

Emitted when the admin toggles the entry pause state.

```solidity theme={null}
event EntryPauseUpdated(bool paused);
```

<ResponseField name="paused" type="bool">
  `true` when new offer creation and fills are paused; `false` when they are resumed.
</ResponseField>

***

## Querying Events with ethers.js

Use typed event filters to query events efficiently. The following example retrieves all `OfferCreated` events for a specific asset address across a block range:

```typescript theme={null}
const filter = canopy.filters.OfferCreated(null, null, ASSET_ADDRESS);
const events = await canopy.queryFilter(filter, fromBlock, toBlock);

events.forEach((event) => {
  const { offerId, seller, escrowedAssetAmount, unlockTime } = event.args;
  const isDeferred = unlockTime > 0n;
  console.log(`Offer ${offerId} by ${seller} — ${isDeferred ? "deferred" : "spot"}`);
});
```

Pass `null` for any indexed parameter you do not want to filter on. The three indexed fields of `OfferCreated` correspond to topic positions `[1]`, `[2]`, and `[3]` in the raw log, so you can also compose raw `eth_getLogs` filters directly if you are working outside of an ethers.js context.
