Withdraw process with the Liquidity Handler
Example: A walkthrough of a withdrawal from IbAlluoUSD to USDC
Step 1: User calls withdraw with 100 USDC and withdraw is called from the handler to send funds directly to the user.
/// @notice  Withdraws accuratel
/// @dev When called, immediately check for new interest index. Then find the adjusted amount in IbAlluo tokens
///      Then burn appropriate amount of IbAlluo tokens to receive asset token
/// @param _targetToken Asset token
/// @param _amount Amount (parsed 10**18) in asset value
function withdrawTo(
    address _recipient,
    address _targetToken,
    uint256 _amount
) public {
    updateRatio();
    uint256 adjustedAmount = (_amount * multiplier) / growingRatio;
    _burn(_msgSender(), adjustedAmount);
    ILiquidityHandler handler = ILiquidityHandler(liquidityHandler);
    if (supportedTokens.contains(_targetToken) == false) {
        (address liquidToken,) = ILiquidityHandler(liquidityHandler).getAdapterCoreTokensFromIbAlluo(address(this));
        // This just is used to revert if there is no active route.
        require(IExchange(exchangeAddress).buildRoute(liquidToken, _targetToken).length > 0, "!Supported");
        handler.withdraw(
        _recipient,
        liquidToken,
        _amount,
        _targetToken
        );
    } else {
        handler.withdraw(
        _recipient,
        _targetToken,
        _amount
        );
    }
    emit TransferAssetValue(_msgSender(), address(0), adjustedAmount, _amount, growingRatio);
    emit BurnedForWithdraw(_msgSender(), adjustedAmount);
}Step 2: When withdraw is called for 100 USDC, carry out checks and either withdraw funds directly or add the user to queue.
- If the buffer can be used to meet the withdrawal, simply call withdraw on the adapter to send 100 USDC tokens directly to the user. 
- If there is not enough funds in the buffer OR there is an existing queue of withdrawals, add the user to the queue until there are enough funds to meet it. 
 /** @notice Called by ibAlluo, withdraws tokens from the adapter.
 * @dev Attempt to withdraw. If there are insufficient funds, you are added to the queue.
 ** @param _user Address of depositor
 ** @param _token Address of token (USDC, DAI, USDT...)
 ** @param _amount Amount of tokens in 10**18
 */
