Alluo Explained
  • Welcome
    • 🧭 The Basics
  • Getting Started
    • πŸ’» The DeFi Web App
      • ❓ Connecting to the app
      • 🚜 Depositing into the farms
      • πŸ™Œ Other basics
      • 🏦 Importing your Mobile app wallet to Metamask
      • 🧬 Add the polygon network manually in Metamask.
      • ⛓️ Bridging Stablecoins from another chain
    • πŸ“±The Mobile app
      • πŸ—οΈ Setting up your account
      • 🏦 Depositing money into the app
      • πŸ™Œ Other basics
      • πŸ” Exporting your private key
    • πŸ“–Tech deep dive: Contract Address Library
  • Understanding Alluo
    • πŸ’΅ How does Alluo get the yield?
      • 🐰 Going deeper into the Alluo protocol rabbit hole
    • 🧐 FAQ
  • Tokens & Tokenomics
    • πŸͺ™ The tokens
    • πŸ‘¨β€πŸ”¬Tech deep dive: Interest Bearing {asset} token
      • Depositing
      • Withdrawals
      • IbAlluo on different chains
      • StIbAlluo and Superfluid
        • A closer look at the integration between IbAlluo and StIbAlluo
      • Using the IbAlluo contract directly to create streams
      • Liquidity Handler and adapters
        • Deposit process with the Liquidity Handler
        • Withdraw process with the Liquidity Handler
    • πŸ“ˆ Tokenomics
    • πŸ‘¨β€πŸ”¬Tech deep dive: Boosting yield by compounding rewards
      • Deposit into the Vault
      • Withdraw from the Vault
      • Redeem rewards
      • Automatic boosting with Alluo
      • FraxConvex Vaults
      • Managing withdrawal requests in IERC4626
  • Decentralisation and Trust
    • πŸ—³οΈ Trustless governance and execution
    • πŸ‘¨β€πŸ”¬Tech deep dive: Vote Executor Architecture
      • Off chain votes to on chain data
      • Onchain data verifcation
      • Automated execution of votes
        • Tokenomics
        • Liquidity Direction
        • Setting APYs on farms
      • Cross chain execution of votes
      • Manually submitting vote results onchain
    • ↔️Alluo Exchange
      • Interacting with the Exchange
    • vlAlluo Architecture
    • Contracts upgrades
    • Investment strategies
      • πŸ“ˆFrax Convex Finance
        • Adding new pools into the strategy
        • Investing into a pool
  • More Advanced Features
    • πŸ” Repeat payments, streaming IbAlluo
  • Product Updates
    • πŸ‘Œ Product Roadmap: Building the right products
    • πŸ’» Web App releases
    • πŸ“± Mobile App releases
    • 🏎️ Alluo Boost
  • tutorial projects
    • Example: USDC to streaming 1 IbAlluo per second
    • Example: Using IbAlluoUSD and Ricochet to do capital efficient DCA into ETH
Powered by GitBook
On this page
  1. Decentralisation and Trust
  2. Tech deep dive: Vote Executor Architecture
  3. Automated execution of votes

Liquidity Direction

Fully on chain execution, with rebalancing of strategies across multiple asset classes

PreviousTokenomicsNextSetting APYs on farms

Last updated 2 years ago

Automated liquidity Direction occurs in 3 steps:

  1. The automated script offchain submits the target percentage for each asset class.

  2. The VoteExecutorMaster and StrategyHandler calculates the current TVL and uses these percentages to determine how much each strategy for every asset class should hold.

  3. The VoteExecutorMaster withdraw and deposits to strategies to meet this percentage.

Step 1: Submission of percentages

An example of this is:

Where 6.7% goes to stETH-ETH and 93.3% goes to ETH-frxETH.

Onchain, you submit the name of the strategy and the percentage where 10000 is 100%.

(”Curve/Convex stETH-ETH”, 670), (”Curve/FraxConvex ETH-frxETH”, 9330)

Would be an example of how it is parsed.

Step 2: Calculation of current TVL

On each strategy, we use oracles to determine the value of each position held in each strategy and sum it up to grab current TVL of the specific asset class. This is done by calling β€œgetDeployedAmount”

CurveConvexStrategyV2
function getDeployedAmountAndRewards(
        bytes calldata data
    ) external onlyRole(DEFAULT_ADMIN_ROLE) returns (uint256) {
        (
            IERC20 lpToken,
            uint256 convexPoolId,
            uint256 assetId
        ) = decodeRewardsParams(data);

        uint256 lpAmount;
        if (convexPoolId != type(uint256).max) {
            ICvxBaseRewardPool rewards = getCvxRewardPool(convexPoolId);
            lpAmount = rewards.balanceOf(address(this));
            rewards.getReward(address(this), true);
        } else {
            lpAmount = lpToken.balanceOf(address(this));
        }

        (uint256 fiatPrice, uint8 fiatDecimals) = IPriceFeedRouterV2(priceFeed)
            .getPriceOfAmount(address(lpToken), lpAmount, assetId);

        return
            IPriceFeedRouterV2(priceFeed).decimalsConverter(
                fiatPrice,
                fiatDecimals,
                18
            );
}

Then, we calculate the positive or negative 'delta' for each strategy.

