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

# Build a hook

How to write an Efficient pool's hooks: the invariant hook's interfaces, the request it receives, the checks on its answer and the optional hooks.

**An Efficient pool's three required hooks are one contract that prices the pool.** They're the invariant, fee and recentring hooks. The contract quotes every fill and its fee. It folds in price observations and moves any anchor.

The pool passes that contract its book. A pool with a price band also passes one checked feed price. The pool then checks the answer against limits no hook can change.

A pool operator can run the Everlong hook, which Everlong vaults use, or write a new one against the interfaces below. This page uses the Everlong hook as its worked example. [Hooks](/how-pools-work/hooks.md) explains the rules behind each check.

## Choose the pricing

**The pool has no curve of its own.** So any pricing that fits the request and the pool's checks works. Every fill must still sit within the fee floor and cap, and under the output ceiling. In a pool with a price band, the fill must also sit inside that band around the feed price.

| Approach                  | What the hook does                                                                                                              | What it reads                                                  |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| Curve around an anchor    | Prices on a curve around an anchor, and recentres the anchor toward observed prices. The Everlong hook works this way           | The book and the prints `poke` and `recenter` forward          |
| Price from the feed       | Quotes around the feed price each swap carries in a pool with a price band. There's no anchor to recentre                       | The feed price                                                 |
| Inventory skew            | Moves its price or fee with what the pool holds, lends and owes                                                                 | The book                                                       |
| Leverage inside the quote | Also quotes a leverage curve, and fills on whichever pays the trader more. The Everlong hook can do this in a pool that borrows | The book, the feed price and the pool's `FEATURE_LEVERAGE` bit |

A hook may fill less than the input, and the trader keeps the rest. The request carries no trader address. On a swap, the hook's caller is always the pool.

## Implement the interfaces

**The invariant hook implements the three interfaces below.** The pool's `HookSet` must name that one contract in all three required roles. A set that names a different fee or recentring hook fails with `HookInvalid`.

The fee hook is that same contract, because it prices the fee inside each fill. So is the recentring hook, because it moves the anchor of the book it prices.

```solidity
interface IInvariantHook {
    function spot(PoolContext calldata ctx) external view returns (uint256 spotN36);
    function previewExactIn(SwapContext calldata ctx) external view returns (FillResult memory);
    function executeExactIn(SwapContext calldata ctx) external returns (FillResult memory);
}

interface IRecenterHook {
    function observe(ObservationContext calldata ctx) external;
    function recenter(ObservationContext calldata ctx) external returns (bool moved, uint256 anchorN36);
}

interface IHookIdentity {
    function hookInterfaceVersion() external view returns (uint16);
    function boundPool() external view returns (address);
}
```

| Call                                   | When the pool makes it                                                   | What the hook must do                                             |
| -------------------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------- |
| `executeExactIn`                       | Once per swap, as a call                                                 | Return the fill and its fee, and write any state the fill changes |
| `previewExactIn`                       | On `previewSwap`, as a static call, up to four times for a sell          | Return the fill `executeExactIn` would return on the same state   |
| `spot`                                 | Before and after each `observe`                                          | Return the hook's current price                                   |
| `observe`                              | On `poke`, which anyone can call                                         | Fold the print, or ignore one it has already folded               |
| `recenter`                             | On `recenter`, from the keeper, the pool operator or the controller hook | Move the anchor and report it, or report no move                  |
| `boundPool` and `hookInterfaceVersion` | At creation and at both ends of a hook change                            | Name this pool, and answer 3                                      |

The pool never compares a swap's preview with its execution. So a preview that drifts misquotes every router and aggregator that relies on `previewSwap`. The Everlong hook runs one fill function for both on the same state, so the two agree.

## The request

**Each swap hands the hook a `SwapContext`.** It carries the pool's book and feed price. It also carries the direction, the exact input, the fee bounds and an output ceiling.

```solidity
struct PoolContext {
    uint256 physicalPoolAsset;
    uint256 postedPoolAsset;
    uint256 liquidLoanAsset;
    uint256 suppliedLoanAsset;
    uint256 debtLoanAsset;
    uint256 priceN36;
}

struct SwapContext {
    PoolContext pool;
    bool poolAssetIn;
    uint256 amountIn;
    uint256 maxAmountOut;
    uint64 feeFloorWad;
    uint64 feeCapWad;
}
```

