# Introduction

## What is DeFi Saver?

{% hint style="info" %}
This is technical documentation about the DeFi Saver SDK and smart contracts. If you are looking for information about using the DeFi Saver app, please visit our [help center](https://help.defisaver.com/).
{% endhint %}

[DeFi Saver](https://defisaver.com) is an advanced management dashboard for all your DeFi needs.

The following documentation will go over the Solidity architecture that powers DeFi Saver and provide an in-depth explanation on how it works.

With DeFi Saver you can manage and interact between decentralized finance protocols. By creating strategies you can create advanced actions that will be executed automatically when certain conditions are met. The code is [open source](https://github.com/DecenterApps/defisaver-v3-contracts) and runs on the Ethereum blockchain and other L2 networks.

<figure><img src="/files/rRkLEuUYmRGSB7Gtepgj" alt=""><figcaption><p>Overview of main system components for executing strategy</p></figcaption></figure>

### Main concepts

| Term             | Description                                                                                                                                                                                                                                                                                                                         |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Action**       | A contract which will perform a specific action (e.g. Maker Payback). It inherits the standard ActionBase. Actions are proxy/logic contracts that are called through user's wallet (e.g Safe) and can’t hold any state.                                                                                                             |
| **Trigger**      | A contract which will perform a check if a certain condition is met (e.g. whether Maker Vault's collateralization ratio is lower than specified). It inherits the standard Trigger Interface.                                                                                                                                       |
| **Recipe**       | A recipe is a series of actions that are bundled together and executed in one sequence. Actions can share return values and use them as inputs in next actions.A recipe can be either executed immediately or through a strategy. If a flash loan action is used within a recipe, it must be the first action.                      |
| **Strategy**     | Is the main building block. A Strategy is an array of triggers and a recipe, where the recipe will be executed if all the specified trigger conditions are met. Users build their own strategies which are executed by bots.                                                                                                        |
| **Bundle**       | A list of strategies that have the same triggers but their recipes are different. A good example is a normal repay and a repay with a flash loan, would be in a repay bundle. The bot can choose which recipe is better at time of execution. Users can subscribe to a bundle, rather than multiple strategies to save on gas cost. |
| **Subscription** | Users subscribe to certain strategies and write their own data for that strategy. For instance if a strategy involves MakerDAO, user subscription might include the users vaultId. Users can subscribe to multiple strategies with different subscription data.                                                                     |


# Core

All recipes and strategies are executed through the Core system of contracts.

Core contracts implement actions and triggers alongside authorization handling and other functionalities. All contracts except `DFSRegistry` and auth contracts,`ProxyAuth` and `SafeModuleAuth`, are upgradable (through a time lock), but updates to the core system should not be frequent.


# Smart wallets

Every operation inside the DeFi Saver system is executed from the user’s smart wallet. DeFi Saver originally started with the `DSProxy` smart wallet, originating from `MakerDAO`, and later added support for `Safe Wallets`, as well as `Instadapp DSA` smart wallets and `Summer.Fi` smart wallets.

Currently, the Safe smart wallet is the default wallet created by the DeFi Saver UI for new users, with an option to load existing `DSProxy`, `DSAProxy`, or `Summer.Fi` smart wallets.

The table below lists the supported feature sets for each smart wallet type:

| Smart wallet         | Manual recipe execution | Strategy execution   | TxSaver execution    |
| -------------------- | ----------------------- | -------------------- | -------------------- |
| Safe                 | :white\_check\_mark:    | :white\_check\_mark: | :white\_check\_mark: |
| DSProxy              | :white\_check\_mark:    | :white\_check\_mark: | :x:                  |
| DSAProxy (Instadapp) | :white\_check\_mark:    | :x:                  | :x:                  |
| Summer.Fi proxy      | :white\_check\_mark:    | :x:                  | :x:                  |


# DFS Registry

All the contract addresses used in the protocol are registered in the `DFSRegistry` contract.

Each contract has an unique ID and the same ID cannot be registered twice. A contract address can be fetched by calling `getAddr(id)`.

{% hint style="info" %}
`ID is a bytes4 value and it is a keccak256 of the contract name and the first 4 bytes from the result.`
{% endhint %}

```javascript
// Example of fetching the contract address
const contractAddr = await registry.getAddr(bytes4(utils.keccak256(utils.toUtf8Bytes(contractName))));
```

The first time a contract address is registered a `waitPeriod` is also set, which represents the number of seconds needed to pass before the contract address can be updated. In order to update the contract address you call `startContractChange` and wait for the entry's `waitPeriod` before you can call `approveContractChange`. This is done so that users have sufficient time to exit the system, or the owners have enough time to react in case of a malicious contract upgrade.

While the contract is in the process of an update, the update can be canceled using `cancelContractChange`.

{% hint style="info" %}
All the state modifying function in this contract are only callable by the owner.
{% endhint %}

Below is the interface of the contract:

```solidity
 
contract DFSRegistry {
    function getAddr(bytes4 _id) public view returns (address);

    function isRegistered(bytes4 _id) public view returns (bool);

    function addNewContract(bytes4 _id, address _contractAddr, uint256 _waitPeriod) public onlyOwner;
    
    function startContractChange(bytes4 _id, address _newContractAddr) public onlyOwner;

    function approveContractChange(bytes4 _id) public onlyOwner;
      
    function cancelContractChange(bytes4 _id) public onlyOwner;
      
    function startWaitPeriodChange(bytes4 _id, uint256 _newWaitPeriod) public onlyOwner;
    
    function approveWaitPeriodChange(bytes4 _id) public onlyOwner;
    
    function cancelWaitPeriodChange(bytes4 _id) public onlyOwner;
}
```


# Strategy Executor

This is the main and only entry point to trigger the execution of strategies. The public function `executeStrategy()` is only callable by certain addresses, which is enforced by the `BotAuth` contract. Besides those checks, there are a few others; first because the bot calling is sending the full `StrategySub` struct the hash is checked if it's valid. Second, each sub can be enabled/disabled by the user that created it, and there is also a check if the sub is allowed. Once the conditions are passed auth contract is called. In case user is using `Safe` smart wallet, `SafeModuleAuth` is called which is authorized by the owner of safe to execute transactions from safe module. In case of `DSProxy` smart wallet, `ProxyAuth` is called (which holds users `DSProxy` authorizations). From auth contract, `RecipeExecutor` is called which will execute a recipe in context of user's wallet.

{% hint style="info" %}
Triggers are not checked in the `StrategyExecutor` but rather in `RecipeExecutor` to enable changeable triggers and some minor gas cost savings.
{% endhint %}

Below is the interface of the contract:

```solidity
contract StrategyExecutor {

    /// @notice Checks all the triggers and executes actions
    /// @dev Only authorized callers can execute it
    /// @param _subId Id of the subscription
    /// @param _strategyIndex Which strategy in a bundle, need to specify because when sub is part of a bundle
    /// @param _triggerCallData All input data needed to execute triggers
    /// @param _actionsCallData All input data needed to execute actions
    /// @param _sub StrategySub struct needed because on-chain we store only the hash
    function executeStrategy(
        uint256 _subId,
        uint256 _strategyIndex,
        bytes[] calldata _triggerCallData,
        bytes[] calldata _actionsCallData,
        StrategySub memory _sub
    ) public;

}
```

{% hint style="info" %}
For L2 networks, the `StrategyExecutor` implementation does not send the `_sub` field; it is read on-chain from `SubStorage`, as subscriptions are stored directly on-chain due to lower gas costs compared to mainnet.
{% endhint %}


# Recipe Executor

Recipe Executor is the main entry point of execution of recipes; it can be called directly (when a user manually executes a set of actions), through `StrategyExecutor` when the Recipe is part of a strategy or through `TxSaverExecutor` when it is part of TxSaver transaction. The contract checks if the first action is a special flash loan action type and adequately sets up the code to execute the flash loan.

**Recipe Executor is always called through a user's wallet** and can't hold any state. There are few entry points to recipe execution:

1. `executeRecipe()` -> used when the recipe is executed manually
2. `executeActionsFromFL` -> called by FL contract as part of callback. Used in flashloan recipes.
3. `executeRecipeFromStrategy()` -> called by `StrategyExecutor` . See [Strategy Executor](/protocol/core/strategy-executor).
4. `executeRecipeFromTxSaver()` -> called by `TxSaverExecutor` . See [TxSaver](/protocol/txsaver).

```solidity
/// @dev List of actions grouped as a recipe
/// @param name Name of the recipe useful for logging what recipe is executing
/// @param callData Array of calldata inputs to each action
/// @param subData Used only as part of strategy, subData injected from StrategySub.subData
/// @param actionIds Array of identifiers for actions - bytes4(keccak256(ActionName))
/// @param paramMapping Describes how inputs to functions are piped from return/subbed values
struct Recipe {
    string name;
    bytes[] callData;
    bytes32[] subData;
    bytes4[] actionIds;
    uint8[][] paramMapping;
}
```

When the recipe is called through the `StrategyExecutor` there are additional checks if the triggers are executed correctly. All triggers must return true for the execution to continue. After that, from the Strategy data a new Recipe object is created and goes through the same flow as a manually executed recipe.

{% hint style="info" %}
Triggers can be changeable! If Trigger returns true to a `isChangeable()` call after the trigger is checked, the sub-data of that trigger can be updated. Useful for strategies, for example, where on every five days some recipe happens, trigger adds five days from the last execution as the next trigger date.
{% endhint %}

Recipe Executor also handles a particular type of actions `Flash loan` actions. Flash loan actions are always sent first, and they callback the Recipe Executor through the `executeActionsFromFL` function.

Below is the interface of the contract:

```solidity
interface IRecipeExecutor {
    /// @notice Called directly through user wallet to execute a recipe
    /// @dev This is the main entry point for Recipes executed manually
    /// @param _currRecipe Recipe to be executed
    function executeRecipe(StrategyModel.Recipe calldata _currRecipe) external payable;
    
    /// @notice Called by TxSaverExecutor through safe wallet
    /// @param _currRecipe Recipe to be executed
    /// @param _txSaverData TxSaver data signed by user
    function executeRecipeFromTxSaver(
        StrategyModel.Recipe calldata _currRecipe,
        StrategyModel.TxSaverSignedData calldata _txSaverData
    ) external payable;
    
    /// @notice Called by user wallet through the auth contract to execute a recipe & check triggers
    /// @param _subId Id of the subscription we want to execute
    /// @param _actionCallData All input data needed to execute actions
    /// @param _triggerCallData All input data needed to check triggers
    /// @param _strategyIndex Which strategy in a bundle, need to specify because when sub is part of a bundle
    /// @param _sub All the data related to the strategies Recipe
    function executeRecipeFromStrategy(
        uint256 _subId,
        bytes[] calldata _actionCallData,
        bytes[] calldata _triggerCallData,
        uint256 _strategyIndex,
        StrategyModel.StrategySub memory _sub
    ) external payable;
    
    /// @notice This is the callback function that FL actions call
    /// @dev FL function must be the first action and repayment is done last
    /// @param _currRecipe Recipe to be executed
    /// @param _flAmount Result value from FL action
    function executeActionsFromFL(StrategyModel.Recipe calldata _currRecipe, bytes32 _flAmount)
        external
        payable;
}
```


# Safe Module Auth

This contract receives the user's `Safe` permission to perform actions on their behalf.

Each user that has their positions managed by a `Safe` wallet, is permitting other contracts to perform actions on their behalf. In Defi Saver the `SafeModuleAuth` contract receives that permission.

{% hint style="info" %}
Due to having permission for users' wallets, SafeModuleAuth is an immutable contract
{% endhint %}

The `SafeModuleAuth` has only one function `callExecute` which is only callable by the `StrategyExecutor` contract. The function calls the users `Safe` and performs the configured actions. This allows for the rest of the system to change while keeping the safe permission in a fixed contract.

Below is the interface of the contract:

```solidity
contract SafeModuleAuth {
    function callExecute(
        address _safeAddr,
        address _recipeExecutorAddr,
        bytes memory _callData
    ) external payable onlyExecutor notPaused;
}
```

{% hint style="info" %}
`callExecute` function has `notPaused` modifier which can be used to pause the system of strategy execution in the case of unexpected behaviour
{% endhint %}


# Proxy Auth

This contract receives the user's `DSProxy` permission to perform actions on their behalf.

Each user that has their positions managed by a `DSProxy,` is permitting other contracts to perform actions on their behalf. In DeFi Saver the `ProxyAuth` contract receives that permission.

{% hint style="info" %}
Due to having permission for users' proxies, ProxyAuth is an immutable contract.
{% endhint %}

The `ProxyAuth` has only one function `callExecute` which is only callable by the `StrategyExecutor` contract. The function calls the users `DSProxy` and performs the configured actions. This allows for the rest of the system to change while keeping the proxy permission in a fixed contract.

Below is the interface of the contract:

```solidity
contract ProxyAuth {
    function callExecute(
        address _proxyAddr,
        address _contractAddr,
        bytes memory _callData
    ) public payable onlyExecutor;
}
```


# Bot Auth

This contract handles authority of who can call strategy executions on `StrategyExecutor`. The contract is registered in the `DFSRegistry` and `isApproved` function of the Bot Auth is called before each strategy execution.

In the current implementation, only DeFi Saver approved bots have access to calling strategy execution. While there are plans to allow other participants to run bots in the future, this is currently not allowed.

{% hint style="info" %}
Besides checking the caller, BotAuth also has the `subId` as an input, allowing it to make decisions on who can execute based on the subscription or strategy.
{% endhint %}

Below is the interface of the contract:

```solidity
contract BotAuth {

    /// @notice Checks if the caller is approved for the specific subscription
    /// @dev First param is subId but it's not used in this implementation 
    /// @dev Currently auth callers are approved for all strategies
    /// @param _caller Address of the caller
    function isApproved(uint256, address _caller) public view returns (bool) {
        return approvedCallers[_caller];
    }

    /// @notice Adds a new bot address which will be able to call executeStrategy()
    /// @param _caller Bot address
    function addCaller(address _caller) public onlyOwner;

    /// @notice Removes a bot address so it can't call executeStrategy()
    /// @param _caller Bot address
    function removeCaller(address _caller) public onlyOwner;
}
```


# Strategy Storage

Record of all the Strategies created

All the data associated with the Strategies are stored in this contract; users can subscribe to specific strategies that are created.

`StrategyStorage` stores an array of strategies (struct detailed below).

```solidity
/// @dev Template/Class which defines a Strategy
/// @param name Name of the strategy useful for logging what strategy is executing
/// @param creator Address of the user which created the strategy
/// @param triggerIds Array of identifiers for trigger - bytes4(keccak256(TriggerName))
/// @param actionIds Array of identifiers for actions - bytes4(keccak256(ActionName))
/// @param paramMapping Describes how inputs to functions are piped from return/subbed values
/// @param continuous If the action is repeated (continuos) or one time
struct Strategy {
    string name;
    address creator;
    bytes4[] triggerIds;
    bytes4[] actionIds;
    uint8[][] paramMapping;
    bool continuous;
}
```

{% hint style="info" %}
Strategies can currently only be created by the owner, but there is a flag to open it so anyone can create strategies.
{% endhint %}

Below is the interface of the contract:

```solidity
contract StrategyStorage {
    /// @notice Creates a new strategy and writes the data in an array
    /// @dev Can only be called by auth addresses if it's not open to public
    /// @param _name Name of the strategy useful for logging what strategy is executing
    /// @param _triggerIds Array of identifiers for trigger - bytes4(keccak256(TriggerName))
    /// @param _actionIds Array of identifiers for actions - bytes4(keccak256(ActionName))
    /// @param _paramMapping Describes how inputs to functions are piped from return/subbed values
    /// @param _continuous If the action is repeated (continuos) or one time
    function createStrategy(
        string memory _name,
        bytes4[] memory _triggerIds,
        bytes4[] memory _actionIds,
        uint8[][] memory _paramMapping,
        bool _continuous
    ) public onlyAuthCreators returns (uint256);
    
    /// @notice Switch to determine if bundles can be created by anyone
    /// @dev Callable only by the owner
    /// @param _openToPublic Flag if true anyone can create bundles
    function changeEditPermission(bool _openToPublic) public onlyOwner;
    
    ////////////////////////////// VIEW METHODS /////////////////////////////////

    function getStrategy(uint _strategyId) public view returns (Strategy memory);
    function getStrategyCount() public view returns (uint256);
    function getPaginatedStrategies(uint _page, uint _perPage) public view returns (Strategy[] memory);
}
```


# Sub Storage

Storage of users subscriptions to strategies/bundles

Users can subscribe to different strategies by providing their specific data and giving the necessary authorizations. `SubStorage` contract keeps track of all the users' subscriptions and provides ways for users to edit their subscriptions.

{% hint style="info" %}
In order to save on gas costs, the whole user subscription struct is not stored on chain. A hash is stored, user wallet address, and if the subscription is enabled.
{% endhint %}

The stored struct on `SubStorage` contract is `StoredSubData` and it's stored in an array. There is no way to change the ordering of the array and delete elements.

```solidity
/// @dev Actual data of the sub we store on-chain
/// @dev In order to save on gas we store a keccak256(StrategySub) and verify later on
/// @param userProxy Address of the users smart wallet/proxy
/// @param isEnabled Toggle if the subscription is active
/// @param strategySubHash Hash of the StrategySub data the user inputted
struct StoredSubData {
    bytes20 userProxy; // address but put in bytes20 for gas savings
    bool isEnabled;
    bytes32 strategySubHash;
}
```

The struct that is sent as calldata and hashed is:

```solidity
/// @dev Instance of a strategy, user supplied data
/// @param id Id of the strategy or bundle, depending on the isBundle bool
/// @param isBundle If true the id points to bundle, if false points directly to strategyId
/// @param triggerData User supplied data needed for checking trigger conditions
/// @param subData User supplied data used in recipe
struct StrategySub {
    uint64 id;
    bool isBundle;
    bytes[] triggerData;
    bytes32[] subData;
}
```

Below is the interface of the contract:

```solidity
contract SubStorage {
    /// @notice Adds users info and records StoredSubData, logs StrategySub
    /// @dev To save on gas we don't store the whole struct but rather the hash of the struct
    /// @param _sub Subscription struct of the user (is not stored on chain, only the hash)
    function subscribeToStrategy(
        StrategySub memory _sub
    ) public isValidId(_sub.id, _sub.isBundle) returns (uint256);
    
    /// @notice Updates the users subscription data
    /// @dev Only callable by proxy who created the sub.
    /// @param _subId Id of the subscription to update
    /// @param _sub Subscription struct of the user (needs whole struct so we can hash it)
    function updateSubData(
        uint256 _subId,
        StrategySub calldata _sub
    ) public onlySubOwner(_subId) isValidId(_sub.id, _sub.isBundle);
    
    /// @notice Enables the subscription for execution if disabled
    /// @dev Must own the sub. to be able to enable it
    /// @param _subId Id of subscription to enable
    function activateSub(
        uint _subId
    ) public onlySubOwner(_subId);
    
    /// @notice Disables the subscription (will not be able to execute the strategy for the user)
    /// @dev Must own the sub. to be able to disable it
    /// @param _subId Id of subscription to disable
    function deactivateSub(
        uint _subId
    ) public onlySubOwner(_subId);
    
    ///////////////////// VIEW ONLY FUNCTIONS ////////////////////////////
    
    function getSub(uint _subId) public view returns (StoredSubData memory);
    function getSubsCount() public view returns (uint256);
}
```


# Bundle Storage

Record of all the Bundles created

Bundles are grouped strategies that serve the same purpose and have the same triggers are grouped into a bundle. This is used for gas savings where users can subscribe to a bundle and the bundle will contain a few different recipes to accomplish the same goal. An example of this is to use a normal MakerRepay recipe or a Flash loan version; which one is better might vary from user to user or the current market situation. Bot executing the transaction can choose the best strategy from the bundle to which the user subscribed.

{% hint style="info" %}
Bundles can currently only be created by the owner, but there is a flag to open it so anyone can create bundles.
{% endhint %}

BundleStorage stores an array of bundles which are a list of strategies and a creator field.

```solidity
/// @dev Group of strategies bundled together so user can sub to multiple strategies at once
/// @param creator Address of the user who created the bundle
/// @param strategyIds Array of strategy ids stored in StrategyStorage
struct StrategyBundle {
    address creator;
    uint64[] strategyIds;
}
```

{% hint style="info" %}
Strategies in a bundle must have the same triggers in their exact order.
{% endhint %}

Below is the interface of the contract:

```solidity
contract BundleStorage {
    /// @notice Adds a new bundle to array
    /// @dev Can only be called by auth addresses if it's not open to public
    /// @dev Strategies need to have the same number of triggers and ids exists
    /// @param _strategyIds Array of strategyIds that go into a bundle
    function createBundle(
        uint64[] memory _strategyIds
    ) public onlyAuthCreators sameTriggers(_strategyIds) returns (uint256);
    
    /// @notice Switch to determine if bundles can be created by anyone
    /// @dev Callable only by the owner
    /// @param _openToPublic Flag if true anyone can create bundles
    function changeEditPermission(bool _openToPublic) public onlyOwner;
    
    ////////////////////////////// VIEW METHODS /////////////////////////////////
    
    function getStrategyId(uint256 _bundleId, uint256 _strategyIndex) public view returns (uint256);
    function getBundle(uint _bundleId) public view returns (StrategyBundle memory);
    function getBundleCount() public view returns (uint256);
    function getPaginatedBundles(uint _page, uint _perPage) public view returns (StrategyBundle[] memory); 
}
```


# Actions

An Action is a contract that will perform a specific operation. In the context of DeFi, that includes actions such as supplying an asset, making a token swap, making a deposit, etc.

Actions are registered in the `DFSRegistry` and can be combined into recipes. Actions are always called through a user's wallet (`Safe, DSProxy, DSAProxy, SummerFi wallet`) and cannot hold any state. Flashloan Actions are a special type of actions which can hold state (reentrancy field) but they are not called through user's wallet. There are currently only two types of actions: Standard action and `FlashLoan` action. We need to differentiate between these, as they will have different execution paths in the `RecipeExecutor`.

{% hint style="info" %}
When calling a FlashLoan action, it only makes sense to call them as part of a Recipe. You also need to add an extra empty input in the callData if it's a FlashLoan action.
{% endhint %}

Each action should inherit the `ActionBase` contract, which implements a standard interface to call the actions and some helper functions or contracts to make development easier. Actions can be called directly through a user's walelt with `executeActionDirect()` which takes an array of bytes representing the inputs of that action. Each action can have different inputs and in order to keep a universal interface, all the inputs are converted into a bytes array before calling. Action always returns one `bytes32` value, which can later be used as input for other actions.

{% hint style="info" %}
While a single Action can be executed as a Recipe through the `RecipeExecutor`, it is more gas efficient to call a single Action directly when needed.
{% endhint %}

Each action should also produce a log message in the standard `DFSLogger`.

Actions can be bundled into Recipes, which can then be executed manually or as part of a Strategy. Because of this, some of the inputs of an Action can be hardcoded in Subscription data or it can be an output of a different Action. That's why when an Action is executed inside of a recipe a different function is used `executeAction()`. Here `subData` is passed along as well as `paramMapping` data and `returnValues` from previous functions.

### Return values and subscription data mapping

`paramMapping` is an array of `uint8` values representing if any inputs of the actions need to be switched out, either with `subData` or by `returnValues`.

If the value is 0, this means the inputs are used and not modified. Values in the range of `[1-127]` are used for `returnValues` mapping, meaning that 1 means the return value of the first action, 2 represents the second action return values, etc... going up to 127.

If values are in the `[127-255]` range that means that the `Subscriptions` subData is used to replace inputs, following the same logic as return values in how they are mapped.

{% hint style="info" %}
The last values in the subData range, 254 and 255 are 'reserved'. That means that the ActionBase contract and the related parseParam methods will inject:

* for 254 the address of the user's wallet
* for 255 will inject the address of the:
  * owner of the DSProxy if wallet is DSProxy.
  * owner of Safe, in case of 1/1 wallet, and safe wallet itself in case of n/m wallet
    {% endhint %}

```
// Mapping example
// In an action that has 3 inputs, the first two are not changed
// and the third one will be changed with the return value of the second action
[0, 0, 2] 
```

Below is the interface of the ActionBase:

```solidity
contract ActionBase {

    enum ActionType { FL_ACTION, STANDARD_ACTION, FEE_ACTION, CHECK_ACTION, CUSTOM_ACTION }

    /// @notice Parses inputs and runs the implemented action through a user's wallet
    /// @dev Is called by the RecipeExecutor chaining actions together
    /// @param _callData Array of input values each value encoded as bytes
    /// @param _subData Array of subscribed vales, replaces input values if specified
    /// @param _paramMapping Array that specifies how return and subscribed values are mapped in input
    /// @param _returnValues Returns values from actions before, which can be injected in inputs
    /// @return Returns a bytes32 value, each actions implements what that value is
    function executeAction(
        bytes memory _callData,
        bytes32[] memory _subData,
        uint8[] memory _paramMapping,
        bytes32[] memory _returnValues
    ) public payable virtual returns (bytes32);

    /// @notice Parses inputs and runs the single action through a user's wallet
    /// @dev Used to save gas when executing a single action directly
    function executeActionDirect(bytes memory _callData) public virtual payable;

    /// @notice Returns the type of action we are implementing
    function actionType() public pure virtual returns (uint8);
}
```


# Triggers

A Trigger is a contract that will perform a check if certain conditions are met. It is used as a part of a Strategy to check when to execute a defined Recipe. Before executing recipe from a strategy RecipeExecutor contracts checks if every trigger in strategy is triggered and if needed rewrites some of the trigger parameters (e.g. updating to a new timestamp in the TimestampTrigger)

There is only one function that needs to be implemented and that is `isTriggered()` which returns a `bool` of the state of the trigger. When used within a strategy, all triggers need to return true for the Task to be executed.

Functions that need to be implemented are:

* `isTriggered()` which returns a `bool` of the state of the trigger. When used within a strategy, all triggers need to return true for the Task to be executed.
* `isChangeable()` which returns a `bool` if the trigger can have it's sub data changed during a strategy execution.
* `changedSubData()` which returns `bytes` that the RecipeExecutor contract can overwrite the existing triggerData with. (RecipeExecutor line 116) This needs to be implemented only if isChangeable() returns true.

Triggers can accept external data from the bot caller if needed, but will generally rely on the subscription data.

{% hint style="info" %}
Triggers like actions can only be added by the admin through the DFSRegistry contract
{% endhint %}

Below is the interface of the TriggerInterface:

```solidity
abstract contract ITrigger {
    function isTriggered(bytes memory, bytes memory) public virtual returns (bool);
    function isChangeable() public virtual returns (bool);
    function changedSubData(bytes memory) public virtual returns (bytes memory);
}

```


# Strategies

Strategy is a collection of triggers (conditions) and actions (Recipe) executed once the specified conditions are met. You can think of Strategies as Recipes that are not manually executed by the user but rather executed at some point in the future by bots when the conditions are met. Strategies are executed by Defi Saver backend bots, which themselves do not hold permission to users' funds. All execution is done through the user's wallet (`Safe,DSProxy`), which gives authorization to a smart contract (`SafeModuleAuth, ProxyAuth`) to execute the Strategy once it's possible.

An example of a strategy would be a simple Repay strategy for Maker vaults, which would take Dai earning yield in another protocol and pay back the users vault if it goes below a specific ratio. Here we would have a `McdRatioTrigger` as an only trigger to the strategy and a recipe that would withdraw yield and call `McdPayback`.

The costs of sending the transaction are taken from the user's position, and each strategy specifies where and in what tokens that fee will come from. The user does not have to give any allowances or maintain any balances in certain wallets, as for instance, dai generated from a Maker vault would be used for payment.

There are two types of strategies that can be created:

1. **Continuous** - Can be executed multiple times if the trigger conditions are met. An example is a maker repay strategy that might repay the users various times when price crashes happen
2. **One time** - A strategy triggered only once and after that would be disabled. Users can manually re-enable the action, so it is eligible to execute again.

### Roles

* **Strategy designer** - A person who creates a strategy, which will enable other users to subscribe to that strategy. Strategy designer will create a combination of triggers and actions and how values are mapped to it. Only the DFS team will be creating strategies in the first version of the system, but that restriction will be lifted in the next versions.
* **Subscribers** - Users who subscribe to a certain strategy provide their own subscription data that represent the positions that they want to automate. Subscribers will be able to choose and subscribe to strategies on the interface easily and it won't require any advanced knowledge.
* **Bots** - Backend code built by the DFS team monitors users' subscriptions to strategies and triggers them accordingly. Bots are the only ones with the authority to trigger actions, but they do not hold user funds or control user positions. Smart contracts hold authority and bots are the only ones that can call the smart contract actions.


# TxSaver

TxSaver is a service that executes transactions on behalf of users. Users using the DefiSaver frontend sign their transaction, that will be executed through their own personal safe wallet. Signature validation is done by the safe wallet. Given that anyone can execute a signed safe transaction, we execute that tx for the user, sending the signed tx to our backend that calls the `TxSaverExecutor` contract. It enables us to offer MEV protection and protection against failed tx, better price and order routes and also remove the complexity of sending a tx from the user. Users sign a maximum amount of fee tokens they are willing to pay for a transaction. Current contract architecture supports paying fees in various form:

* From the user’s position, when executing recipes that has a sell action.
* From the smart wallet owner's EOA (Externally Owned Account), if the safe wallet is a 1/1 wallet.
* From the smart wallet itself, if the safe wallet is an n/m wallet.

{% hint style="info" %}
From the release only first option will be enabled and supported, meaning users will have option to send transactions when executing advanced DefSaver actions and paying gas cost from their position. Also, transactions that have msg.value will not be sent through TxSaver.
{% endhint %}

### Notice

* TxSaver **only** works with the safe wallet, as we take a safe tx that the user signs and execute it for the user.
* It requires no additional authorization as anyone can execute a signed safe tx on behalf of the user.
* Main and the only entry point is **TxSaverExecutor** which later on calls the already existing system of the **RecipeExecutor** with addition of handling fee taking and order injection.
* Backend has an option to inject a new exchange route if the old one is outdated or if we can fetch a

  better one (only the route is injected, amount and minPrice from the user signed tx are

  **immutable**).
* Fee for the tx execution is taken from the user position in source token before swap execution inside contracts.
* Backend send the gasUsed for the tx as there is no way on the solidity side to calculate the exact

  amount spent (with gas refunds taken into account). There are limits that the fee can't be larger

  than the maximum signed by the user.
* Our own backend is the only one sending the tx, and has necessary checks that the fee and gasCost are correct.
* Tx has a deadline parameter so we can't send signed tx later in time.
* For tx that take fee from the position, fee taking is inside **DFSSell** action.

### TxSaver Smart Contracts

<figure><img src="/files/TtDQOozfCqpPoROMU4tB" alt=""><figcaption><p>TxSaver Smart Contracts Architecture With Existing Recipe System</p></figcaption></figure>

**TxSaverExecutor**

This is the main entry point for executing TxSaver transactions, with one `executeTx` function that can only be called by our bots:

```solidity
/// @notice Execute a TxSaver transaction signed by user
/// @notice When taking fee from position, gas fee is taken inside sell action.
/// @notice Right now, we only support fee taking from position if recipe has sell action
///
/// @notice when fee is taken from EOA/wallet:
/// @notice If wallet is 1/1, gas fee is taken from eoa
/// @notice If wallet is n/m, gas fee is taken from wallet itself
///
/// @param _params SafeTxParams data needed to execute safe tx
/// @param _estimatedGas Estimated gas usage for the transaction
/// @param _l1GasCostInEth Additional gas cost added for Optimism based L2s
/// @param _injectedExchangeData Exchange data injected by backend
function executeTx(
    SafeTxParams calldata _params,
    uint256 _estimatedGas,
    uint256 _l1GasCostInEth,
    DFSExchangeData.InjectedExchangeData calldata _injectedExchangeData
) external {
```

**BotAuthForTxSaver**

Handles authorization of who can call TxSaverExecutor.

**TxSaverBytesTransientStorage**

Helper contract used for storing injected order and params for gas fee taking. This contract is part of TxSaverExecutor.

### Additional signature data

Data for safe transaction is represented in struct `SafeParams` :

```solidity
/// @notice Data needed to execute a Safe transaction
/// @param safe Address of the Safe wallet
/// @param refundReceiver Injected address to track safe points
/// @param data Data payload of Safe transaction
/// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v})
struct SafeTxParams {
    address safe;
    address refundReceiver;
    bytes data;
    bytes signatures;
}
```

`Data` field represents encoded call to function `executeRecipeFromTxSaver` inside `RecipeExecutor` :

```solidity
/// @notice Called by TxSaverExecutor through safe wallet
/// @param _currRecipe Recipe to be executed
/// @param _txSaverData TxSaver data signed by user
function executeRecipeFromTxSaver(
    Recipe calldata _currRecipe,
    TxSaverSignedData calldata _txSaverData
) public payable {
```

Besides the regular recipe, the tx will have additional data represented in a struct `TxSaverSignedData` :

```solidity
/// @dev Data needed when signing tx saver transaction
/// @param maxTxCostInFeeToken Max tx cost user is willing to pay in fee token
/// @param feeToken Address of the token user is willing to pay fee in
/// @param tokenPriceInEth Price of the token in ETH
/// @param deadline Deadline for the relay transaction to be executed
/// @param shouldTakeFeeFromPosition Flag to indicate if fee should be taken from position, otherwise from EOA/wallet
struct TxSaverSignedData {
    uint256 maxTxCostInFeeToken;
    address feeToken;
    uint256 tokenPriceInEth;
    uint256 deadline;
    bool shouldTakeFeeFromPosition;
}
```

Since only taking the fee from the position is enabled in the first iteration, `shouldTakeFeeFromPosition` will be set to `true`. Additionally, `feeToken` will not be used, as the fee will be taken from the source token from position anyway.

### Security

The system is both internally and externally audited, with the audit available at: [Security & Audits](/protocol/security-and-audits)


# Security & Audits

## Audits

| Description                 | Auditor                                       | Date      | Report                                                                                                                  |
| --------------------------- | --------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------- |
| DFS Recipe Audit            | [Dedaub](https://www.dedaub.com/)             | Mar. 2021 | [link](https://github.com/DecenterApps/defisaver-v3-contracts/blob/main/audits/Dedaub-Mar-2021.pdf)                     |
| DFS Recipe Audit            | [Consensys](https://consensys.net/diligence/) | Mar. 2021 | [link](https://github.com/DecenterApps/defisaver-v3-contracts/blob/main/audits/Consensys-Mar-2021.pdf)                  |
| Strategies Audit            | [Dedaub](https://www.dedaub.com/)             | Jan. 2022 | [link](https://github.com/defisaver/defisaver-v3-contracts/blob/feature/strategies/audits/DFS-Strategies-Jan-2022.pdf)  |
| Safe Upgrade Audit          | [Dedaub](https://dedaub.com/)                 | Feb. 2024 | [link](https://github.com/defisaver/defisaver-v3-contracts/blob/main/audits/Dedaub-Safe-Update-Feb-2024.pdf)            |
| BytesTransientStorage Audit | [Optimum](https://www.optimumsec.xyz/)        | Apr. 2024 | [link](https://github.com/defisaver/defisaver-v3-contracts/blob/main/audits/Optimum-BytesTransientStorage-Apr-2024.pdf) |
| TxSaver Audit               | [Optimum](https://www.optimumsec.xyz/)        | Jun. 2024 | [link](https://github.com/defisaver/defisaver-v3-contracts/blob/main/audits/Optimum-TxSaver-Jun-2024.pdf)               |

## Bounty

<https://immunefi.com/bounty/defisaver/>


# Pause Control

Each contract can be killed (destroyed) by the Owner multisig, which effectively pauses any execution until further investigation is done. All of the contracts that can be self destructed don't hold any funds so there is no risk of locking user funds in that way.

In the DFSRegistry, the Owner multisig can revert the contract address to the previous one without any timelock. If the current contract has a fault, it's a quick way to fall back to the earlier version.

Enough signers for multisig can be assembled in less than one hour for any emergencies.


# Admin access control

DeFi Saver is a management app for decentralized finance positions with a varying level of admin controllable elements depending on the part of the app used.

We want to highlight different layers of smart contracts that should be distinguished.

### 1 - User's Smart Wallet (Safe, DSProxy)

Each user has their own wallet through which they interact with the DFS contracts. Most contracts in the DFS system are logic/target contracts that don't hold and state or funds and are called through the users wallet. In order to execute actions for the user (strategy system) `auth` permission is given to the auth contract (`SafeModuleAuth` in case of Safe, `ProxyAuth` in case of DSProxy) which are immutable.

### 2 - Protocol actions smart contracts

All contracts are found [here](https://github.com/defisaver/defisaver-v3-contracts).

These are the smart contracts used by our recipe architecture and utilized by users when using the DeFi Saver interface (application).

While these contracts are, for the most part, immutable, that isn't a specifically relevant characteristic, as they can be replaced in the registry and consequently in the UI when updating is needed due to adding support for more protocol interactions, optimising gas usage, or other reasons.

These contracts have been audited by ConsenSys Diligence and Dedaub, with reports available [here](https://github.com/DecenterApps/defisaver-v3-contracts/tree/main/audits).

### 3 - DFS Registry

This smart contract holds the addresses of live DFS contracts retrievable by hashed contract name. With only the multisig owner being able to add new contracts, change existing ones or revert to the old one. When adding a new contract to the registry, a time lock can be added, so every time a new change of that contract is requested, it's locked from executing until enough time has passed. **Core contracts have the timelock time set to 7 days while other action contracts that are used in strategies have a set change time of 1 day.**

### 4 - Exchange Wrapper Allowlist

In order to validate wrapper exchange addresses this is a contract where we keep track of them. This contract holds the addresses of wrapper contracts (found [here](https://github.com/defisaver/defisaver-v3-contracts/tree/main/contracts/exchangeV3)) that can be added/removed from the contracts via the multisig owner. These wrapper contracts are used when swapping tokens via DFSSell action, which can only be done via pre-approved wrappers.

### 5 - Recipe Executor Contract

This contract is the starting point for executing recipes.

It's a contract that doesn't hold state, owned by the multisig, which can kill or withdraw leftover funds from it. The functions on it are to be called through users wallet, along with all the necessary calldata. During recipe execution, the Action addresses that the proxy will execute are found through DFSRegistry, a state-holding contract that returns an address via bytes32 id (hashed name of the action).

### 6 - Automation smart contracts

Automation is a trustless, non-custodial service for management of collateralized debt positions.

The logic for executing user configured actions is contained within Automation smart contracts and user configurations are stored on chain.

Automation contracts are upgradeable, with any upgrades being locked behind a 24h timelock which can be activated by a 2/3 multisig (Admin) with actual upgrades initiable by a different 3/5 multisig (Owner).

For more information about the security of Automation, we recommend visiting our security audit report summary post [here](https://medium.com/defi-saver/defi-saver-automation-security-audit-summary-a883d4fde1b).

Automation smart contracts have been audited by Dedaub with the report available [here](https://github.com/DecenterApps/defisaver-contracts/blob/master/audits/Dedaub%20-%20DeFi%20Saver%20Automation%20Audit%20-%20February%202021.pdf).<br>


# Deployed contracts

All of the Defi Saver related addresses can be found [here](https://github.com/defisaver/defisaver-v3-contracts/tree/main/addresses).

{% embed url="<https://github.com/defisaver/defisaver-v3-contracts/blob/main/addresses/mainnet.json>" %}
Mainnet addresses
{% endembed %}

{% embed url="<https://github.com/defisaver/defisaver-v3-contracts/blob/main/addresses/arbitrum.json>" %}
Arbitrum addresses
{% endembed %}

{% embed url="<https://github.com/defisaver/defisaver-v3-contracts/blob/main/addresses/optimism.json>" %}
Optimism addresses
{% endembed %}

{% embed url="<https://github.com/defisaver/defisaver-v3-contracts/blob/main/addresses/base.json>" %}
Base addresses
{% endembed %}

{% embed url="<https://github.com/defisaver/defisaver-v3-contracts/blob/main/addresses/linea.json>" %}
Linea addresses
{% endembed %}

{% embed url="<https://github.com/defisaver/defisaver-v3-contracts/blob/main/addresses/plasma.json>" %}
Plasma addresses
{% endembed %}


# Deployed strategies

All of the information about Defi Saver deployed strategies can be found here [here](https://github.com/defisaver/defisaver-v3-contracts/tree/main/addresses/strategies).

{% embed url="<https://github.com/defisaver/defisaver-v3-contracts/blob/main/addresses/strategies/mainnet.json>" %}
Mainnet strategies
{% endembed %}

{% embed url="<https://github.com/defisaver/defisaver-v3-contracts/blob/main/addresses/strategies/arbitrum.json>" %}
Arbitrum strategies
{% endembed %}

{% embed url="<https://github.com/defisaver/defisaver-v3-contracts/blob/main/addresses/strategies/optimism.json>" %}
Optimism strategies
{% endembed %}

{% embed url="<https://github.com/defisaver/defisaver-v3-contracts/blob/main/addresses/strategies/base.json>" %}
Base strategies
{% endembed %}


# Exchange

Token swaps at DeFi Saver are done in a fully non-custodial way, with tokens being swapped on-chain using decentralized exchanges and DEX aggregators to find the best swap rate at the moment.

<figure><img src="/files/cjlpPOMuoJ8dPI12eiKf" alt=""><figcaption><p>Basic overview of Defi Saver exchange components</p></figcaption></figure>


# DFS Sell

### Description

**Action ID:** 0x7f2a0f35

Exchange two tokens.

### SDK Action

```javascript
const dfsSellAction = new dfs.actions.basic.SellAction(
    exchangeOrder,
    from,
    to,
);
```

### Contract

This is a DFS **STANDARD\_ACTION**.

**Input:**

```solidity

struct OffchainData {
    address wrapper; // dfs wrapper address for the aggregator (must be in WrapperExchangeRegistry)
    address exchangeAddr; // exchange address we are calling to execute the order (must be in ExchangeAggregatorRegistry)
    address allowanceTarget; // exchange aggregator contract we give allowance to
    uint256 price; // expected price that the aggregator sent us
    uint256 protocolFee; // deprecated (used as a separate fee amount for 0x v1)
    bytes callData; // 0ff-chain calldata the aggregator gives to perform the swap
}

struct ExchangeData {
    address srcAddr; // source token address (which we're selling)
    address destAddr; // destination token address (which we're buying)
    uint256 srcAmount; // amount of source token in token decimals
    uint256 destAmount; // amount of bought token in token decimals
    uint256 minPrice; // minPrice we are expecting (checked in DFSExchangeCore)
    uint256 dfsFeeDivider; // service fee divider
    address user; // user to check if custom fee is set for the user
    address wrapper; // on-chain wrapper address (must be in WrapperExchangeRegistry)
    bytes wrapperData; // on-chain additional data for on-chain (uniswap route for example)
    OffchainData offchainData; // offchain aggregator order
}

// @param exchangeData data
// @param from The order sender
// @param to The order recipient
struct Params {
    ExchangeData exchangeData;
    address from;
    address to;
}
```

**Return value:**

```solidity
return bytes32(exchangeAmount);
```

#### Events:

```solidity
emit ActionEvent("DFSSell", logData);

logger.logActionDirectEvent("DFSSell", logData);

bytes memory logData = abi.encode(
    wrapper,
    _exchangeData.srcAddr,
    _exchangeData.destAddr,
    _exchangeData.srcAmount,
    exchangedAmount,
    _exchangeData.dfsFeeDivider
);
```


# DFSSellNoFee

### Description

A exchange sell action through the dfs exchange that does not take any fee

> **Notes**
>
> Sells a specified srcAmount for the dest token

### Action ID

`0xebf16d4a`

### SDK Action

```ts
const dFSSellNoFeeAction = new dfs.actions.DFSSellNoFeeAction(
    exchangeOrder,
    from,
    to,
);

```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    
struct OffchainData {
    address wrapper; // dfs wrapper address for the aggregator (must be in WrapperExchangeRegistry)
    address exchangeAddr; // exchange address we are calling to execute the order (must be in ExchangeAggregatorRegistry)
    address allowanceTarget; // exchange aggregator contract we give allowance to
    uint256 price; // expected price that the aggregator sent us
    uint256 protocolFee; // deprecated (used as a separate fee amount for 0x v1)
    bytes callData; // 0ff-chain calldata the aggregator gives to perform the swap
}

struct ExchangeData {
    address srcAddr; // source token address (which we're selling)
    address destAddr; // destination token address (which we're buying)
    uint256 srcAmount; // amount of source token in token decimals
    uint256 destAmount; // amount of bought token in token decimals
    uint256 minPrice; // minPrice we are expecting (checked in DFSExchangeCore)
    uint256 dfsFeeDivider; // service fee divider
    address user; // user to check if custom fee is set for the user
    address wrapper; // on-chain wrapper address (must be in WrapperExchangeRegistry)
    bytes wrapperData; // on-chain additional data for on-chain (uniswap route for example)
    OffchainData offchainData; // offchain aggregator order
}

// @param exchangeData data
// @param from The order sender
// @param to The order recipient
struct Params {
    ExchangeData exchangeData;
    address from;
    address to;
}
```

### Return Value

```solidity
return bytes32(exchangedAmount);
```

### Events and Logs

```solidity
emit ActionEvent("DFSSellNoFee", logData);
logger.logActionDirectEvent("DFSSellNoFee", logData);
bytes memory logData = abi.encode(params);
```


# LimitSell

### Description

**Action ID:** 0x8c712c04

A special Limit Sell action used as a part of the limit order strategy

### SDK Action

```javascript
const dfsLimitSellAction = new dfs.actions.basic.LimitSell(
    exchangeOrder,
    from,
    to,
    gasUsed
);
```

### Contract

This is a DFS **STANDARD\_ACTION**.

**Input:**

```solidity

struct OffchainData {
    address wrapper; // dfs wrapper address for the aggregator (must be in WrapperExchangeRegistry)
    address exchangeAddr; // exchange address we are calling to execute the order (must be in ExchangeAggregatorRegistry)
    address allowanceTarget; // exchange aggregator contract we give allowance to
    uint256 price; // expected price that the aggregator sent us
    uint256 protocolFee; // deprecated (used as a separate fee amount for 0x v1)
    bytes callData; // 0ff-chain calldata the aggregator gives to perform the swap
}

struct ExchangeData {
    address srcAddr; // source token address (which we're selling)
    address destAddr; // destination token address (which we're buying)
    uint256 srcAmount; // amount of source token in token decimals
    uint256 destAmount; // amount of bought token in token decimals
    uint256 minPrice; // minPrice we are expecting (checked in DFSExchangeCore)
    uint256 dfsFeeDivider; // service fee divider
    address user; // user to check if custom fee is set for the user
    address wrapper; // on-chain wrapper address (must be in WrapperExchangeRegistry)
    bytes wrapperData; // on-chain additional data for on-chain (uniswap route for example)
    OffchainData offchainData; // offchain aggregator order
}

// @param exchangeData data
// @param from The order sender
// @param to The order recipient
// @param gasUsed Amount of gas spent as part of the strategy   
struct Params {
    ExchangeData exchangeData;
    address from;
    address to;
    uint256 gasUsed;
}
```

**Return value:**

```solidity
return bytes32(exchangeAmount);
```

#### Events:

```solidity
emit ActionEvent("LimitSell", logData);

bytes memory logData = abi.encode(
    wrapper,
    _exchangeData.srcAddr,
    _exchangeData.destAddr,
    _exchangeData.srcAmount,
    exchangedAmount,
    _exchangeData.dfsFeeDivider
);
```


# Flash loans

{% hint style="info" %}
In a DFS Recipe, flash loan actions must be the first action in the recipe. If a FL action is put anywhere else it will fail. The flash loan is returned after all other actions are finished, and it expects to have funds return to the FL action that took the loan.
{% endhint %}

Flash loan actions are a special kind of action in the DeFi Saver Recipe system. These are the only actions that do not run in a context of the user's wallet, but rather the action itself is the caller and the receiver of the flash loan. When calling a flash loan action, an additional empty callData is needed, as that is used to pass on the data of the other actions in the recipe.

{% hint style="info" %}
While other actions can be called directly (not through RecipeExecutor), flash loan actions can only be called through RecipeExecutor as it makes no sense to only call a flash loan action.
{% endhint %}

You may also notice that in every flash loan action there are 2 extra callData parameters `flParamGetterAddr` and `flParamGetterData`. Both can be used for on-chain getting of flash loan parameters. Because the flash loan action is always the first action, we can't pipe any previous action data into these actions, so we can call the `flParamGetterAddr` supplied by the user and on-chain fetch flash loan amounts and other info. This is needed where we for instance want to get the exact Maker Vault debt which changes from block to block, so that information must be fetched in that transaction.

Different flashloan sources supported at DefiSaver at the moment:

```solidity
enum FLSource {
    EMPTY,
    AAVEV2,
    BALANCER,
    GHO,
    MAKER,
    AAVEV3,
    UNIV3,
    SPARK,
    MORPHO_BLUE,
    CURVEUSD,
    BALANCER_V3
}
```


# FLAction

### Description

**Action ID:** 0xbcab5e2a

Generalized fl action that gets and receives FL from different variety of sources.

### SDK Action

```javascript
const specificFLAction = new dfs.actions.flashloan.BalancerFlashLoanAction(
    tokens,
    amounts,
);

const flAction = new dfs.actions.flashloan.FLAction(specificFLAction);

```

{% hint style="info" %}
In FLAction `flParamGetterAddr` and `flParamGetterData` are not used for on-chain getting of flash loan parameters. `flParamGetterData` is used to choose between FL providers
{% endhint %}

### Contract

This is a DFS **FL\_ACTION**.

**Input:**

Inputs are not parsed as the FL action is always the first action and there are no return values before this action

```solidity
// @param tokens Array of tokens being flash loaned
// @param amounts Array of amounts being flash loaned
// @param modes Modes we want to flash loan (repay debt or incur debt, only 0 debt type is supported).
// @param onBehalfOf If we are not repaying the flash loan what address will incur the debt (can be empty if we are just repaying the loan)
// @param flParamGetterAddr Address of an on chain contract that can change (amount, token) while calling the action. If it's an empty address it will not be called. Not used in this implementation.
// @param flParamGetterData Used to choose between FL providers
// @param recipeData Recipe data for post fl execution inside recipe
struct FlashLoanParams {
    address[] tokens;
    uint256[] amounts;
    uint256[] modes;
    address onBehalfOf;
    address flParamGetterAddr;
    bytes flParamGetterData;
    bytes recipeData;
}
```

**Return value:**

```solidity
return bytes32(amount);
```

#### Events:

```solidity
emit ActionEvent("FLAction", abi.encode("<FL_PROVIDER_NAME>", flParams);
```

### Supported providers

#### Aave V2/V3

Aave V2/V3 flash loans are specific as you can borrow multiple assets all at once and repay them at the end or incur debt at the end. When repaying the Aave V2/V3 FL you only need to set an approval and tokens will get pulled with no need to send the FL amount anywhere

#### Spark

Same as AAVE

#### Balancer

Receives a flash loan from the Balancer Vault. Multiple assets can be borrowed and repaid in the same flash loan

{% hint style="info" %}
Asset borrow limit is determined by the asset balance of the Vault.
{% endhint %}

#### Maker

Receives a flash loan from the Maker protocol. Protocol only supports DAI loans.

{% hint style="info" %}
The flash loan fee and limit are set by the Maker Governance, they are liable to change in the future. Currently the fee is set at `0%` and the loan limit is `0.5 * 1e9 DAI`
{% endhint %}

#### Uniswap V3

Receives a flash loan from Uniswap V3 protocol.

#### Gho

Gets a GHO FL from Gho Flash Minter.

#### Morpho Blue

Receives a flash loan from Morpho Blue protocol.

#### Curve USD

Receives a crvUSD flash loan from CurveUsd protocol.

#### Balancer V3

Receives a flash loan from BalancerV3 Vault. Multiple assets can be borrowed and repaid in the same flash loan.


# Utils

Helper utility contracts.


# ApproveToken

### Description

Helper action to approve spender to pull an amount of tokens from user's wallet

> **Notes**
>
> Approves an amount of tokens for spender to pull from user's wallet

### Action ID

`0xbb8027f4`

### SDK Action

```ts
const approveTokenAction = new dfs.actions.basic.ApproveTokenAction(
    token,
    spender,
    amount
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param tokenAddr Address of token to approve
    /// @param spender Address of the spender
    /// @param amount Amount of tokens to approve
    struct Params {
        address tokenAddr;
        address spender;
        uint256 amount;
    }
```

### Return Value

```solidity
return bytes32(inputData.amount);
```

### Events and Logs

```solidity
emit ActionEvent("ApproveToken", logData);
logger.logActionDirectEvent("ApproveToken", logData);
bytes memory logData = abi.encode(params);
```


# AutomationV2Unsub

### Description

Unsubscribe from old automation v2.

> **Notes**
>
> This action is deprecated.

### Action ID

`0x6195d72e`

### SDK Action

```ts
const automationV2UnsubAction = new dfs.actions.basic.AutomationV2Unsub(
    protocol,
    cdpId
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param cdpId ID of the cdp to unsubscribe from
    /// @param protocol Protocol to unsubscribe from (MCD, COMPOUND, AAVE)
    struct Params {
        uint256 cdpId;
        Protocols protocol;
    }
```

### Return Value

```solidity
return bytes32(0);
```

### Events and Logs

```solidity
emit ActionEvent("Unsubscribe", logData);
logger.logActionDirectEvent("Unsubscribe", logData);
bytes memory logData = abi.encode(params);
```


# ChangeProxyOwner

### Description

Changes the owner of the DSProxy and updated the DFSRegistry

### Action ID

`0x67314f12`

### SDK Action

```ts
const changeProxyOwnerAction = new dfs.actions.basic.ChangeProxyOwnerAction(
    newOwner
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param newOwner Address of the new owner
    struct Params {
        address newOwner;
    }
```

### Return Value

```solidity
return bytes32(bytes20(inputData.newOwner));
```

### Events and Logs

```solidity
```


# CreateSub

### Description

Action to create a new subscription

> **Notes**
>
> Gives user's wallet permission if needed and registers a new sub

### Action ID

`0xf41e543f`

### SDK Action

```ts
const createSubAction = new dfs.actions.basic.CreateSubAction(
    sub
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param _sub Subscription struct of the user (is not stored on chain, only the hash)
    struct Params {
        StrategyModel.StrategySub sub;
    }
```

### Return Value

```solidity
return (bytes32(subId));
```

### Events and Logs

```solidity
```


# GasFeeTaker

### Description

Helper action to take gas fee from the user's wallet and send it to the fee recipient.

> **Notes**
>
> If divider is lower the fee is greater, should be max 5 bps

### Action ID

`0x4571b8b3`

### SDK Action

```ts
const gasFeeTakerAction = new dfs.actions.basic.GasFeeAction(
    gasStart,
    feeToken,
    availableAmount,
    dfsFeeDivider
);

```

### Action Type

`FEE_ACTION`

### Input Parameters

```solidity
    /// @param gasUsed Gas used by the transaction
    /// @param feeToken Address of the token to send
    /// @param availableAmount Amount of tokens available to send
    /// @param dfsFeeDivider Divider for the DFS fee
    struct GasFeeTakerParams {
        uint256 gasUsed;
        address feeToken;
        uint256 availableAmount;
        uint256 dfsFeeDivider;
    }
```

### Return Value

```solidity
return bytes32(amountLeft);
```

### Events and Logs

```solidity
```


# GasFeeTakerL2

### Description

Helper action to take gas fee from the user's wallet on L2 and send it to the fee recipient.

> **Notes**
>
> If divider is lower the fee is greater, should be max 5 bps.

### Action ID

`0x7ec82077`

### SDK Action

```ts
const gasFeeTakerL2Action = new dfs.actions.basic.GasFeeActionL2(
    gasStart,
    feeToken,
    availableAmount,
    dfsFeeDivider,
    l1GasCostInEth
);
```

### Action Type

`FEE_ACTION`

### Input Parameters

```solidity
    /// @param gasUsed Gas used by the transaction
    /// @param feeToken Address of the token to send
    /// @param availableAmount Amount of tokens available to send
    /// @param dfsFeeDivider Divider for the DFS fee
    /// @param l1GasCostInEth Additional L1 gas cost in Eth
    struct Params {
        uint256 gasUsed;
        address feeToken;
        uint256 availableAmount;
        uint256 dfsFeeDivider;
        uint256 l1GasCostInEth;
    }
```

### Return Value

```solidity
return bytes32(amountLeft);
```

### Events and Logs

```solidity
```


# HandleAuth

### Description

Action to enable/disable smart wallet authorization

### Action ID

`0xd951fa77`

### SDK Action

```ts
const handleAuthAction = new dfs.actions.basic.HandleAuthAction(
    enableAuth
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param enableAuth Whether to enable or disable authorization
    struct Params {
        bool enableAuth;
    }
```

### Return Value

```solidity
return bytes32(0);
```

### Events and Logs

```solidity
```


# KingClaim

### Description

Action to Claim KING token as EtherFi reward on behalf of smart wallet

### Action ID

`0xb5997e1a`

### SDK Action

```ts
const kingClaimAction = new dfs.actions.basic.KingClaimAction(
    to,
    amount,
    merkleRoot,
    merkleProof
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param to Address where to send the KING token
    /// @param amount Amount of KING token to claim
    /// @param merkleRoot Merkle root of the claim
    /// @param merkleProof Merkle proof of the claim
    struct Params {
        address to;
        uint256 amount;
        bytes32 merkleRoot;
        bytes32[] merkleProof;
    }
```

### Return Value

```solidity
return bytes32(inputData.amount);
```

### Events and Logs

```solidity
```


# MerklClaim

### Description

Claims Merkl rewards

> **Notes**
>
> You can claim Merkl rewards for anyone, but distinctTokens array should be empty in that case

### Action ID

`0x79b06cc7`

### SDK Action

```ts
const merklClaimAction = new dfs.actions.merkl.MerklClaimAction(
    users,
    tokens,
    amounts,
    proofs,
    distinctTokens,
    amountsClaimedPerDistinctToken,
    to
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param users Array of addresses who received the reward (from API)
    /// @param tokens The addresses of the tokens that we are claiming the reward in (from API)
    /// @param amounts Amounts to claim (from API)
    /// @param proofs Merkle proofs (from API)
    /// @param distinctTokens Distinct token addresses from tokens array if we want tokens to be sent from smart wallet
    /// @param amountsClaimedPerDistinctToken Amount of tokens to send from smart wallet, amount should match token address at same index in distinctTokens
    /// @param to The address to which the tokens claimed by smart wallet will be sent to
    struct Params {
        address[] users;
        address[] tokens;
        uint256[] amounts;
        bytes32[][] proofs;
        address[] distinctTokens;
        uint256[] amountsClaimedPerDistinctToken;
        address to;
    }
```

### Return Value

```solidity
return bytes32(0);
```

### Events and Logs

```solidity
emit ActionEvent("MerklClaim", logData);
logger.logActionDirectEvent("MerklClaim", logData);
bytes memory logData = abi.encode(params);
```


# PermitToken

### Description

Helper action to invoke a permit action signed by a user

> **Notes**
>
> Every successful call to permit increases owners nonce by one.

### Action ID

`0x25a4d738`

### SDK Action

```ts
const permitTokenAction = new dfs.actions.basic.PermitTokenAction(
    token,
    owner,
    spender,
    value,
    deadline,
    v,
    r,
    s
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param tokenAddr Address of the token to permit
    /// @param owner Address of the owner
    /// @param spender Address of the spender
    /// @param value Amount of tokens to permit
    /// @param deadline Deadline of the permit
    /// @param v ECDSA signature v
    /// @param r ECDSA signature r
    /// @param s ECDSA signature s
    struct Params {
        address tokenAddr;
        address owner;
        address spender;
        uint256 value;
        uint256 deadline;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }
```

### Return Value

```solidity
return bytes32(inputData.value);
```

### Events and Logs

```solidity
```


# PullToken

### Description

Helper action to pull a token from the specified address

> **Notes**
>
> Pulls a token from the specified addr, doesn't work with raw ETH. If amount is type(uint).max it will send whole user's wallet balance.

### Action ID

`0xcc063de4`

### SDK Action

```ts
const pullTokenAction = new dfs.actions.basic.PullTokenAction(
    token,
    from,
    amount
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param tokenAddr Address of the token to pull
    /// @param from Address of the sender
    /// @param amount Amount of tokens to pull
    struct Params {
        address tokenAddr;
        address from;
        uint256 amount;
    }
```

### Return Value

```solidity
return bytes32(inputData.amount);
```

### Events and Logs

```solidity
```


# RemoveTokenApproval

### Description

Helper action to remove token approval given to a spender

> **Notes**
>
> Remove approval for spender to pull tokens from user wallet

### Action ID

`0x64514212`

### SDK Action

```ts
const removeTokenApprovalAction = new dfs.actions.basic.RemoveTokenApprovalAction(
    token,
    spender
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param tokenAddr Address of the token to remove approval from
    /// @param spender Address of the spender
    struct Params {
        address tokenAddr;
        address spender;
    }
```

### Return Value

```solidity
return bytes32(0);
```

### Events and Logs

```solidity
emit ActionEvent("RemoveTokenApproval", logData);
logger.logActionDirectEvent("RemoveTokenApproval", logData);
bytes memory logData = abi.encode(params);
```


# SDaiWrap

### Description

Action that deposits dai into sDai.

### Action ID

`0xf7fc13f2`

### SDK Action

```ts
const sDaiWrapAction = new dfs.actions.basic.SDaiWrapAction(
    amount,
    from,
    to
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount - Amount of dai to deposit
    /// @param from - Address from which the tokens will be pulled
    /// @param to - Address that will receive the sDai
    struct Params {
        uint256 amount;
        address from;
        address to;
    }
```

### Return Value

```solidity
return bytes32(shares);
```

### Events and Logs

```solidity
emit ActionEvent("SDaiWrap", logData);
logger.logActionDirectEvent("SDaiWrap", logData);
bytes memory logData = abi.encode(params);
```


# SDaiUnwrap

### Description

Action that redeems sDai for dai.

### Action ID

`0xeafe8383`

### SDK Action

```ts
const sDaiUnwrapAction = new dfs.actions.basic.SDaiUnwrapAction(
    amount,
    from,
    to
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount - Amount of sDai to redeem
    /// @param from - Address from which the tokens will be pulled
    /// @param to - Address that will receive the dai
    struct Params {
        uint256 amount;
        address from;
        address to;
    }
```

### Return Value

```solidity
return bytes32(daiAmount);
```

### Events and Logs

```solidity
emit ActionEvent("SDaiUnwrap", logData);
logger.logActionDirectEvent("SDaiUnwrap", logData);
bytes memory logData = abi.encode(params);
```


# SendToken

### Description

Helper action to send a token to the specified address

> **Notes**
>
> Sends a token to the specified addr, works with Eth also If amount is type(uint).max it will send whole user's wallet balance

### Action ID

`0x02abc227`

### SDK Action

```ts
const sendTokenAction = new dfs.actions.basic.SendTokenAction(
    token,
    to,
    amount
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param tokenAddr Address of the token to send
    /// @param to Address of the recipient
    /// @param amount Amount of tokens to send
    struct Params {
        address tokenAddr;
        address to;
        uint256 amount;
    }
```

### Return Value

```solidity
return bytes32(inputData.amount);
```

### Events and Logs

```solidity
```


# SendTokenAndUnwrap

### Description

Helper action to send a token to the specified address and unwrap if weth address

> **Notes**
>
> Sends a token to the specified addr, works with Eth also. If amount is type(uint).max it will send whole users' wallet balance. If weth address is set it will unwrap by default.

### Action ID

`0x17782156`

### SDK Action

```ts
const sendTokenAndUnwrapAction = new dfs.actions.basic.SendTokenAndUnwrapAction(
    token,
    to,
    amount
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param tokenAddr Address of the token to send
    /// @param to Address of the recipient
    /// @param amount Amount of tokens to send
    struct Params {
        address tokenAddr;
        address to;
        uint256 amount;
    }
```

### Return Value

```solidity
return bytes32(inputData.amount);
```

### Events and Logs

```solidity
```


# SendTokensAndUnwrap

### Description

Helper action to send tokens to the specified addresses and unwrap for weth address

> **Notes**
>
> Sends tokens to the specified addresses, works with Eth also If token is weth address, it will unwrap by default If amount is type(uint).max it will send whole users' wallet balance

### Action ID

`0x13bc5bc1`

### SDK Action

```ts
const sendTokensAndUnwrapAction = new dfs.actions.basic.SendTokensAndUnwrapAction(
    tokens,
    receivers,
    amounts
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param tokens list of tokens to send
    /// @param receivers list of addresses that will receive corresponding tokens
    /// @param amounts list of amounts of corresponding tokens that will be sent
    struct Params {
        address[] tokens;
        address[] receivers;
        uint256[] amounts;
    }
```

### Return Value

```solidity
return bytes32(0);
```

### Events and Logs

```solidity
```


# SendTokens

### Description

Helper action to send tokens to the specified addresses

> **Notes**
>
> Sends tokens to the specified addresses, works with Eth also

### Action ID

`0xa87d9d0e`

### SDK Action

```ts
const sendTokensAction = new dfs.actions.basic.SendTokensAction(
    tokens,
    receivers,
    amounts
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param tokens list of tokens to send
    /// @param receivers list of addresses that will receive corresponding tokens
    /// @param amounts list of amounts of corresponding tokens that will be sent
    struct Params {
        address[] tokens;
        address[] receivers;
        uint256[] amounts;
    }
```

### Return Value

```solidity
return bytes32(0);
```

### Events and Logs

```solidity
```


# StarknetClaim

### Description

Action that helps Smart wallets claim Starknet tokens

### Action ID

`0x33e5cbf6`

### SDK Action

```ts
const starknetClaimAction = new dfs.actions.basic.StarknetClaimAction(
    payload,
    gasPrice
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param payload Array of payloads
    /// @param gasPrice Gas price
    struct Params {
        uint256[] payload;
        uint256 gasPrice;
    }
```

### Return Value

```solidity
```

### Events and Logs

```solidity
```


# SubInputs

### Description

Helper action to subtract 2 inputs/return values

### Action ID

`0x2f36fd35`

### SDK Action

```ts
const subInputsAction = new dfs.actions.basic.SubInputsAction(
    a,
    b
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param a First input
    /// @param b Second input
    struct Params {
        uint256 a;
        uint256 b;
    }
```

### Return Value

```solidity
return bytes32(a - b)
```

### Events and Logs

```solidity
```


# SumInputs

### Description

Helper action to sum up 2 inputs/return values

### Action ID

`0xb49404ac`

### SDK Action

```ts
const sumInputsAction = new dfs.actions.basic.SumInputsAction(
    a,
    b
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param a First input
    /// @param b Second input
    struct Params {
        uint256 a;
        uint256 b;
    }
```

### Return Value

```solidity
return bytes32(a + b)
```

### Events and Logs

```solidity
```


# ToggleSub

### Description

ToggleSub - Sets the state of the sub to active or deactivated

> **Notes**
>
> User can only disable/enable his own subscriptions. This gives permission to dsproxy or safe to our auth contract to be able to execute the strategy

### Action ID

`0xd6499530`

### SDK Action

```ts
const toggleSubAction = new dfs.actions.basic.ToggleSubAction(
    subId,
    active
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param subId ID of the subscription to toggle
    /// @param active Whether to activate or deactivate the subscription
    struct Params {
        uint256 subId;
        bool active;
    }
```

### Return Value

```solidity
return (bytes32(inputData.subId));
```

### Events and Logs

```solidity
```


# TokenBalance

### Description

TokenBalance - Returns the balance of a token for a given address.

### Action ID

`0x019d9978`

### SDK Action

```ts
const tokenBalanceAction = new dfs.actions.basic.TokenBalanceAction(
    tokenAddr,
    holderAddr
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param tokenAddr Address of the token
    /// @param holderAddr Address of the holder
    struct Params {
        address tokenAddr;
        address holderAddr;
    }
```

### Return Value

```solidity
return bytes32(inputData.tokenAddr.getBalance(inputData.holderAddr));
```

### Events and Logs

```solidity
```


# TokenizedVaultAdapter

### Description

No description available

> **Notes**
>
> Action that handles ERC4626 vault operations

### Action ID

`0x3e46d5ba`

### SDK Actions

All actions are mapped to the same contract.

```ts
const tokenizedVaultAdapterDepositAction = new dfs.actions.basic.TokenizedVaultAdapterDepositAction(
    amount,
    minOut,
    vaultAddress,
    from,
    to,
    underlyingAssetAddress
);

const tokenizedVaultAdapterMintAction = new dfs.actions.basic.TokenizedVaultAdapterMintAction(
    amount,
    maxIn,
    vaultAddress,
    from,
    to,
    underlyingAssetAddress
);

const tokenizedVaultAdapterRedeemAction = new dfs.actions.basic.TokenizedVaultAdapterRedeemAction(
    amount,
    minOut,
    vaultAddress,
    from,
    to
);

const tokenizedVaultAdapterWithdrawAction = new dfs.actions.basic.TokenizedVaultAdapterWithdrawAction(
    amount,
    maxIn,
    vaultAddress,
    from,
    to
);

```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount - For DEPOSIT and REDEEM represents exact input token amount, otherwise represents exact output
    /// @param minOutOrMaxIn - For DEPOSIT and REDEEM represents min output token amount, otherwise represents max input
    /// @param vaultAddress - Address of the ERC4626 vault
    /// @param from - Address from which to pull the input token
    /// @param to - Asset that will receive the output token
    /// @param operationId - Enum id that represents the selected operation (DEPOSIT, MINT, WITHDRAW, REDEEM)
    struct Params {
        uint256 amount;
        uint256 minOutOrMaxIn;
        address vaultAddress;
        address from;
        address to;
        OperationId operationId;
    }
```

### Return Value

```solidity
return bytes32(returnAmount);
```

### Events and Logs

```solidity
emit ActionEvent("TokenizedVaultAdapter", logData);
logger.logActionDirectEvent("TokenizedVaultAdapter", logData);
bytes memory logData = abi.encode(params);
```


# TransferNFT

### Description

Helper action to transfer a NFT token to the specified address.

> **Notes**
>
> The user's wallet must have approve if \_from != user's wallet.

### Action ID

`0xa3443678`

### SDK Action

```ts
const transferNFTAction = new dfs.actions.basic.TransferNFTAction(
    nftAddr,
    from,
    to,
    nftId
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param nftAddr Address of the NFT contract
    /// @param from Address of the sender
    /// @param to Address of the recipient
    /// @param nftId ID of the NFT to transfer
    struct Params {
        address nftAddr;
        address from;
        address to;
        uint256 nftId;
    }
```

### Return Value

```solidity
return bytes32(inputData.nftId);
```

### Events and Logs

```solidity
```


# UnwrapEth

### Description

Helper action to un-wrap WETH9 to Eth

> **Notes**
>
> Unwraps WETH9 -> Eth. If to == user's wallet, it will stay on user's wallet.

### Action ID

`0x929145d0`

### SDK Action

```ts
const unwrapEthAction = new dfs.actions.basic.UnwrapEthAction(
    amount,
    to
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount Amount of Weth to unwrap
    /// @param to Address where to send the unwrapped Eth
    struct Params {
        uint256 amount;
        address to;
    }
```

### Return Value

```solidity
return bytes32(_unwrapEth(inputData.amount, inputData.to));
```

### Events and Logs

```solidity
```


# UpdateSub

### Description

Updates users sub information on SubStorage contract

> **Notes**
>
> User can only change his own subscriptions

### Action ID

`0xa985d903`

### SDK Action

```ts
const updateSubAction = new dfs.actions.basic.UpdateSubAction(
    subId,
    sub
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param subId Id of the Subscription
    /// @param sub Object that represents the updated sub
    struct Params {
        uint256 subId;
        StrategyModel.StrategySub sub;
    }
```

### Return Value

```solidity
return (bytes32(inputData.subId));
```

### Events and Logs

```solidity
```


# WrapEth

### Description

Helper action to wrap Ether to WETH9

> **Notes**
>
> Wraps native Eth to WETH9 token. If amount is type(uint256).max wraps whole balance.

### Action ID

`0x11135183`

### SDK Action

```ts
const wrapEthAction = new dfs.actions.basic.WrapEthAction(
    amount,
    includeEthInTx
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount Amount of ether to wrap
    struct Params {
        uint256 amount;
    }
```

### Return Value

```solidity
return bytes32(_wrapEth(inputData.amount));
```

### Events and Logs

```solidity
```


# Checkers

These actions are used in strategies as a way to ensure that the aftermath of strategy execution is what the user subscribed to.


# AaveV2RatioCheck

### Description

Action to check the ratio of the Aave V2 position after strategy execution.

> **Notes**
>
> 5% offset acceptable.

### Action ID

`0xe2833393`

### SDK Action

```ts
const aaveV2RatioCheckAction = new dfs.actions.checkers.AaveV2RatioCheckAction(
    ratioState,
    targetRatio
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    struct Params {
        RatioState ratioState;
        uint256 targetRatio;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV2RatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# AaveV3OpenRatioCheck

### Description

Action to check the ratio of the Aave V3 position after strategy execution.

> **Notes**
>
> This action only checks for current ratio, without comparing it to the start ratio
>
> 5% offset acceptable
>
> We are checking for 5% RATIO\_OFFSET only when the target ratio is < 999%
>
> If `targetRatio` is 999% or more then skip `RATIO_OFFSET` check because it is very hard to be precise under 5%.

### Action ID

`0x72b17abf`

### SDK Action

```ts
const aaveV3OpenRatioCheckAction = new dfs.actions.checkers.AaveV3OpenRatioCheckAction(
    targetRatio,
    market,
    user
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param targetRatio Target ratio.
    /// @param market Market address.
    /// @param user EOA or Smart Wallet address parameter that was added later in order to add support for EOA strategies.
    struct Params {
        uint256 targetRatio;
        address market;
        address user;
    }

```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV3OpenRatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# AaveV3RatioCheck

### Description

Action to check the ratio of the Aave V3 position after strategy execution.

> **Notes**
>
> 5% offset acceptable

### Action ID

`0x71ef3d4e`

### SDK Action

```ts
const aaveV3RatioCheckAction = new dfs.actions.checkers.AaveV3RatioCheckAction(
    ratioState,
    targetRatio
    market,
    user
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    /// @param market Aave V3 Market parameter that was added later in order to add support for different markets in strategies
    /// @param user EOA or Smart Wallet address parameter that was added later in order to add support for EOA strategies
    struct Params {
        RatioState ratioState;
        uint256 targetRatio;
        address market;
        address user;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV3RatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# AaveV4RatioCheck

### Description

Action to check the ratio of the Aave V4 position after strategy execution.

> **Notes**
>
> 5% offset acceptable

### Action ID

`0x707ad6fd`

### SDK Action

```ts
const aaveV4RatioCheckAction = new dfs.actions.AaveV4RatioCheckAction(
    ratioState,
    targetRatio,
    spoke,
    user
);

```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    /// @param spoke Aave V4 spoke address.
    /// @param user User address.
    struct Params {
        RatioState ratioState;
        uint256 targetRatio;
        address spoke;
        address user;
    }
```

### Return Value

```solidity
return bytes32(current);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV4RatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# CompV2RatioCheck

### Description

Action to check the ratio of the Compound V2 position after strategy execution.

> **Notes**
>
> 5% offset acceptable

### Action ID

`0x950d2607`

### SDK Action

```ts
const compV2RatioCheckAction = new dfs.actions.checkers.CompoundV2RatioCheckAction(
    ratioState,
    targetRatio
);

```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    struct Params {
        RatioState ratioState;
        uint256 targetRatio;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("CompV2RatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# CompV3RatioCheck

### Description

Action to check the ratio of the Compound V3 position after strategy execution.

> **Notes**
>
> 5% offset acceptable

### Action ID

`0x2ce67bb9`

### SDK Action

```ts
const compV3RatioCheckAction = new dfs.actions.checkers.CompoundV3RatioCheckAction(
    ratioState,
    targetRatio,
    market,
    user
);

```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    /// @param market Market address.
    /// @param user User address.
    struct Params {
        RatioState ratioState;
        uint256 targetRatio;
        address market;
        address user;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("CompV3RatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# CurveUsdCollRatioCheck

### Description

Action to check the collateral ratio of the Curve USD position after strategy execution.

> **Notes**
>
> 5% offset acceptable

### Action ID

`0xe677476f`

### SDK Action

```ts
const curveUsdCollRatioCheckAction = new dfs.actions.checkers.CurveUsdCollRatioCheck(
    ratioState,
    targetRatio,
    controllerAddr
);

```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    /// @param controllerAddress CurveUsd Controller address.
    struct Params {
        RatioState ratioState;
        uint256 targetRatio;
        address controllerAddress;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("CurveUsdCollRatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# FluidRatioCheck

### Description

Action to check the ratio of the Fluid position after strategy execution.

> **Notes**
>
> 5% offset acceptable

### Action ID

`0xac7a850e`

### SDK Action

```ts
const fluidRatioCheckAction = new dfs.actions.checkers.FluidRatioCheckAction(
    nftId,
    ratioState,
    targetRatio
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param nftId NFT ID representing the position.
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    struct Params {
        uint256 nftId;
        RatioState ratioState;
        uint256 targetRatio;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("FluidRatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# LiquityRatioCheck

### Description

Action to check the ratio of the Liquity position after strategy execution.

### Action ID

`0xafe610df`

### SDK Action

```ts
const liquityRatioCheckAction = new dfs.actions.checkers.LiquityRatioCheckAction(
    ratioState,
    targetRatio
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    struct Params {
        RatioState ratioState;
        uint256 targetRatio;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("LiquityRatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# LiquityRatioIncreaseCheck

### Description

Action to check if ratio of the Liquity position after strategy execution is greater than the target ratio.

> **Notes**
>
> 5% offset acceptable

### Action ID

`0x3b29b437`

### SDK Action

```ts
const liquityRatioIncreaseCheckAction = new dfs.actions.checkers.LiquityRatioIncreaseCheckAction(
    targetRatioIncrease
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param targetRatioIncrease Target ratio increase.
    struct Params {
        uint256 targetRatioIncrease;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("LiquityRatioIncreaseCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# LiquityV2RatioCheck

### Description

Action to check the ratio of the Liquity V2 position after strategy execution.

> **Notes**
>
> 5% offset acceptable

### Action ID

`0x81a0dbab`

### SDK Action

```ts
const liquityV2RatioCheckAction = new dfs.actions.checkers.LiquityV2RatioCheckAction(
    market,
    troveId,
    ratioState,
    targetRatio
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param market Market address.
    /// @param troveId Trove ID.
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    struct Params {
        address market;
        uint256 troveId;
        RatioState ratioState;
        uint256 targetRatio;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("LiquityV2RatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# LiquityV2TargetRatioCheck

### Description

Action to check the ratio of the LiquityV2 position after strategy execution.

> **Notes**
>
> This action only checks for current ratio, without comparing it to the start ratio. 5% offset acceptable

### Action ID

`0xd54d1b42`

### SDK Action

```ts
const liquityV2TargetRatioCheckAction = new dfs.actions.checkers.LiquityV2TargetRatioCheckAction(
    market,
    troveId,
    targetRatio
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param market Market address.
    /// @param troveId Trove ID.
    /// @param targetRatio Target ratio.
    struct Params {
        address market;
        uint256 troveId;
        uint256 targetRatio;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("LiquityV2TargetRatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# McdRatioCheck

### Description

Action to check the ratio of the Maker position after strategy execution.

> **Notes**
>
> 2% offset acceptable

### Action ID

`0x39274269`

### SDK Action

```ts
const mcdRatioCheckAction = new dfs.actions.McdRatioCheckAction(
    ...args
);

```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param ratioState State of the ratio (SHOULD_BE_LOWER or SHOULD_BE_HIGHER)
    /// @param checkTarget Whether to check if the ratio is in the target range.
    /// @param ratioTarget Target ratio.
    /// @param vaultId Vault ID.
    /// @param startRatioIndex Index in returnValues where ratio before actions is stored
    struct Params {
        RatioState ratioState;
        bool checkTarget;
        uint256 ratioTarget;
        uint256 vaultId;
        uint256 startRatioIndex;
    }
```

### Return Value

```solidity
return bytes32(inputData.ratioTarget);
```

### Events and Logs

```solidity
emit ActionEvent("McdRatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# MorphoAaveV2RatioCheck

### Description

Action to check the ratio of the Morpho Aave V2 position after strategy execution.

> **Notes**
>
> 5% offset acceptable

### Action ID

`0xb22cb584`

### SDK Action

```ts
const morphoAaveV2RatioCheckAction = new dfs.actions.checkers.MorphoAaveV2RatioCheckAction(
    ratioState,
    targetRatio,
    user
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    /// @param user User address.
    struct Params {
        RatioState ratioState;
        uint256 targetRatio;
        address user;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("MorphoAaveV2RatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# MorphoBlueRatioCheck

### Description

Action to check the ratio of the Morpho Aave V2 position after strategy execution.

> **Notes**
>
> 5% offset acceptable

### Action ID

`0xb22cb584`

### SDK Action

```ts
const morphoAaveV2RatioCheckAction = new dfs.actions.checkers.MorphoAaveV2RatioCheckAction(
    ratioState,
    targetRatio,
    user
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    /// @param user User address.
    struct Params {
        RatioState ratioState;
        uint256 targetRatio;
        address user;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("MorphoAaveV2RatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# MorphoBlueTargetRatioCheck

### Description

Action to check the ratio of the Morpho Blue position after strategy execution.

> **Notes**
>
> This action only checks for current ratio, without comparing it to the start ratio. 5% offset acceptable

### Action ID

`0x49f12f40`

### SDK Action

```ts
const morphoBlueTargetRatioCheckAction = new dfs.actions.checkers.MorphoBlueTargetRatioCheckAction(
    loanToken,
    collateralToken,
    oracle,
    irm,
    lltv,
    user,
    targetRatio
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param marketParams Morpho market parameters
    /// @param user User address that owns the position (EOA or proxy)
    /// @param targetRatio Target ratio
    struct Params {
        MarketParams marketParams;
        address user;
        uint256 targetRatio;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("MorphoBlueTargetRatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# SparkRatioCheck

### Description

Action to check the ratio of the Spark position after strategy execution.

> **Notes**
>
> 5% offset acceptable

### Action ID

`0xaf91e475`

### SDK Action

```ts
const sparkRatioCheckAction = new dfs.actions.checkers.SparkRatioCheckAction(
    ratioState,
    targetRatio
);
```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param ratioState State of the ratio (IN_BOOST or IN_REPAY)
    /// @param targetRatio Target ratio.
    struct Params {
        RatioState ratioState;
        uint256 targetRatio;
    }
```

### Return Value

```solidity
return bytes32(currRatio);
```

### Events and Logs

```solidity
emit ActionEvent("SparkRatioCheck", logData);
bytes memory logData = abi.encode(currRatio);
```


# LiquityV2NewInterestRateChecker

### Description

> **Notes**
>
> Validates that the interest rate of a LiquityV2 trove was correctly adjusted after strategy execution.

### Action ID

`0xc3bbc489`

### SDK Action

```ts
const liquityV2NewInterestRateCheckerAction = new dfs.actions.LiquityV2NewInterestRateCheckerAction(
    market,
    troveId,
    interestRateChange
);

```

### Action Type

`CHECK_ACTION`

### Input Parameters

```solidity
    /// @param oldRate The original interest rate before adjustment
    /// @param newRate The actual interest rate after adjustment
    /// @param market Address of the LiquityV2 market containing the trove
    /// @param troveId ID of the trove to check the interest rate for
    /// @param interestRateChange Expected interest rate change amount (in basis points or wei)
    struct Params {
        address market;
        uint256 troveId;
        uint256 interestRateChange;
    }
```

### Return Value

```solidity
return bytes32(troveData.annualInterestRate)
```

### Events and Logs

```solidity
emit ActionEvent("LiquityV2NewInterestRateChecker", logData);
bytes memory logData = abi.encode(params);
```


# Aave V2


# AaveBorrow

### Description

Borrow a token from an Aave market

> **Notes**
>
> User borrows tokens from the Aave protocol

### Action ID

`0x5faaad42`

### SDK Action

```ts
const aaveBorrowAction = new dfs.actions.aave.AaveBorrowAction(
    market,
    tokenAddr,
    amount,
    rateMode,
    to,
    onBehalf
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param market Aave Market address.
    /// @param tokenAddr Token address.
    /// @param amount Amount of tokens to borrow.
    /// @param rateMode Rate mode.
    /// @param to Address to send the borrowed tokens to.
    /// @param onBehalf Address to send the borrowed tokens on behalf of. Defaults to the user's wallet.
    struct Params {
        address market;
        address tokenAddr;
        uint256 amount;
        uint256 rateMode;
        address to;
        address onBehalf;
    }
```

### Return Value

```solidity
return bytes32(borrowAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AaveBorrow", logData);
logger.logActionDirectEvent("AaveBorrow", logData);
bytes memory logData = abi.encode(params);
```


# AaveCollateralSwitch

### Description

Switch action if user wants to use tokens for collateral on aave market

### Action ID

`0xa8af8b82`

### SDK Action

```ts
const aaveCollateralSwitchAction = new dfs.actions.aave.AaveCollateralSwitchAction(
    market,
    tokens,
    useAsCollateral
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param market Aave Market address.
    /// @param tokens Tokens to switch as collateral.
    /// @param useAsCollateral Whether to use the tokens as collateral.
    struct Params {
        address market;
        address[] tokens;
        bool[] useAsCollateral;
    }
```

### Return Value

```solidity
return bytes32(0);
```

### Events and Logs

```solidity
```


# AavePayback

### Description

Payback a token a user borrowed from an Aave market

> **Notes**
>
> User paybacks tokens to the Aave protocol

### Action ID

`0x9ca7f8d2`

### SDK Action

```ts
const aavePaybackAction = new dfs.actions.aave.AavePaybackAction(
    market,
    tokenAddr,
    amount,
    rateMode,
    from,
    onBehalf
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param market Aave Market address.
    /// @param tokenAddr Token address.
    /// @param amount Amount of tokens to pay back.
    /// @param rateMode Rate mode.
    /// @param from Address to send the payback tokens from.
    /// @param onBehalf Address to send the payback tokens on behalf of. Defaults to the user's wallet.
    struct Params {
        address market;
        address tokenAddr;
        uint256 amount;
        uint256 rateMode;
        address from;
        address onBehalf;
    }
```

### Return Value

```solidity
return bytes32(paybackAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AavePayback", logData);
logger.logActionDirectEvent("AavePayback", logData);
bytes memory logData = abi.encode(params);
```


# AaveSupply

Supply a token to an AaveV2 market.

## AaveSupply

### Description

Supply a token to an Aave market

> **Notes**
>
> User deposits tokens to the Aave protocol. User needs to approve its wallet to pull the \_tokenAddr tokens.

### Action ID

`0xc380343c`

### SDK Action

```ts
const aaveSupplyAction = new dfs.actions.aave.AaveSupplyAction(
    market,
    tokenAddr,
    amount,
    from,
    onBehalf,
    enableAsColl
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param market Aave Market address.
    /// @param tokenAddr Token address.
    /// @param amount Amount of tokens to supply.
    /// @param from Address to send the supply tokens from.
    /// @param onBehalf Address to send the supply tokens on behalf of. Defaults to the user's wallet.
    /// @param enableAsColl Whether to enable the token as collateral.
    struct Params {
        address market;
        address tokenAddr;
        uint256 amount;
        address from;
        address onBehalf;
        bool enableAsColl;
    }
```

### Return Value

```solidity
return bytes32(supplyAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AaveSupply", logData);
logger.logActionDirectEvent("AaveSupply", logData);
bytes memory logData = abi.encode(params);
```


# AaveWithdraw

Withdraw a token from an AaveV2 market.

### Description

Withdraw a token from an Aave market

> **Notes**
>
> User withdraws tokens from the Aave protocol

### Action ID

`0x4a76aaa3`

### SDK Action

```ts
const aaveWithdrawAction = new dfs.actions.aave.AaveWithdrawAction(
    market,
    tokenAddr,
    amount,
    to
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param market Aave Market address.
    /// @param tokenAddr Token address.
    /// @param amount Amount of tokens to withdraw.
    /// @param to Address to send the withdrawn tokens to.
    struct Params {
        address market;
        address tokenAddr;
        uint256 amount;
        address to;
    }
```

### Return Value

```solidity
return bytes32(withdrawnAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AaveWithdraw", logData);
logger.logActionDirectEvent("AaveWithdraw", logData);
bytes memory logData = abi.encode(params);
```


# AaveClaimAave

### Description

Action to claim AAVE rewards from stkAave token

> **Notes**
>
> Claims AAVE reward from stkAave token

### Action ID

`0x22ed53c6`

### SDK Action

```ts
const aaveClaimAAVEAction = new dfs.actions.aave.AaveClaimAAVEAction(
    amount,
    to
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount Amount of AAVE token to claim (uintMax is supported)
    /// @param to Address that will be receiving the rewards
    struct Params {
        uint256 amount;
        address to;
    }
```

### Return Value

```solidity
return bytes32(claimedAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AaveClaimAAVE", logData);
logger.logActionDirectEvent("AaveClaimAAVE", logData);
bytes memory logData = abi.encode(params);
```


# AaveClaimStkAave

### Description

Action to claim stkAave rewards

> **Notes**
>
> Claims stkAave rewards on the assets of the lending pool

### Action ID

`0xd93d7e7f`

### SDK Action

```ts
const aaveClaimStkAaveAction = new dfs.actions.aave.AaveClaimStkAaveAction(
    assets,
    amount,
    to
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param assets Assets to claim rewards from.
    /// @param amount Amount of rewards to claim.
    /// @param to Address that will be receiving the rewards.
    struct Params {
        address[] assets;
        uint256 amount;
        address to;
    }
```

### Return Value

```solidity
return bytes32(claimedAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AaveClaimStkAave", logData);
logger.logActionDirectEvent("AaveClaimStkAave", logData);
bytes memory logData = abi.encode(params);
```


# AaveUnstake

### Description

Action to unstake stkAave tokens

### Action ID

`0x887729ae`

### SDK Action

Following actions will map to AaveUnstake contract:

```ts
const aaveFinalizeUnstakeAction = new dfs.actions.aave.AaveFinalizeUnstakeAction(
    amount,
    to
);
const aaveStartUnstakeAction = new dfs.actions.aave.AaveStartUnstakeAction();

```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount amount of stkAave tokens to burn (max.uint to redeem whole balance)
    /// @param to address to receive AAVE tokens
    struct Params {
        uint256 amount;
        address to;
    }
```

### Return Value

```solidity
return bytes32(claimedAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AaveUnstake", logData);
logger.logActionDirectEvent("AaveUnstake", logData);
bytes memory logData = abi.encode(params);
```


# Aave V3


# AaveV3ATokenPayback

### Description

Allows a user to repay with aTokens of the underlying debt asset eg. Pay DAI debt using aDAI tokens. This is a L2 specific action which has `executeActionDirectL2()` where a tightly packed data is sent.

> **Notes**
>
> User needs to approve its wallet to pull aTokens.\
> If amount bigger than the current debt is sent just the max. debt amount will be pulled/paid.

### Action ID

`0x62c722e3`

### SDK Action

```ts
const aaveV3ATokenPaybackAction = new dfs.actions.aaveV3.AaveV3ATokenPaybackAction(
    useDefaultMarket,
    market,
    amount,
    from,
    rateMode,
    aTokenAddr,
    assetId
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount Amount of tokens to be paid back.
    /// @param from Address to send the payback tokens from.
    /// @param rateMode Rate mode.
    /// @param assetId Asset id.
    /// @param useDefaultMarket Whether to use the default market.
    /// @param market Aave Market address.
    struct Params {
        uint256 amount;
        address from;
        uint8 rateMode;
        uint16 assetId;
        bool useDefaultMarket;
        address market;
    }
```

### Return Value

```solidity
return bytes32(paybackAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV3ATokenPayback", logData);
logger.logActionDirectEvent("AaveV3ATokenPayback", logData);
bytes memory logData = abi.encode(params);
```


# AaveV3Borrow

### Description

Borrow a token from AaveV3 market

> **Notes**
>
> User borrows tokens from the Aave protocol

### Action ID

`0x9e9290b1`

### SDK Action

```ts
const aaveV3BorrowAction = new dfs.actions.aaveV3.AaveV3BorrowAction(
    useDefaultMarket,
    market,
    amount,
    to,
    rateMode,
    assetId,
    useOnBehalf,
    onBehalf
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount Amount of tokens to borrow.
    /// @param to Address to send the borrowed tokens to.
    /// @param rateMode Rate mode.
    /// @param assetId Asset id.
    /// @param useDefaultMarket Whether to use the default market.
    /// @param useOnBehalf Whether to use on behalf.
    /// @param market Aave Market address.
    /// @param onBehalf Address to send the borrowed tokens on behalf of. Defaults to the user's wallet.
    struct Params {
        uint256 amount;
        address to;
        uint8 rateMode;
        uint16 assetId;
        bool useDefaultMarket;
        bool useOnBehalf;
        address market;
        address onBehalf;
    }
```

### Return Value

```solidity
return bytes32(borrowAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV3Borrow", logData);
logger.logActionDirectEvent("AaveV3Borrow", logData);
bytes memory logData = abi.encode(params);
```


# AaveV3ClaimRewards

### Description

Claims single reward type specified by reward for the list of assets. Rewards are received by to address.

### Action ID

`0x3c4556e9`

### SDK Action

```ts
const aaveV3ClaimRewardsAction = new dfs.actions.aaveV3.AaveV3ClaimRewardsAction(
    assetsLength,
    amount,
    to,
    reward,
    assets
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param assetsLength Length of assets.
    /// @param amount Amount of rewards to claim.
    /// @param to Address that will be receiving the rewards.
    /// @param reward Reward address.
    /// @param assets Assets to claim rewards from.
    struct Params {
        uint8 assetsLength;
        uint256 amount;
        address to;
        address reward;
        address[] assets;
    }
```

### Return Value

```solidity
return bytes32(amountReceived);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV3ClaimRewards", logData);
logger.logActionDirectEvent("AaveV3ClaimRewards", logData);
bytes memory logData = abi.encode(params);
```


# AaveV3CollateralSwitch

### Description

Switch action if user wants to use tokens for collateral on aaveV3 market

### Action ID

`0xbffa4e35`

### SDK Action

```ts
const aaveV3CollateralSwitchAction = new dfs.actions.aaveV3.AaveV3CollateralSwitchAction(
    useDefaultMarket,
    market,
    arrayLength,
    assetIds,
    useAsCollateral
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param arrayLength Length of the array.
    /// @param useDefaultMarket Whether to use the default market.
    /// @param assetIds Asset ids.
    /// @param useAsCollateral Whether to use the tokens as collateral.
    /// @param market Aave Market address.
    struct Params {
        uint8 arrayLength;
        bool useDefaultMarket;
        uint16[] assetIds;
        bool[] useAsCollateral;
        address market;
    }
```

### Return Value

```solidity
return bytes32(0);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV3CollateralSwitch", logData);
logger.logActionDirectEvent("AaveV3CollateralSwitch", logData);
bytes memory logData = abi.encode(params);
```


# AaveV3Payback

### Description

Payback a token a user borrowed from an AaveV3 market

> **Notes**
>
> User paybacks tokens to the Aave protocol.

### Action ID

`0x17683e81`

### SDK Action

```ts
const aaveV3PaybackAction = new dfs.actions.aaveV3.AaveV3PaybackAction(
    useOnDefaultMarket,
    market,
    amount,
    from,
    rateMode,
    tokenAddress,
    assetId,
    useOnBehalf,
    onBehalf
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount Amount of tokens to be paid back.
    /// @param from Address to send the payback tokens from.
    /// @param rateMode Rate mode.
    /// @param assetId Asset id.
    /// @param useDefaultMarket Whether to use the default market.
    /// @param useOnBehalf Whether to use on behalf.
    /// @param market Aave Market address.
    /// @param onBehalf Address to send the payback tokens on behalf of. Defaults to the user's wallet.
    struct Params {
        uint256 amount;
        address from;
        uint8 rateMode;
        uint16 assetId;
        bool useDefaultMarket;
        bool useOnBehalf;
        address market;
        address onBehalf;
    }
```

### Return Value

```solidity
return bytes32(paybackAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV3Payback", logData);
logger.logActionDirectEvent("AaveV3Payback", logData);
bytes memory logData = abi.encode(params);
```


# AaveV3SetEMode

### Description

Set positions eMode on Aave v3

> **Notes**
>
> User sets EMode for Aave position on its wallet

### Action ID

`0x3d35d254`

### SDK Action

```ts
const aaveV3SetEModeAction = new dfs.actions.aaveV3.AaveV3SetEModeAction(
    useOnDefaultMarket,
    market,
    categoryId
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param categoryId eMode category id (0 - 255).
    /// @param useDefaultMarket Whether to use the default market.
    /// @param market Aave Market address.
    struct Params {
        uint8 categoryId;
        bool useDefaultMarket;
        address market;
    }
```

### Return Value

```solidity
return bytes32(categoryId);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV3SetEMode", logData);
logger.logActionDirectEvent("AaveV3SetEMode", logData);
bytes memory logData = abi.encode(params);
```


# AaveV3Withdraw

### Description

Withdraw a token from an Aave market

> **Notes**
>
> User withdraws tokens from the Aave protocol. Send type(uint).max to withdraw whole amount.

### Action ID

`0x72a6498a`

### SDK Action

```ts
const aaveV3WithdrawAction = new dfs.actions.aaveV3.AaveV3WithdrawAction(
    useDefaultMarket,
    market,
    amount,
    to,
    assetId
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param assetId Asset id.
    /// @param useDefaultMarket Whether to use the default market.
    /// @param amount Amount of tokens to withdraw.
    /// @param to Address to send the withdrawn tokens to.
    /// @param market Aave Market address.
    struct Params {
        uint16 assetId;
        bool useDefaultMarket;
        uint256 amount;
        address to;
        address market;
    }
```

### Return Value

```solidity
return bytes32(withdrawnAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV3Withdraw", logData);
logger.logActionDirectEvent("AaveV3Withdraw", logData);
bytes memory logData = abi.encode(params);
```


# AaveV3Supply

### Description

Supply a token to an Aave market

> **Notes**
>
> User deposits tokens to the Aave protocol. User needs to approve its wallet to pull the tokens being supplied

### Action ID

`0xfc33bf00`

### SDK Action

```ts
const aaveV3SupplyAction = new dfs.actions.aaveV3.AaveV3SupplyAction(
    useDefaultMarket,
    market,
    amount,
    from,
    tokenAddress,
    assetId,
    enableAsColl,
    useOnBehalf,
    onBehalf
);
```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount Amount of tokens to supply.
    /// @param from Address to send the supply tokens from.
    /// @param assetId Asset id.
    /// @param enableAsColl Whether to enable as collateral.
    /// @param useDefaultMarket Whether to use the default market.
    /// @param useOnBehalf Whether to use on behalf.
    /// @param market Aave Market address.
    /// @param onBehalf Address to send the supply tokens on behalf of. Defaults to the user's wallet.
    struct Params {
        uint256 amount;
        address from;
        uint16 assetId;
        bool enableAsColl;
        bool useDefaultMarket;
        bool useOnBehalf;
        address market;
        address onBehalf;
    }
```

### Return Value

```solidity
return bytes32(supplyAmount);
```

### Events and Logs

```solidity
emit ActionEvent("AaveV3Supply", logData);
logger.logActionDirectEvent("AaveV3Supply", logData);
bytes memory logData = abi.encode(params);
```


# GhoClaimAave

### Description

Action to claim AAVE rewards from stkGHO token

> **Notes**
>
> Claims AAVE reward from stkGHO token.

### Action ID

`0x17ca00ae`

### SDK Action

```ts
const ghoClaimAAVEAction = new dfs.actions.stkgho.GhoClaimAAVEAction(
    amount,
    to
);

```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount Amount of AAVE token to claim (uintMax is supported)
    /// @param to Address that will be receiving the rewards
    struct Params {
        uint256 amount;
        address to;
    }
```

### Return Value

```solidity
return bytes32(claimedAmount);
```

### Events and Logs

```solidity
emit ActionEvent("GhoClaimAAVE", logData);
logger.logActionDirectEvent("GhoClaimAAVE", logData);
bytes memory logData = abi.encode(params);
```


# GhoUnstake

### Description

Action to unstake stkGHO tokens.

### Action ID

`0xe1c6999c`

### SDK Action

Following actions will map to GhoUnstake contract:

```ts
const ghoFinalizeUnstakeAction = new dfs.actions.stkgho.GhoFinalizeUnstakeAction(
    amount,
    to
);
const ghoStartUnstakeAction = new dfs.actions.stkgho.GhoStartUnstakeAction();

```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param amount amount of stkGHO tokens to burn (max.uint to redeem whole balance, 0 to start cooldown period)
    /// @param to address to receive GHO tokens
    struct Params {
        uint256 amount;
        address to;
    }
```

### Return Value

```solidity
return bytes32(claimedAmount);
```

### Events and Logs

```solidity
emit ActionEvent("GhoUnstake", logData);
logger.logActionDirectEvent("GhoUnstake", logData);
bytes memory logData = abi.encode(params);
```


# UmbrellaStake

### Description

UmbrellaStake - Stake aTokens/underlying or GHO tokens using Umbrella Stake Token

> **Notes**
>
> This action will always pull aTokens or underlying for non GHO staking and wrap them into waTokens for staking. Wraps aTokens into waTokens. Wraps underlying asset into waTokens.

### Action ID

`0x1c4fe1da`

### SDK Action

```ts
const umbrellaStakeAction = new dfs.actions.umbrella.UmbrellaStakeAction(
    stkToken,
    from.address,
    to.address,
    amount,
    useATokens,
    minSharesOut
);

```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param stkToken The umbrella stake token.
    /// @param from The address from which the aToken or GHO will be pulled.
    /// @param to The address to which the stkToken will be transferred
    /// @param amount The amount of aToken/underlying or GHO to be staked.
    /// @param useATokens Whether to use aTokens or underlying for staking (e.g. aUSDC or USDC).
    /// @param minSharesOut The minimum amount of stkToken shares to receive.
    struct Params {
        address stkToken;
        address from;
        address to;
        uint256 amount;
        bool useATokens;
        uint256 minSharesOut;
    }
```

### Return Value

```solidity
return bytes32(stkTokenShares);
```

### Events and Logs

```solidity
emit ActionEvent("UmbrellaStake", logData);
logger.logActionDirectEvent("UmbrellaStake", logData);
bytes memory logData = abi.encode(params);
```


# UmbrellaUnstake

### Description

UmbrellaUnstake - Unstake aTokens/underlying or GHO tokens using Umbrella Stake Token

> **Notes**
>
> This action will always unwrap waTokens to aTokens/underlying after unstaking. Passing zero as amount will start cooldown period.

### Action ID

`0x5ea3fadd`

### SDK Action

Following actions will map to UmbrellaUnstake contract:

```ts
const finalizeUnstakeAction = new sdk.actions.umbrella.UmbrellaFinalizeUnstakeAction(
    stkToken,
    to.address,
    amount,
    useATokens,
    minSharesOut
);
const startUnstakeAction = new sdk.actions.umbrella.UmbrellaStartUnstakeAction(
    stkToken
);

```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param stkToken The umbrella stake token.
    /// @param to The address to which the aToken/underlying or GHO will be transferred
    /// @param stkAmount The amount of stkToken shares to burn (max.uint to redeem whole balance, 0 to start cooldown period)
    /// @param useATokens Whether to unwrap waTokens to aTokens or underlying (e.g. aUSDC or USDC).
    /// @param minAmountOut The minimum amount of aToken/underlying or GHO to be received
    struct Params {
        address stkToken;
        address to;
        uint256 stkAmount;
        bool useATokens;
        uint256 minAmountOut;
    }
```

### Return Value

```solidity
return bytes32(redeemedAmount);
```

### Events and Logs

```solidity
emit ActionEvent("UmbrellaUnstake", logData);
logger.logActionDirectEvent("UmbrellaUnstake", logData);
bytes memory logData = abi.encode(params);
```


# UmbrellaClaimRewards

### Description

UmbrellaClaimRewards - Claim rewards from staking in Umbrella staking system

### Action ID

`0x9160bac0`

### SDK Action

```ts
const umbrellaClaimRewardsAction = new dfs.actions.umbrella.UmbrellaClaimRewardsAction(
    asset,
    to,
    rewards
);

```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param asset The asset to claim rewards from
    /// @param to The address to send the rewards to
    /// @param rewards The rewards to claim
    struct Params {
        address asset;
        address to;
        address[] rewards;
    }
```

### Return Value

```solidity
return bytes32(amounts[0]);
```

### Events and Logs

```solidity
emit ActionEvent("UmbrellaClaimRewards", logData);
logger.logActionDirectEvent("UmbrellaClaimRewards", logData);
bytes memory logData = abi.encode(params);
```


# GhoStake

### Description

Action to stake GHO tokens.

### Action ID

`0x1904bda0`

### SDK Action

```ts
const ghoStakeAction = new dfs.actions.GhoStakeAction(
    from,
    to,
    amount
);

```

### Action Type

`STANDARD_ACTION`

### Input Parameters

```solidity
    /// @param from address to pull the GHO tokens from
    /// @param to address to send the stkGHO tokens to
    /// @param amount amount of GHO tokens to stake
    struct Params {
        address from;
        address to;
        uint256 amount;
    }
```

### Return Value

```solidity
return bytes32(stkTokensReceived);
```

### Events and Logs

```solidity
emit ActionEvent("GhoStake", logData);
logger.logActionDirectEvent("GhoStake", logData);
bytes memory logData = abi.encode(params);
```


# Aave V4




---

[Next Page](/llms-full.txt/1)