function withdraw(
    address _user,
    address _token,
    uint256 _amount
) external whenNotPaused onlyRole(DEFAULT_ADMIN_ROLE) {
    uint256 inAdapter = getAdapterAmount(msg.sender);
    WithdrawalSystem storage withdrawalSystem = ibAlluoToWithdrawalSystems[
        msg.sender
    ];
    if (inAdapter >= _amount && withdrawalSystem.totalWithdrawalAmount == 0) {
        uint256 adapterId = ibAlluoToAdapterId.get(msg.sender);
        address adapter = adapterIdsToAdapterInfo[adapterId].adapterAddress;
        IHandlerAdapter(adapter).withdraw(_user, _token, _amount);
        emit WithdrawalSatisfied(
            msg.sender,
            _user,
            _token,
            _amount,
            0,
            block.timestamp
        );
    } else {
        uint256 lastWithdrawalRequest = withdrawalSystem
            .lastWithdrawalRequest;
        withdrawalSystem.lastWithdrawalRequest++;
        withdrawalSystem.withdrawals[
            lastWithdrawalRequest + 1
        ] = Withdrawal({
            user: _user,
            token: _token,
            amount: _amount,
            time: block.timestamp
        });
        withdrawalSystem.totalWithdrawalAmount += _amount;
        emit AddedToQueue(
            msg.sender,
            _user,
            _token,
            _amount,
            lastWithdrawalRequest + 1,
            block.timestamp
        );
    }
}
Step 2.5: If the user wanted a withdrawal in a token not supported by the IbAlluo (i.e WETH for IbAlluoUSD), carry out the withdrawal and do a swap through the Alluo Exchange before sending to the user
//// Same function as above but overload for case when you want to withdraw in a different token native to an ibAlluo.
// For example: Withdraw USDC from ibAlluoEth.
function withdraw(
    address _user,
    address _token,
    uint256 _amount,
    address _outputToken
) external whenNotPaused onlyRole(DEFAULT_ADMIN_ROLE) {
    uint256 inAdapter = getAdapterAmount(msg.sender);
    WithdrawalSystem storage withdrawalSystem = ibAlluoToWithdrawalSystems[
        msg.sender
    ];
    if (
        inAdapter >= _amount && withdrawalSystem.totalWithdrawalAmount == 0
    ) {
        uint256 adapterId = ibAlluoToAdapterId.get(msg.sender);
        address adapter = adapterIdsToAdapterInfo[adapterId].adapterAddress;
        if (_token != _outputToken) {
            IHandlerAdapter(adapter).withdraw(address(this), _token, _amount);
            _withdrawThroughExchange(_token, _outputToken, _amount, _user);
        } else {
            IHandlerAdapter(adapter).withdraw(_user, _token, _amount);
        }
        emit WithdrawalSatisfied(
            msg.sender,
            _user,
            _token,
            _amount,
            0,
            block.timestamp
        );
    } else {
        require(
            _token == _outputToken,
            "Handler: Only supported tokens"
        );
        uint256 lastWithdrawalRequest = withdrawalSystem
            .lastWithdrawalRequest;
        withdrawalSystem.lastWithdrawalRequest++;
        withdrawalSystem.withdrawals[
            lastWithdrawalRequest + 1
        ] = Withdrawal({
            user: _user,
            token: _token,
            amount: _amount,
            time: block.timestamp
        });
        withdrawalSystem.totalWithdrawalAmount += _amount;
        emit AddedToQueue(
            msg.sender,
            _user,
            _token,
            _amount,
            lastWithdrawalRequest + 1,
            block.timestamp
        );
    }
}
// Here, we are directly swapping tokens and then sending these tokens 
function _withdrawThroughExchange(
    address _inputToken,
    address _targetToken,
    uint256 _amount18,
    address _user
) internal {
    uint256 amountinInputTokens = (_amount18 *
        10**ERC20Upgradeable(_inputToken).decimals()) / 10**18;
    IERC20Upgradeable(_inputToken).safeIncreaseAllowance(
        exchangeAddress,
        amountinInputTokens
    );
    uint256 amountinTargetTokens = IExchange(exchangeAddress).exchange(
        _inputToken,
        _targetToken,
        amountinInputTokens,
        0
    );
    IERC20Upgradeable(_targetToken).safeTransfer(_user, amountinTargetTokens);
}Step 3: When withdraw is called on the adapter, it sends the tokens directly to the user. 
/// @notice When called by liquidity handler, withdraws funds from liquidity pool
/// @dev It checks against arbitragers attempting to exploit spreads in stablecoins. 
/// @param _user Recipient address
/// @param _token Deposit token address (eg. USDC)
/// @param _amount  Amount to be withdrawn in 10*18
function withdraw (address _user, address _token, uint256 _amount ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    
    uint256[3] memory amounts;
    address liquidToken = ICurvePoolUSD(curvePool).underlying_coins(liquidTokenIndex);
    uint256 amount = _amount / 10**(18 - IERC20Metadata(liquidToken).decimals());
    amounts[liquidTokenIndex] = amount;
    
    if(_token == liquidToken){
        ICurvePoolUSD(curvePool).remove_liquidity_imbalance(
            amounts, 
            _amount * (10000 + slippage) / 10000,
            true
        );
        IERC20(_token).safeTransfer(_user, amount);
    }
    else{
        // We want to be save agains arbitragers so at any withraw contract checks 
        // how much will be burned curveLp by withrawing this amount in token with most liquidity
        // and passes this burned amount to get tokens
        uint256 toBurn = ICurvePoolUSD(curvePool).calc_token_amount(amounts, false);
        uint256 minAmountOut = _amount / 10**(18 - IERC20Metadata(_token).decimals());
        uint256 toUser = ICurvePoolUSD(curvePool).remove_liquidity_one_coin(
                toBurn, 
                int128(indexes[_token]), 
                minAmountOut * (10000 - slippage) / 10000,
                true
            );
        IERC20(_token).safeTransfer(_user, toUser);
    }
}
    Last updated