The loan asset is the other token in the pool's pair, which the pool can borrow. In an Efficient pool that trades BTC against USDC, it's USDC. That token is the first loan asset. Any extra asset the pool can borrow is a loan asset too.

**The hook sees every price and loan amount in one frame, set by the first loan asset.** Loan amounts are N18. That's the first loan asset's value at 18 decimals. Every loan asset is summed in at its cross to the first. Pool asset amounts stay in the pool asset's base units.

`priceN36` is the feed price, in N18 per base unit of pool asset. It's scaled by 1e18 so a cheap 18-decimal asset keeps its digits. A pool with a price band reads it once per swap, after the feed's staleness and sequencer checks. See [Price feeds and oracles](/how-pools-work/price-feeds-and-oracles.md). A pool with no price band reads no feed and passes zero here. So its hook must price without a feed price.

`amountIn` is in pool asset base units on a sell and N18 on a buy. `maxAmountOut` is the other way round.

When the pool can borrow the first loan asset, a sell's ceiling is the credit room under the LTV pin. That ceiling never exceeds the per-swap cap.

When that asset's borrowing is off, a sell's ceiling is that asset's cash: its execution buffer plus what the pool has supplied. The same per-swap cap applies. The credit room also caps the ceiling while debt from before the switch went off remains.

A buy's ceiling is the pool asset the pool holds, including any posted at its lending markets. A buy's input stops at the first loan asset's per-swap cap. A larger buy fills at the cap, and the trader keeps the rest.

On a swap, funding isn't part of a sell's ceiling, so a hook needn't model the lending markets. A sell reverts after the fill if the venues, the lending markets the pool uses, can't fund it. The pool's `swapContext(poolAssetIn, amountIn)` view returns the exact request a swap would send now.

On `previewSwap`, the pool caps a sell's ceiling at what the loan asset's execution buffer and the venues can fund. So a sell's preview request can carry a lower ceiling than its swap request.

In a pool that borrows, that cap counts the sell's whole input as collateral. A partial fill uses less of that input. If the venues can then no longer fund its output, the pool asks the hook again on a lower ceiling. It asks up to four times in all, then reverts with `OutputAboveCeiling`.

## The answer

**The hook returns a `FillResult`.** The pool pays the fill's gross output less its fee, and doesn't alter the fill.

```solidity
struct FillResult {
    uint256 amountInUsed;
    uint256 grossOut;
    uint256 feeOut;
    uint256 feeWad;
    uint256 spotAfterN36;
    uint256 xAfter;
}
```

`amountInUsed` is at most `amountIn`. `feeOut` is the fee kept from `grossOut`, and `feeWad` is the rate the fill was computed at. `spotAfterN36` and `xAfter` give the price and curve coordinate after the fill. `xAfter` is zero for a hook without a curve coordinate.

**The hook must clip its fee rate to `feeCapWad` and solve the fill on the clipped rate.** The Everlong hook does this with its curve fee.

The pool converts loan amounts at the edge, rounding a payout down and an input up. The Everlong hook snaps its loan amounts to the loan asset's native units with `LOAN_SCALE`.

## What the pool checks

**The pool checks every answer in the order below.** Any failure reverts the whole swap. A revert inside the hook reaches the trader with the hook's revert data, unchanged.

| Check                                                                                                  | Revert                |
| ------------------------------------------------------------------------------------------------------ | --------------------- |
| An answer shorter than the six words of a `FillResult`                                                 | Empty revert          |
| `feeWad` under the fee floor or over the fee cap                                                       | `FeeOutOfBounds`      |
| No input used, more input than offered, a fee above the gross output, or nothing left after conversion | `FillInvalid`         |
| Output after the fee under the trader's minimum                                                        | `Slippage`            |
| Output after the fee over the ceiling                                                                  | `OutputAboveCeiling`  |
| In a pool with a price band, input and output after the fee more than the band apart at the feed price | `PriceBand`           |
| In a pool with a price band, the credit check after settlement                                         | `LedgerBoundBreached` |

## Observations and recentres

