> For the complete documentation index, see [llms.txt](https://docs.metric.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.metric.xyz/run-a-pool/build-an-extension.md).

# Build an extension

Extensions run before or after swaps, deposits and withdrawals. They can reject an action or record its result, but they cannot replace the current quote or change the executed amounts. You can reuse [standard extensions](/how-pools-work/extensions-and-permissions.md#standard-extensions) or write your own.

For a public pool, only **withdrawal** extensions can lower the tier. Any extension registered on `beforeRemoveLiquidity` or `afterRemoveLiquidity` makes the pool 🔴 **High Risk**, including Metric's standard extensions. Adding swap or deposit extensions doesn't lower the tier.

See [Pool tiers](/use-metric/provide-and-manage-liquidity/pool-tiers.md).

## 1. Pick the trigger

| Extension               | Runs                | Also receives                                                                    |
| ----------------------- | ------------------- | -------------------------------------------------------------------------------- |
| `beforeSwap`            | Before execution    | Request, bid/ask/ref, initial slot 0                                             |
| `afterSwap`             | After execution     | Everything `beforeSwap` receives, plus final slot 0, amount deltas, platform fee |
| `beforeAddLiquidity`    | Before a deposit    | Owner, salt, bin deltas                                                          |
| `afterAddLiquidity`     | After a deposit     | Everything `beforeAddLiquidity` receives, plus amounts added                     |
| `beforeRemoveLiquidity` | Before a withdrawal | Owner, salt, bin deltas                                                          |
| `afterRemoveLiquidity`  | After a withdrawal  | Everything `beforeRemoveLiquidity` receives, plus amounts removed                |

Slot 0 snapshots hold the whole packed slot 0: bin index and position, fees, asymmetry and pause level.

Any extension can revert. A revert undoes the whole action, token transfers included.

Swap extensions also run inside `simulateSwapAndRevert`. That function is permissionless: the caller chooses `sender` and the quote, and all state changes are rolled back. Extensions should behave the same as in a real swap so that router quotes stay accurate.

Below are a few examples and use cases:

| Use case                | Extension            | Logic                                                                                                                                                                                                                                                                                 |
| ----------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Priority-fee cap**    | `beforeSwap`         | Revert when the transaction's priority fee is above the pool's cap, so updates that pay above it take priority over swaps pending at the same time. Arrival time and the chain's ordering rules still apply. See [Freshness and ordering](/how-pools-work/freshness-and-ordering.md). |
| **Trading hours**       | `beforeSwap`         | Reject swaps outside market hours or during a halt. Withdrawals stay open. Built for tokenized stocks and other assets with a closed market.                                                                                                                                          |
| **Volume budget**       | `afterSwap`          | Sum executed volume per time window and revert when the swap that just executed pushes the window over budget. Use the actual deltas: swaps can fill partially.                                                                                                                       |
| **Fill reporting**      | `afterSwap`          | Record deltas in strategy state that the [Price Provider](/run-a-pool/build-a-price-provider.md) reads on its next quote: widen the ask after sustained buying, tighten as flow fades. The swap that triggered the report is not repriced.                                            |
| **Deposit eligibility** | `beforeAddLiquidity` | Check the position owner against an allowlist or a signed permit. The caller is usually a router; the owner is the LP.                                                                                                                                                                |
| **Rewards**             | `afterSwap`          | Award points on executed amounts. Fund token rewards separately, never from pool balances.                                                                                                                                                                                            |

## 2. Implement it

Use the exact signatures in `IMetricOmmExtensions`. Implement `initialize(address pool, bytes data)` and the extensions you register, and return each function's own selector on success, for example `IMetricOmmExtensions.afterSwap.selector`. A revert or a wrong selector rejects the action.

* **Initialization.** Authenticate the factory, validate and store settings per pool, and prevent reinitialization. `initialize` runs before the factory records the pool's registry entry and admin, so don't read either of them inside `initialize`.
* **Identity.** In an extension, `msg.sender` is the pool: authenticate it. The `sender` argument is the pool's caller, usually a router, not the trader. `extensionData` is caller-supplied and forwarded unchanged to every extension of that action: validate it, and bind any permit to the account, action, pool, chain, expiry and nonce.
* **State.** Use the supplied deltas and snapshots. Don't reenter the pool's guarded methods, including `getSellAndBuyPrices()`.

{% hint style="warning" %}
**Authenticate the pool.** An extension that doesn't check that `msg.sender` is a pool it was initialized for can be called by anyone, with made-up arguments.
{% endhint %}

## 3. Attach and test

At pool creation, supply up to seven extension addresses, one initialization payload each, and the execution order per action. Addresses and order are fixed for the life of the pool; settings can stay mutable if your extension allows it. Don't register withdrawal extensions unless blocking LP exits is intended.

Confirm that each guard you need is initialized with settings that turn it on. For example, a StopLoss with `drawdownE6 = 0` is attached but does nothing.

Test through the real router and liquidity entry points, not only direct calls: unauthorized callers, replayed permits, partial fills, both directions, limit boundaries, rollback, and withdrawals. Keep extension work bounded and check gas.

Next: [Configure a pool](/run-a-pool/create-and-configure-a-pool.md).