VoteExecutorMaster
else if (commandIndex == 2) {
        // Handle all withdrawals first and then add all deposit actions to an array to be executed afterwards
        (uint256 directionId, uint256 percent) = abi.decode(
                messages[j].commandData,
                (uint256, uint256)
                );

        (address strategyPrimaryToken, IStrategyHandler.LiquidityDirection memory direction) = handler.getDirectionFullInfoById(directionId);
            if (direction.chainId == currentChain) {
                console.log("directionId", directionId);
                if (percent > 0) {
                    assetIdToDepositPercentages[direction.assetId].push(
                    Deposit(directionId, percent)
                    );
                }

                if (percent == 0) {
                    console.log("full exit");
                    IAlluoStrategyV2(direction.strategyAddress).exitAll(
                        direction.exitData,
                        10000,
                        strategyPrimaryToken,
                        address(this),
                        false,
                        false
                    );
                    handler.removeFromActiveDirections(directionId);
                } else {
                    uint newAmount = (percent *
                    amountsDeployed[direction.assetId]) / 10000;
                    console.log("new", newAmount);
                    console.log("old", direction.latestAmount);
                    if (newAmount < direction.latestAmount) {
                        uint exitPercent = 10000 -
                            (newAmount * 10000) /
                            direction.latestAmount;
                        IAlluoStrategyV2(direction.strategyAddress)
                            .exitAll(
                                        direction.exitData,
                                        exitPercent,
                                        strategyPrimaryToken,
                                        address(this),
                                        false,
                                        false
                                    );
                            } else if (newAmount != direction.latestAmount) {
                                uint depositAmount = newAmount -
                                    direction.latestAmount;
                                assetIdToDepositList[direction.assetId].push(
                                    Deposit(directionId, depositAmount)
                                );
                            }
                        }
                    }

This calculation is done by doing:

Current TVL of asset class * Allocation % = Target value.

Step 3: Withdraw and deposit to each strategy

Withdrawals happen immediately and deposits are queued because there is a chance of reverting in some edge cases. This is executed through executeDeposits() immediately after the vote is executed. For example, say that we need to deposit 100k in each of pools A,B and C. These are added to an array of deposits that are executed by calling executeDeposits(). In pseudocode: ["Deposit 100k in pool A", "Deposit 100k in pool B", "Deposit 100k in pool C"] Once each direction is actioned, they are removed from the array.

IAlluoStrategyV2(direction.strategyAddress).exitAll(
                 direction.exitData,
                 exitPercent,
                 strategyPrimaryToken,
                  address(this),
                  false,
                  false
            );

function _executeDeposits() internal {
        IPriceFeedRouterV2 feed = IPriceFeedRouterV2(priceFeed);
        IStrategyHandler handler = IStrategyHandler(strategyHandler);
        address exchange = exchangeAddress;
        uint8 numberOfAssets = handler.numberOfAssets();
        for (uint256 i; i < numberOfAssets; i++) {
            Deposit[] storage depositList = assetIdToDepositList[i];
            uint depositListLength = depositList.length;
            address strategyPrimaryToken = handler.getPrimaryTokenByAssetId(
                i,
                1
            );
            uint primaryDecimalsMultiplier = 10 **
                (18 -
                    IERC20MetadataUpgradeable(strategyPrimaryToken).decimals());
            while (depositListLength > 0) {
                Deposit memory depositInfo = depositList[depositListLength - 1];
                console.log("dep to directionId", depositInfo.directionId);
                if (depositInfo.directionId != type(uint).max) {
                    IStrategyHandler.LiquidityDirection
                        memory direction = handler.getLiquidityDirectionById(
                            depositInfo.directionId
                        );
                    (uint256 fiatPrice, uint8 fiatDecimals) = feed.getPrice(
                        strategyPrimaryToken,
                        i
                    );
                    uint exactAmount = (depositInfo.amount *
                        10 ** fiatDecimals) / fiatPrice;
                    console.log("exact", exactAmount);
                    uint256 tokenAmount = exactAmount /
                        primaryDecimalsMultiplier;
                    uint256 actualBalance = IERC20MetadataUpgradeable(
                        strategyPrimaryToken
                    ).balanceOf(address(this));
                    if (depositListLength == 1 && actualBalance < tokenAmount) {
                        uint assetAmount = handler.getAssetAmount(i);
                        uint assetMaxSlippageAmount = assetAmount -
                            ((assetAmount * (10000 - slippage)) / 10000);
                        if (
                            tokenAmount - actualBalance <
                            assetMaxSlippageAmount / primaryDecimalsMultiplier
                        ) {
                            tokenAmount = actualBalance;
                            console.log("changed", tokenAmount);
                        } else {
                            revert("slippage");
                        }
                    }
                    if (direction.entryToken != strategyPrimaryToken) {
                        IERC20MetadataUpgradeable(strategyPrimaryToken)
                            .safeApprove(exchange, tokenAmount);
                        tokenAmount = IExchange(exchange).exchange(
                            strategyPrimaryToken,
                            direction.entryToken,
                            tokenAmount,
                            0
                        );
                    }
                    IERC20MetadataUpgradeable(direction.entryToken)
                        .safeTransfer(direction.strategyAddress, tokenAmount);
                    IAlluoStrategyV2(direction.strategyAddress).invest(
                        direction.entryData,
                        tokenAmount
                    );
                    handler.addToActiveDirections(depositInfo.directionId);
                } else {
                    (uint256 fiatPrice, uint8 fiatDecimals) = feed.getPrice(
                        strategyPrimaryToken,
                        i
                    );
                    uint exactAmount = (depositInfo.amount *
                        10 ** fiatDecimals) / fiatPrice;
                    uint256 tokenAmount = exactAmount /
                        primaryDecimalsMultiplier;

                    IERC20MetadataUpgradeable(strategyPrimaryToken)
                        .safeTransfer(gnosis, tokenAmount);
                }
                depositList.pop();
                depositListLength--;
            }
        }
        handler.calculateOnlyLp();
}
πŸ‘¨β€πŸ”¬
https://vote.alluo.com/#/proposal/0x1d2bf1a7dbb043f4d4264f29201aa28157cd2d815cc2914f36c1a84423641e85