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. Tokens & Tokenomics
  2. Tech deep dive: Interest Bearing {asset} token
  3. Liquidity Handler and adapters

Deposit process with the Liquidity Handler

Example: A walkthrough of a USDC deposit into IbAlluoUSD

Step 1: User calls deposit with 100 USDC and the funds are sent to the voteExecutor and IbAlluo calls the deposit function.

function deposit(address _token, uint256 _amount) external {
    if (supportedTokens.contains(_token) == false) {
        IERC20Upgradeable(_token).safeTransferFrom(_msgSender(), address(this), _amount);
        (, address primaryToken) = ILiquidityHandler(liquidityHandler).getAdapterCoreTokensFromIbAlluo(address(this));
        IERC20Upgradeable(_token).safeIncreaseAllowance(exchangeAddress, _amount);
        _amount = IExchange(exchangeAddress).exchange(_token, primaryToken, _amount, 0);
        _token = primaryToken;
        
        //Funds are sent to the liquidity Handler
        IERC20Upgradeable(primaryToken).safeTransfer(address(liquidityHandler), _amount);
    } else {
        //Funds are sent to the liquidity Handler
        IERC20Upgradeable(_token).safeTransferFrom(_msgSender(),address(liquidityHandler),_amount);
    }
    updateRatio();
    ILiquidityHandler(liquidityHandler).deposit(_token, _amount);
    uint256 amountIn18 = _amount * 10**(18 - AlluoERC20Upgradable(_token).decimals());
    uint256 adjustedAmount = (amountIn18 * multiplier) / growingRatio;
    _mint(_msgSender(), adjustedAmount);
    emit TransferAssetValue(address(0), _msgSender(), adjustedAmount, amountIn18, growingRatio);
    emit Deposited(_msgSender(), _token, _amount);
}

Step 2: Now the 100 USDC are in the liquidityHandler and the deposit function is called, either two of the following occur

  1. If the expectedAdapterAmount (minimum buffer requirement) is not met, the 100 USDC is deposited into the adapter until the buffer is met and any surplus is sent to the treasury.

  2. If the buffer requirement is already met, the funds are sent directly to the treasury.

LiquidityHandler.sol
/** @notice Called by ibAlluo, deposits tokens into the adapter.
 * @dev Deposits funds, checks whether adapter is filled or insufficient, and then acts accordingly.
 ** @param _token Address of token (USDC, DAI, USDT...)
 ** @param _amount Amount of tokens in correct deimals (10**18 for DAI, 10**6 for USDT)
 */
function deposit(address _token, uint256 _amount)
    external
    whenNotPaused
    onlyRole(DEFAULT_ADMIN_ROLE)
{
    uint256 amount18 = _amount *
        10**(18 - ERC20Upgradeable(_token).decimals());

    uint256 inAdapter = getAdapterAmount(msg.sender);
    uint256 expectedAdapterAmount = getExpectedAdapterAmount(
        msg.sender,
        amount18
    );

    uint256 adapterId = ibAlluoToAdapterId.get(msg.sender);
    address adapter = adapterIdsToAdapterInfo[adapterId].adapterAddress;

    IERC20Upgradeable(_token).safeTransfer(adapter, _amount);
    if (inAdapter < expectedAdapterAmount) {
        //
        // Case 1
        //
        if (expectedAdapterAmount < inAdapter + amount18) {
            uint256 toWallet = inAdapter + amount18 - expectedAdapterAmount;
            uint256 leaveInPool = amount18 - toWallet;
            IHandlerAdapter(adapter).deposit(_token, amount18, leaveInPool);
        } else {
            IHandlerAdapter(adapter).deposit(_token, amount18, amount18);
        }
    } else {
        //
        // Case 2
        //
        IHandlerAdapter(adapter).deposit(_token, amount18, 0);
    }

    WithdrawalSystem storage withdrawalSystem = ibAlluoToWithdrawalSystems[
        msg.sender
    ];

    if (
        withdrawalSystem.totalWithdrawalAmount > 0 && !withdrawalSystem.resolverTrigger
    ) {
        uint256 inAdapterAfterDeposit = getAdapterAmount(msg.sender);
        uint256 firstInQueueAmount = withdrawalSystem
            .withdrawals[withdrawalSystem.lastSatisfiedWithdrawal + 1].amount;
        if (firstInQueueAmount <= inAdapterAfterDeposit) {
            withdrawalSystem.resolverTrigger = true;
            emit EnoughToSatisfy(
                msg.sender,
                inAdapterAfterDeposit,
                withdrawalSystem.totalWithdrawalAmount
            );
        }
    }
}

