# Developing a Fuse

If you want a particular Fuse to be available via the IPOR Fusion interface, it must meet certain criteria:&#x20;

* The Fuse must have very highunit test coverage for all the custom code
* You need to supply the balance fuse, unless there is already one available.&#x20;
* Your fuse must have clear comprehensive documentation in-code
* You can submit the fuse as a pull request to this repository: <https://github.com/IPOR-Labs/ipor-fusion/tree/main/contracts/fuses>

## How to build your own fuse

Example fuse: <https://github.com/IPOR-Labs/ipor-fusion/blob/main/contracts/fuses/erc4626/Erc4626SupplyFuse.sol>

The below example outlines how the fuse should be built and documented.

### Section: “Purpose” <a href="#section-purpose" id="section-purpose"></a>

This section should describe the outline of the actions performed by the fuse.

***Example**:*

*Fuse* `SupplyERC4626` *allows for integration with any vault that supports the* `ERC4626` *standard. Integration is limited to vaults that allow deposits and withdrawals without any time restrictions. Configuration containing the addresses of supported vaults is stored within* `plasmaVault`*.*

### Section: Validation and Constraints <a href="#section-validation-and-constraints" id="section-validation-and-constraints"></a>

The section should specify whether any constraints are coded into the fuse or stored within `plasmaVault`*.*

#### Implementation Constraints <a href="#implementation-constraints" id="implementation-constraints"></a>

***Example:***

* *Fuse* `SupplyERC4626` *does not verify if the vault complies with ERC4626.*
* *Fuse is limited to handling only vaults that allow deposits and withdrawals without any time restrictions.*
* *Fuse supports only the vault’s accounting tokens (unless* `plasmaVault` *contains fuses that perform swaps).*

#### Validations Performed Based on Data Stored Within plasmaVault <a href="#validations-performed-based-on-data-stored-within-plasmavault" id="validations-performed-based-on-data-stored-within-plasmavault"></a>

This section describes validations based on the data stored within `plasmaVault`*.* Validation data is set in `plasmaVault` by executing the following function:

{% code fullWidth="true" %}

```solidity
function grandMarketSubstrates(uint256 marketId, bytes32[] calldata substrates) external restricted { 
    PlasmaVaultConfigLib.grandMarketSubstrates(marketId, substrates); 
}
```

{% endcode %}

This section describes what is contained in the substrates array and how to decode it.

The substrates array contains addresses of vaults supported by the Fuse, and there may be more than one. Inside the `supplyFuse`, the value is retrieved using the following function:

<pre class="language-solidity" data-full-width="true"><code class="lang-solidity">function isSubstrateAsAssetGranted(uint256 marketId, address substrateAsAsset) internal view returns (bool) { 
<strong>    PlasmaVaultStorageLib.MarketSubstratesStruct storage marketSubstrates = _getMarketSubstrates(marketId); 
</strong>    return marketSubstrates.substrateAllowances[addressToBytes32(substrateAsAsset)] == 1; 
}
</code></pre>

Located in the `PlasmaVaultConfigLib` library

To convert an `address` to `bytes32`, you should use the `addressToBytes32` function found in the `PlasmaVaultStorageLib` library.

{% code fullWidth="true" %}

```solidity
function addressToBytes32(address addressInput) internal pure returns (bytes32) { 
    return bytes32(uint256(uint160(addressInput))); 
}
```

{% endcode %}

### Section: Implementation Details <a href="#section-implementation-details" id="section-implementation-details"></a>

#### <mark style="color:yellow;">enter</mark> <a href="#enter" id="enter"></a>

This section should contain a detailed description of the implementation of the `enter` function.

The data passed to the `enter` function is provided in the `Erc4626SupplyFuseEnterData` structure:

{% code fullWidth="true" %}

```solidity
struct Erc4626SupplyFuseEnterData { 
    /// @dev vault address 
    address vault; 
    
    /// @dev max amount to supply 
    uint256 amount; 
}
```

{% endcode %}

The main logic of the `enter` function resides within the private function `_enter`:

{% code fullWidth="true" %}

```solidity
function _enter(Erc4626SupplyFuseEnterData memory data) internal { 
    if (!PlasmaVaultConfigLib.isSubstrateAsAssetGranted(MARKET_ID, data.vault)) { 
        revert Erc4626SupplyFuseUnsupportedVault("enter", data.vault, Errors.UNSUPPORTED_ERC4626); 
    } 

    address underlineAsset = IERC4626(data.vault).asset(); 
    ERC20(underlineAsset).forceApprove(data.vault, data.amount); 
    IERC4626(data.vault).deposit(data.amount, address(this)); 
    emit Erc4626SupplyFuse(VERSION, "enter", underlineAsset, data.vault, data.amount); 
}
```

