# Security Requirements

> Security requirements for the fuses of a new market: vault control of positions, limits on every action, allowances, valuation without silent failures, price sources, venue risks and what the IPOR team does not accept.

These are the requirements the IPOR team applies when it reviews the deployed code of a new market. They complement the [Coding Standard](https://ipor.io/docs/build-on-fusion/integrating-a-new-market/coding-standard.md). Where a requirement is an IPOR rule and not something the Fusion code enforces, the text says "IPOR requires"; the Coding Standard explains what that means.

Fuses run by `delegatecall` in the context of the vault, so a mistake in a fuse is a mistake in the vault. See [What a Market Integration Contains](https://ipor.io/docs/build-on-fusion/integrating-a-new-market/market-components.md) for the parts of an integration.

## The vault controls its own positions

IPOR requires that a fuse never hands control of a vault position to an external party. A fuse must not call `setManager`, `setDelegate` or an equivalent, and must not approve an operator or position manager on the venue. The vault stays the only owner and manager of its positions.

Assets leave the venue to the vault and never to a caller-supplied recipient. The reference withdraw and borrow functions use `address(this)`, the vault, as the receiver.

## Limits on every action

Every function that moves value takes minimum or maximum parameters from the caller: for example `minSharesOut` on a deposit, `maxSharesBurned` or `minAmount` on a withdrawal, or `minSharesRepaid` on a repayment. IPOR requires this for every such function except `instantWithdraw` (see below). Do not hard-code percentages or slippage tolerances in the fuse; the [Alpha](https://ipor.io/docs/build-on-fusion/architecture-overview/what-is-an-alpha.md), the role that executes strategy transactions on the vault, chooses the limits for each call.

> **Note:** A limit that is optional in the parameters can be switched off by the caller. In `Erc4626SupplyFuse`, `maxSharesBurned = 0` means no limit, and `instantWithdraw` passes `0`. Treat a limit that is off as a risk you describe in your README.

Implementing `instantWithdraw` is optional. When you implement it, the vault sets `params[0]` to the amount it still needs; the other params are the values the atomist configured. IPOR does not require a bound there. Describe in your README how your fuse bounds the instant-withdraw path.

## Allowances are revoked after use

IPOR requires that after the venue call the fuse sets the allowance of the external spender back to zero, so no residual allowance remains. `AguaSupplyFuse` shows the pattern: `forceApprove(vault, amount)`, the deposit, then `forceApprove(vault, 0)`. See the [allowance cleanup in `AguaSupplyFuse`](https://github.com/IPOR-Labs/ipor-fusion/blob/632350d6a31afbe7a306f0e6458bb4794c18c624/contracts/fuses/agua/AguaSupplyFuse.sol#L83-L87).

The ERC4626 and Aave V4 examples approve the exact amount but do not set the allowance back to zero. They do not demonstrate this requirement.

## No silent failures in valuation

The balance fuse must report what the vault holds, or fail.

- The balance fuse does not catch a revert from the venue.
- It does not return `0` for a position it cannot value. A zero hides the position from the vault balance.
- `ZeroBalanceFuse` is only for a market that creates no separate position. Never use it for a position you cannot value.

The vault calls `balanceOf()` on the balance fuse by `delegatecall` and does not catch a revert, so a reverting balance fuse blocks the balance update.

`try/catch` is allowed only where it is described. The one documented place is the instant-withdraw path, around the call to the venue withdraw, with an event that reports the failure. The vault does not wrap `instantWithdraw` in `try/catch`, so a revert there reverts the user withdrawal, which is why the reference fuses catch only that one call. Reads and substrate checks stay outside `try/catch`. In `Erc4626SupplyFuse` the `maxWithdraw` read is outside it and a zero result is a silent return, not a failure event; do not copy that into a balance fuse.

## Valuation matches the exit

IPOR requires the balance fuse to value the whole position at what an exit pays for it. The Aave V4 balance fuse, for example, is net of total debt, including the accrued risk premium. A valuation above what the exit pays inflates the share price for everyone in the vault. Value the position, not what the venue lets the vault withdraw at the moment: do not cap the valuation with withdrawal limits such as the ERC4626 `max*` functions. They can return `0` while the venue is paused or a cap is reached, and a position that still exists must not be valued at `0`.

## A robust price source

The Price Oracle Middleware rejects a price that is zero or negative and reverts for an asset it cannot price. A custom price feed implementing [`IPriceFeed`](https://github.com/IPOR-Labs/ipor-fusion/blob/main/contracts/price_oracle/price_feed/IPriceFeed.sol) must not be manipulable within one transaction and must report its decimals correctly. Do not price an asset from the spot price of a single pool.

## Venue risks and substrates in the README

Describe the risks of the venue in the README of your fuse repository: upgradeable contracts, admin keys, pauses, withdrawal delays, caps and actions that anyone can take on the vault position. The [Aave V4 README](https://github.com/IPOR-Labs/ipor-fusion/blob/main/contracts/fuses/aave_v4/README.md) and the `SPOL_UNSTAKE` NatSpec in [`IporFusionMarkets.sol`](https://github.com/IPOR-Labs/ipor-fusion/blob/main/contracts/libraries/IporFusionMarkets.sol) are examples.

An [Atomist](https://ipor.io/docs/build-on-fusion/architecture-overview/what-is-an-atomist.md), the administrator of a vault, grants and revokes [substrates](https://ipor.io/docs/build-on-fusion/atomists/vault-configuration-step-by-step/substrates.md). IPOR requires the consequences for open positions to be documented in the README, as the Aave V4 README does in [Grant changes and open positions](https://github.com/IPOR-Labs/ipor-fusion/blob/632350d6a31afbe7a306f0e6458bb4794c18c624/contracts/fuses/aave_v4/README.md#L87-L101). The fuse does not enforce them.

## Checked on-chain

The review is based on the deployed code. For each fuse the IPOR team checks that:

- the fuse has no roles, owner, admin keys, pause or upgrade mechanism, and is not a proxy;
- venue addresses are immutable or come from granted substrates, and the fuse checks the grant before it moves funds;
- no code path gives up control of a position or sends the assets of the vault to an arbitrary recipient.

> **Warning:** The IPOR team does not accept fuses with:
>
> - an arbitrary recipient of assets, or an action other than `instantWithdraw` with no limit;
> - a transfer of control over a vault position to a third party;
> - state outside ERC-7201 namespaced storage;
> - admin keys or upgradeability in the fuse;
> - no README that describes the venue risks;
> - a token that the balance fuse values has no price source in the Price Oracle Middleware and none described in the pull request.

A fuse that does not meet these requirements receives a request for changes in the pull request. See [Whitelist Review](https://ipor.io/docs/build-on-fusion/integrating-a-new-market/whitelist-review.md).