Here is the logic that occurs in the actual adapter. The funds are either kept in the contract as a buffer for withdrawals, or they are sent to the treasury.

USDCurveAdapter.sol
/// @notice When called by liquidity handler, moves some funds to the Gnosis multisig and others into a LP to be kept as a 'buffer'
/// @param _token Deposit token address (eg. USDC)
/// @param _fullAmount Full amount deposited in 10**18 called by liquidity handler
/// @param _leaveInPool  Amount to be left in the LP rather than be sent to the Gnosis wallet (the "buffer" amount)
function deposit(address _token, uint256 _fullAmount, uint256 _leaveInPool) external onlyRole(DEFAULT_ADMIN_ROLE) {
    uint256 toSend = _fullAmount - _leaveInPool;
    address primaryToken = ICurvePoolUSD(curvePool).underlying_coins(primaryTokenIndex);
    if(_token == primaryToken){
        if (toSend != 0) {
            IERC20(primaryToken).safeTransfer(wallet, toSend / 10**(18 - IERC20Metadata(primaryToken).decimals()));
        }
        if (_leaveInPool != 0) {
            uint256[3] memory amounts;
            amounts[primaryTokenIndex] = _leaveInPool / 10**(18 - IERC20Metadata(primaryToken).decimals());
            ICurvePoolUSD(curvePool).add_liquidity(amounts, 0, true);
        }
    }
    else{
        uint256[3] memory amounts;
        amounts[indexes[_token]] = _fullAmount / 10**(18 - IERC20Metadata(_token).decimals());

        uint256 lpAmount = ICurvePoolUSD(curvePool).add_liquidity(amounts, 0, true);
        delete amounts;
        if (toSend != 0) {
            toSend = toSend / 10**(18 - IERC20Metadata(primaryToken).decimals());
            amounts[primaryTokenIndex] = toSend;
            ICurvePoolUSD(curvePool).remove_liquidity_imbalance(
                        amounts, 
                        lpAmount * (10000+slippage)/10000,
                        true);
            IERC20(primaryToken).safeTransfer(wallet, toSend);
        }
    }
} 

Here you can see this logic with the Curve 3pool. In other adapters, funds are just kept as native tokens (ETH, BTC... for IbAlluoBTC, IbAlluoETH) but here the funds are kept as LP tokens. Tokens are converted to a 'primaryToken' with the most liquidity to dissuade arbitrage opportunities upon withdrawals.

Step 3: If this new deposit allows preexisting withdrawal requests to be met, turn on a flag that allows our gelato resolver to execute the pending withdrawal

Inside the deposit function
 WithdrawalSystem storage withdrawalSystem = ibAlluoToWithdrawalSystems[
        msg.sender
    ];

    if (
        withdrawalSystem.totalWithdrawalAmount > 0 && !withdrawalSystem.resolverTrigger
    ) {
        uint256 inAdapterAfterDeposit = getAdapterAmount(msg.sender);
        uint256 firstInQueueAmount = withdrawalSystem
            .withdrawals[withdrawalSystem.lastSatisfiedWithdrawal + 1].amount;
        if (firstInQueueAmount <= inAdapterAfterDeposit) {
            withdrawalSystem.resolverTrigger = true;
            emit EnoughToSatisfy(
                msg.sender,
                inAdapterAfterDeposit,
                withdrawalSystem.totalWithdrawalAmount
            );
        }
    }
PreviousLiquidity Handler and adaptersNextWithdraw process with the Liquidity Handler

Last updated 2 years ago

πŸ‘¨β€πŸ”¬