npm package: @majora-finance/developer-kit
Description: This developer kit aim to provide tools to implement Majora. Blocks
Latest version: 0.0.81

# Majora developer kit

This developer kit aim to provide tools to implement Majora. Blocks

## Instalation

### Initialize a new hardhat project

```bash
mkdir myNewBlock
cd myNewBlock

npx hardhat
```

### Install developer kit npm package

```bash
npm i @majora-finance/developer-kit
```

### Import developer kit tasks to hardhat

Edit your hardhat.config.ts and add

```typescript
import "@majora-finance/developer-kit/src/tasks";
```

## Development steps

### Write your block smart contracts

A block corresponding to an action specific to a DeFi protocol
So to integrate a protocol, you´ll have to implement one block per actions doable on this one

By exemple, for our Aave V3 blocks, you can find 3 blocks

- Deposit: Deposit asset on Aave pool
- Borrow: Deposit asset on Aave pool and borrow a dept at a specified healthfactor
- Leverage: Deposit asset on Aave pool and leverage at a specified healthfactor with dept swapped and deposited as collateral

You can have a look on Aave v3 blocks [here](https://github.com/majora-finance/aave-v3-blocks)

There is two types of block: strategy block and harvest block

#### Commons interface

There is two functions in common between every block present in IMajoraCommonBlock interface:

```solidity
interface IMajoraCommonBlock {
    function ipfsHash() external view returns (string memory);

    function dynamicParamsInfo(
        DataTypes.BlockExecutionType _exec,
        bytes memory parameters,
        DataTypes.OracleState memory oracleState
    ) external view returns (bool, DataTypes.DynamicParamsType, bytes memory);
}
```

The function `ipfsHash` have to return the hash of an IPFS document where the blocks metadatas are stores and `dynamicParamsInfo` function have to return if the block needs dynamic parameters provided by operators on execution (exemple: Swap parameters). To learn more about dynamic parameters, have a look [here](https//wip.com)

#### Strategy Block

The strategy block is dedicated to be executed when the vault will put funds to work by executing the strategy block list and when it will withdraw funds of the strategy.

This block type need to be complient with our protocol by implementing this interface:

```solidity
interface IMajoraStrategyBlock is IMajoraCommonBlock {
    function enter(uint256 _index) external;

    function exit(uint256 _index, uint256 _percent) external;

    function oracleEnter(DataTypes.OracleState memory previous, bytes memory parameters)
        external
        view
        returns (DataTypes.OracleState memory);

    function oracleExit(DataTypes.OracleState memory previous, bytes memory parameters)
        external
        view
        returns (DataTypes.OracleState memory);
}
```

The `enter` & `exit` function are called by the vault with a delegate call. So they are executed with the vault's context. The `enter` function have to execute the action for which the block is written for. The `exit` function have to the the opposit of the enter function.

This functions receive an index number which allow the block to retrieve its parameters with a storage pointer pointing on a bytes variable. You'll have to write a Struct representing the data you'll have to store inside it. For immutables variables, prefer use the constructor to set them.

The `enterOracle` & `exitOracle` function are called by the vault to simulate `enter` or `exit` functions. this functions receive an `OracleState` with simulated tokens amounts at the moment of the block execution. The goal is to edit it like will do the execution function. The bytes parameters is the same block parameters than received on the corresponding execution function.

By exemple, for a Aave deposit block, the enter function have to deposit a token in the lending pool and the exit function have to withdraw funds. For the oracle functions, the `enterOracle` will remove the deposited $TKN and add the $aTKN amount.

There is many function in OracleState library to help to manage it:

- findTokenAmount
- addTokenAmount
- setTokenAmount
- removeTokenAmount
- removeTokenPercent
- removeAllTokenPercent

You can found the library [here](https://github.com/majora-finance/libraries)

#### Harvest Block

Harvest block is based on the same concept than strategy block, the harvest block list is executed on vault harvest.
The `oracleHarvest` is a non view function to be usable with Curve like claims on which there is a need to update a local variable.

```solidity
interface IMajoraStrategyBlock is IMajoraCommonBlock {
    function harvest(uint256 _index) external;

    function oracleHarvest(DataTypes.OracleState memory previous, bytes memory parameters)
        external
        returns (DataTypes.OracleState memory);
}
```

### Block's metadata files

When your block smart contract is ready, you'll have to write contract NatSpec on the contract to generate metadata template with the developer kit.

This file will be used to index the block on our backend and retrive all the information of the block on our frontend.

NatSpec exemple:

```solidity
/**
 * @title Aave V3 Deposit Majora. Block
 * @author Bliiitz
 * @notice Block to deposit a token on Aave V3
 * @custom:block-id AAVE_V3_DEPOSIT
 * @custom:block-type block
 * @custom:block-action Deposit
 * @custom:block-protocol-id AAVE_V3
 * @custom:block-protocol-name Aave v3
 * @custom:block-params-tuple tuple(uint256 tokenInPercent, address token)
 */
contract MajoraAaveV3DepositBlock is IMajoraStrategyBlock {
```

After have written this natspec you can call the hardhat task to generate YAML files.

`npx hardhat generate-block-metadata`

It will create the `blocks-metadata` folder ad create one metadata file per contract.

Yaml exemple:

```yaml
id: AAVE_V3_DEPOSIT
name: Aave V3 Deposit Majora. Block
description: Block to deposit a token on Aave V3
type: block
action: Deposit
protocolId: AAVE_V3
protocolName: Aave v3
paramsTuple: tuple(uint256 tokenInPercent, address token)
params:
  - attribute: tokenInPercent
    name: Amount
    type: percent
    underlyingType: uint256
  - attribute: token
    name: Deposit
    type: ERC20
    underlyingType: address
    resolver: AaveV3Deposit
```

Here, the params object is dedicated to the backend / frontend.

- Attribute: tuple attribute name
- Name: the name of the input on the block configuration modal
- Type: the type of input
- Resolver: a lambda like function which will be executed to retrieve the data
- Underlying type: low level type

#### List of parameters type

- Type `ERC20`: display a list of ERC20 tokens returned by the resolver
- Type `percent`: percent in basis point (10000 = 100%)
- Type `1e18`: display a number input and add a 10^18 multiplier on user input after validation
- Type `select`: display a select based on a list of possibility returned by the resolver or the `values` parameter attribute
- Type `value`: use the value by the specified resolver or the `value` parameter attribute

If you set types as solidity type (uin256 / address / etc) without resolver, an input of corresponding type will be display on the vault configuration

- Type `address`: display a simple text input
- Type `bytes`: display a simple text input
- Type `uint256`: display a simple text input
- Type `bool`: display a simple checkbox input

### Write your block parameter resolvers

### Publish block metadata and deploy your blocks

### Use the devkit to test your blocks

generate yaml config with :
`npx hardhat generate-block-metadata`

# Dev-kit Automated Testing Guide

## Step 1: Write your block smart contracts

First, write your block smart contracts following the step mentionned above and complete the blocks metadata yaml files.

## Step 2: Generate Configuration

Generate the Dev-kit configuration by running the following command:

    npx hardhat generate-devkit-config

This command will create YAML files in the `devkit-config` directory and corresponding tests in the `tests/` directory.

### Configuration Details

For each YAML file, you need to specify the following parameters:

- `blockParameters`: Configuration parameters specific to each block. Ensure the order of these parameters matches the exact sequence outlined in the Solidity contract.

- `constructorArgs`: Arguments required for the constructor within the block.

- `tokenIN` information:

  - `Holder`: Address of a wallet that owns the `tokenIN` on mainnet. This simplifies the process as there's no need to mint or acquire tokens manually.

  - `Amount`: Amount of `tokenIN` that the holder should have.

  - `Decimals`: Number of decimals the `tokenIN` uses.

## Step 3: Run Tests

To run the tests, execute the generated tests, which are named using the pattern `DevKit + BlockName + test.ts`, using this command :

    npx hardhat test test/Devkit<BlockName>.test.ts

Each test corresponds to a YAML configuration file and tests the block as configured.