{% endcode %}

The following code validates if the Atomist has approved the destination ERC4626 vault.

{% code fullWidth="true" %}

```solidity
if (!PlasmaVaultConfigLib.isSubstrateAsAssetGranted(MARKET_ID, data.vault)) { 
    revert Erc4626SupplyFuseUnsupportedVault("enter", data.vault, Errors.UNSUPPORTED_ERC4626); 
}
```

{% endcode %}

Only the approved amount of `underlineAsset` can be transferred to the destination vault.

{% code fullWidth="true" %}

```solidity
address underlineAsset = IERC4626(data.vault).asset(); 
ERC20(underlineAsset).forceApprove(data.vault, data.amount);
```

{% endcode %}

The requested amount is then deposited to `address(this)` (the fuse is invoked in the context of `plasmaVault`).

{% code fullWidth="true" %}

```solidity
IERC4626(data.vault).deposit(data.amount, address(this));
```

{% endcode %}

Finally, an event with information about the current operation is emitted.

{% code fullWidth="true" %}

```solidity
emit Erc4626SupplyFuse(VERSION, "enter", underlineAsset, data.vault, data.amount);
```

{% endcode %}

#### <mark style="color:yellow;">exit</mark> <a href="#exit" id="exit"></a>

This section should contain a detailed description of the implementation of the `exit` function.

The data passed to the `exit` function is provided in the `Erc4626SupplyFuseExitData` structure:

{% code fullWidth="true" %}

```solidity
struct Erc4626SupplyFuseExitData { 
    /// @dev vault address 
    address vault; 
    
    /// @dev max amount to withdraw 
    uint256 amount; 
}
```

{% endcode %}

The main logic of the exit function is contained within the private function \_exit:

{% code fullWidth="true" %}

```solidity
function _exit(Erc4626SupplyFuseExitData memory data) internal { 
    if (!PlasmaVaultConfigLib.isSubstrateAsAssetGranted(MARKET_ID, data.vault)) { 
        revert Erc4626SupplyFuseUnsupportedVault("exit", data.vault, Errors.UNSUPPORTED_ERC4626); 
    } 
    
    uint256 vaultBalanceAssets = IERC4626(data.vault).convertToAssets( IERC4626(data.vault).balanceOf(address(this)) ); 
    uint256 shares = IERC4626(data.vault).withdraw( IporMath.min(data.amount, vaultBalanceAssets), address(this), address(this) ); emit Erc4626SupplyFuse(VERSION, "exit", IERC4626(data.vault).asset(), data.vault, shares); 
}
```

{% endcode %}

The following code is responsible for checking if the Atomist has accepted the vault with which Alpha wants to interact:

{% code fullWidth="true" %}

```solidity
if (!PlasmaVaultConfigLib.isSubstrateAsAssetGranted(MARKET_ID, data.vault)) { 
    revert Erc4626SupplyFuseUnsupportedVault("exit", data.vault, Errors.UNSUPPORTED_ERC4626); 
}
```

{% endcode %}

The amount of assets stored in the vault can be read from:

{% code fullWidth="true" %}

```solidity
uint256 vaultBalanceAssets = IERC4626(data.vault)
    .convertToAssets(IERC4626(data.vault)
    .balanceOf(address(this)) );
```

{% endcode %}

When withdrawing either `data.amount` of assets or the maximum amount can be withdrawn, whichever is smaller.

The hardcoded `address(this)` specifies that the assets are withdrawn to the plasmaVault (the fuse is executed in the context of `plasmaVault`).

{% code fullWidth="true" %}

```solidity
uint256 shares = IERC4626(data.vault)
    .withdraw( IporMath.min(data.amount, vaultBalanceAssets), address(this), address(this) );
```

{% endcode %}

Finally, an event with information about the execution of the operation is emitted.

{% code fullWidth="true" %}

```solidity
emit Erc4626SupplyFuse(VERSION, "exit", IERC4626(data.vault).asset(), data.vault, shares);
```

{% endcode %}

#### instantWithdraw <a href="#instantwithdraw" id="instantwithdraw"></a>

This section should contain a detailed description of the implementation of the `instantWithdraw` function. This optional function is used to instantly withdraw assets from external markets. For some strategies, it may not be desired to use the instant withdrawal. In those cases, the use of this function can be limited to the level of the vault.

Implementation:

{% code fullWidth="true" %}

```solidity
/// @dev params[0] - amount in underlying asset, params[1] - vault address 
function instantWithdraw(bytes32[] calldata params) external override { 
    uint256 amount = uint256(params[0]); 
    address vault = PlasmaVaultConfigLib.bytes32ToAddress(params[1]); 
    _exit(Erc4626SupplyFuseExitData(vault, amount)); 
}
```