**Outside swaps and previews, the pool passes the hook a price only through `poke` and `recenter`.** Both stop while the pool is paused. Each passes an `ObservationContext`: the book, the feed's current print and that print's timestamp.

**The hook must treat that timestamp as non-monotonic.** A print can repeat an earlier stamp with a moved price. That's because a cross carries its older leg's stamp. So the hook clamps its elapsed-time step and declines a value it has already folded. The Everlong hook does both.

On a `poke`, the pool reads `spot` before and after `observe`. It reverts with `PokeMove` if the spot moves too far. A recentre reverts with `AnchorOutOfBounds` if the hook moves the anchor outside the caller's bounds. See [Hooks](/how-pools-work/hooks.md#observations-and-the-anchor).

A pool with no price band reads no feed, so its `poke` reverts with `NoBand`. Its `recenter` passes the hook a zero price and timestamp. Only the caller's bounds then hold the anchor.

A hook that prices from the feed can ignore prints and report no move. A swap forwards no print, but a hook may fold the price its request carries. The Everlong hook never does. Its curve prices on the observations it has already folded. Only its leverage rules read the request's price.

A pause stops pokes and recentres but never buys. So a hook's buy path must work while its observations age.

## Identity, access and gas

**The pool admits an invariant hook only if it meets three conditions:**

* It has code.
* It names this pool in `boundPool()`.
* It answers `hookInterfaceVersion()` with 3.

Any other answer, or a revert, fails creation or a hook change with `HookInvalid`.

In a pool with no price band, the hook must also declare `HOOK_CAP_UNPRICED`, or the same check fails. The hook makes that declaration through the optional `IHookCapabilities.hookCapabilities()`. The Everlong hook declares it only when built with no leverage settings.

The pool checks a hook's identity, never its logic. Anyone can call a hook directly. So the Everlong hook accepts `executeExactIn`, `observe` and `recenter` calls only from its pool. It reverts other callers with `NotPool`.

The pool holds its lock while a swap, poke or recentre runs. A hook can still read pool settings such as `paused()`, `switches()` and `loanConfig`. But guarded views such as `totalAssets` and `previewSwap` revert with `Reentrant`.

A hook's settings follow its code, not the pool's governance delay. The Everlong hook takes its owner and keeper from the pool's authority contract. It fixes its leverage settings at construction. [The Everlong strategy](https://docs.foreverlong.io/everlong/everlong-strategy) lists them.

**The pool forwards all remaining gas to the hook, with no cap.** So the hook's cost lands on every swap. The Everlong hook keeps its swap-time settings in a code-only record, read in one account access. It packs its swap state into a few storage words.

## The optional hooks

**A hook set has two optional slots.** No hook for either has shipped yet.

* **Controller hook.** It prices nothing. The pool lets it make the `setPriorities`, `maintain` and `recenter` calls that [Hooks](/how-pools-work/hooks.md#the-optional-hooks) describes. With `setPriorities`, it orders the pool's lending markets for certain operations when the pool has more than one. It may leave both identity calls unanswered. But any pool it names must be this one, and any version it declares must be 3.
* **Loan-to-loan swap hook.** It prices `swapLoan` between two loan assets, in native units, through `previewSwapLoan` and `executeSwapLoan`. [Hooks](/how-pools-work/hooks.md#loan-to-loan-swaps) lists the checks on those swaps. It passes the same identity checks as the invariant hook. It runs only while feature bit 6, `FEATURE_LOAN_SWAP`, is set.

## Deploy and test

1. Read the pool's address from the factory's `predictPool(creator, salt, p)`. Fix it in the hook at construction, since `boundPool()` must name it before the pool exists.
2. Test both directions against the checks above, at the fee floor and cap and at the ceiling. Include partial fills, buys while paused, and repeated or out-of-order prints.
3. Compare `previewExactIn` with `executeExactIn` on the same state. Take each request from the pool's `swapContext` view.
4. Name the hook in all three required roles of the `HookSet` passed to `createPool`. See [Configure a pool](/run-a-pool/configure-a-pool.md).

**A live pool replaces its invariant hook only through a hook change.** [Hooks](/how-pools-work/hooks.md#changing-hooks) covers hook changes. The new hook starts from its own state, not the old hook's.