{% endcode %}

The `params` value is set to `plasmaVault` using the `configureInstantWithdrawalFuses` method.

{% code fullWidth="true" %}

```solidity
function configureInstantWithdrawalFuses( PlasmaVaultLib.InstantWithdrawalFusesParamsStruct[] calldata fuses ) external restricted { 
    PlasmaVaultLib.configureInstantWithdrawalFuses(fuses); 
}
```

{% endcode %}

The first parameter in the array is the amount to withdraw from the external vault (this value is reserved in every fuse). The second parameter is the address of the vault from which to withdraw assets.

Next, the private function `_exit` with the parameters `Erc4626SupplyFuseExitData(vault, amount)` can be called.

## Balance Fuse <a href="#todo" id="todo"></a>

### General Purpose

The purpose of the Balance fuse is to count the funds that the plasma vault holds within external protocols. Each marketId found in IporFusionMarkets should have its own balance fuse. However, if it is not needed, and the given fuse does not generate any funds in the external protocol (such as for UNISWAP\_SWAP\_V2 and UNISWAP\_SWAP\_V3), the ZeroBalanceFuse should be connected. It is also important to remember that some fuses change the state in more than one place corresponding to different marketIds. In such cases, the Dependency graph should be configured using the updateDependencyBalanceGraph method.

### Steps to Create a Balance Fuse

* Define the Contract Start by defining the contract and inheriting from the appropriate interface, typically IMarketBalanceFuse.
* Declare any state variables needed by the contract, such as the marketID(requaier) and any protocol-specific addresses or constants.
* Implement the Constructor Implement the constructor to initialize the state variables. Ensure that any necessary validations are performed.
* Implement the balanceOf Function Implement the balanceOf function to calculate the balance of the Plasma Vault in the associated protocol. This function should:
  * Retrieve the relevant market substrates.
  * Loop through each substrate to calculate the balance.
  * Convert the balance to a standard unit (e.g., USD) using a price oracle.

### Example

This guide explains how to create a balance fuse similar to `ERC4626BalanceFuse.sol` and `MorphoBlueBalanceFuse.sol`.

#### Define the Contract

Start by defining the contract and inheriting from the `IMarketBalanceFuse` interface.

```solidity
contract ExampleBalanceFuse is IMarketBalanceFuse {
```

#### Declare State Variables

Declare any state variables needed by the contract and Initialize.

```solidity
uint256 public immutable MARKET_ID;

constructor(uint256 marketId_) {
    MARKET_ID = marketId_;
}
```

#### Implement the balanceOf Function

```solidity
function balanceOf() external view override returns (uint256) {
    bytes32[] memory substrates = PlasmaVaultConfigLib.getMarketSubstrates(MARKET_ID);
    uint256 len = substrates.length;
    if (len == 0) {
        return 0;
    }

    uint256 balance;
    for (uint256 i; i < len; ++i) {
        // Calculate balance for each substrate
    }

    return balance;
}
```

Complete Example

```solidity
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import {IPriceOracleMiddleware} from "../../price_oracle/IPriceOracleMiddleware.sol";
import {IMarketBalanceFuse} from "../IMarketBalanceFuse.sol";
import {PlasmaVaultConfigLib} from "../../libraries/PlasmaVaultConfigLib.sol";
import {IporMath} from "../../libraries/math/IporMath.sol";
import {PlasmaVaultLib} from "../../libraries/PlasmaVaultLib.sol";

contract ExampleBalanceFuse is IMarketBalanceFuse {
    using SafeCast for uint256;

    uint256 public immutable MARKET_ID;

    constructor(uint256 marketId_) {
        MARKET_ID = marketId_;
    }

    function balanceOf() external view override returns (uint256) {
        /// @dev this substractes should be setup in coresponding fuse with the same MARKET_ID
        bytes32[] memory substrates = PlasmaVaultConfigLib.getMarketSubstrates(MARKET_ID);
        uint256 len = substrates.length;
        if (len == 0) {
            return 0;
        }

        uint256 balance;
        for (uint256 i; i < len; ++i) {
            // Calculate balance for each substrate
        }

        return balance;
    }

    function _convertToUsd(
        address priceOracleMiddleware_,
        address asset_,
        uint256 amount_
    ) internal view returns (uint256) {
        if (amount_ == 0) return 0;
        (uint256 price, uint256 decimals) = IPriceOracleMiddleware(priceOracleMiddleware_).getAssetPrice(asset_);
        return IporMath.convertToWad(amount_ * price, IERC20Metadata(asset_).decimals() + decimals);
    }
}
```


---

# Agent Instructions: Querying This Documentation

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

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ipor.io/build-on-fusion/developer-guide/developing-a-fuse.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
