ETH Price: $2,526.02 (-1.30%)

Transaction Decoder

Block:
22817182 at Jun-30-2025 12:18:11 PM +UTC
Transaction Fee:
0.00025658962055199 ETH $0.65
Gas Used:
284,339 Gas / 0.90240741 Gwei

Emitted Events:

546 TetherToken.Approval( owner=[Receiver] 0x0226fc899c57d9fe0aeafb08b14ac3acc88b4d2a, spender=ERC1967Proxy, value=0 )
547 0x0226fc899c57d9fe0aeafb08b14ac3acc88b4d2a.0xed7e8f919df9cc0d0ad8b4057d084ebf319b630564d5da283e14751adc931f3a( 0xed7e8f919df9cc0d0ad8b4057d084ebf319b630564d5da283e14751adc931f3a, 0x0000000000000000000000000226fc899c57d9fe0aeafb08b14ac3acc88b4d2a, 0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000044, 095ea7b3000000000000000000000000ca1c673aaaf41b6aa92820aef1fd7745, d8fb77a600000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
548 TetherToken.Approval( owner=[Receiver] 0x0226fc899c57d9fe0aeafb08b14ac3acc88b4d2a, spender=ERC1967Proxy, value=1000000 )
549 0x0226fc899c57d9fe0aeafb08b14ac3acc88b4d2a.0xed7e8f919df9cc0d0ad8b4057d084ebf319b630564d5da283e14751adc931f3a( 0xed7e8f919df9cc0d0ad8b4057d084ebf319b630564d5da283e14751adc931f3a, 0x0000000000000000000000000226fc899c57d9fe0aeafb08b14ac3acc88b4d2a, 0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000044, 095ea7b3000000000000000000000000ca1c673aaaf41b6aa92820aef1fd7745, d8fb77a600000000000000000000000000000000000000000000000000000000, 000f424000000000000000000000000000000000000000000000000000000000 )
550 TetherToken.Transfer( from=[Receiver] 0x0226fc899c57d9fe0aeafb08b14ac3acc88b4d2a, to=ERC1967Proxy, value=1000000 )
551 TetherToken.Approval( owner=ERC1967Proxy, spender=InitializableImmutableAdminUpgradeabilityProxy, value=1000000 )
552 InitializableImmutableAdminUpgradeabilityProxy.0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a( 0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a, 0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0000000000000000000000000000000000000000001645ac178558a661ce9a0d, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000022fb736d2c8abe96a7918d, 000000000000000000000000000000000000000003a54251e6914b99d5899b31, 000000000000000000000000000000000000000003cda448683cdf4132578455 )
553 TetherToken.Transfer( from=ERC1967Proxy, to=InitializableImmutableAdminUpgradeabilityProxy, value=1000000 )
554 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000ca1c673aaaf41b6aa92820aef1fd7745d8fb77a6, 00000000000000000000000000000000000000000000000000000000000f4240 )
555 InitializableImmutableAdminUpgradeabilityProxy.0x458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b86196( 0x458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b86196, 0x000000000000000000000000ca1c673aaaf41b6aa92820aef1fd7745d8fb77a6, 0x000000000000000000000000ca1c673aaaf41b6aa92820aef1fd7745d8fb77a6, 00000000000000000000000000000000000000000000000000000000000f4240, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000003a54251e6914b99d5899b31 )
556 InitializableImmutableAdminUpgradeabilityProxy.0x2b627736bca15cd5381dcf80b0bf11fd197d01a037c52b927a881a10fb73ba61( 0x2b627736bca15cd5381dcf80b0bf11fd197d01a037c52b927a881a10fb73ba61, 0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0x000000000000000000000000ca1c673aaaf41b6aa92820aef1fd7745d8fb77a6, 0x0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000ca1c673aaaf41b6aa92820aef1fd7745d8fb77a6, 00000000000000000000000000000000000000000000000000000000000f4240 )
557 ERC1967Proxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000226fc899c57d9fe0aeafb08b14ac3acc88b4d2a, 00000000000000000000000000000000000000000000000000000000000f423d )
558 ERC1967Proxy.0x90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15( 0x90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15, 0x0000000000000000000000000226fc899c57d9fe0aeafb08b14ac3acc88b4d2a, 00000000000000000000000000000000000000000000000000000000000f4240, 00000000000000000000000000000000000000000000000000000000000f423d )
559 0x0226fc899c57d9fe0aeafb08b14ac3acc88b4d2a.0xed7e8f919df9cc0d0ad8b4057d084ebf319b630564d5da283e14751adc931f3a( 0xed7e8f919df9cc0d0ad8b4057d084ebf319b630564d5da283e14751adc931f3a, 0x0000000000000000000000000226fc899c57d9fe0aeafb08b14ac3acc88b4d2a, 0x000000000000000000000000ca1c673aaaf41b6aa92820aef1fd7745d8fb77a6, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000024, b6b55f2500000000000000000000000000000000000000000000000000000000, 000f424000000000000000000000000000000000000000000000000000000000 )
560 0x0226fc899c57d9fe0aeafb08b14ac3acc88b4d2a.0x280bb3599696acbf79fb8ffcde81a57337b52500f789600fbb1cff9b4cbaba39( 0x280bb3599696acbf79fb8ffcde81a57337b52500f789600fbb1cff9b4cbaba39, 0x0000000000000000000000000000000000000000000000000000000000000004, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000003, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000000140, 0000000000000000000000000000000000000000000000000000000000000220, 000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000000044, 095ea7b3000000000000000000000000ca1c673aaaf41b6aa92820aef1fd7745, d8fb77a600000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000000044, 095ea7b3000000000000000000000000ca1c673aaaf41b6aa92820aef1fd7745, d8fb77a600000000000000000000000000000000000000000000000000000000, 000f424000000000000000000000000000000000000000000000000000000000, 000000000000000000000000ca1c673aaaf41b6aa92820aef1fd7745d8fb77a6, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000000024, b6b55f2500000000000000000000000000000000000000000000000000000000, 000f424000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x0226fc89...cC88B4d2A
0.020641849632258844 Eth
Nonce: 41
0.020385260011706854 Eth
Nonce: 43
0.00025658962055199
0x23878914...A74D4086a
(Titan Builder)
12.680753738648668967 Eth12.680753755724363273 Eth0.000000017075694306
0x87870Bca...50B4fA4E2
(Aave: Pool V3)
0xCa1C673a...5D8FB77A6
0xdAC17F95...13D831ec7

Execution Trace

0x0226fc899c57d9fe0aeafb08b14ac3acc88b4d2a.3f707e6b( )
  • TetherToken.approve( _spender=0xCa1C673aaaf41B6AA92820aeF1FD7745D8FB77A6, _value=0 )
  • TetherToken.approve( _spender=0xCa1C673aaaf41B6AA92820aeF1FD7745D8FB77A6, _value=1000000 )
  • ERC1967Proxy.b6b55f25( )
    • MultiProtocolTokenVault.deposit( _amount=1000000 )
      • TetherToken.allowance( _owner=0x0226fc899C57d9fE0AeAfB08b14AC3AcC88B4d2A, _spender=0xCa1C673aaaf41B6AA92820aeF1FD7745D8FB77A6 ) => ( remaining=1000000 )
      • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
        • AToken.balanceOf( user=0xCa1C673aaaf41B6AA92820aeF1FD7745D8FB77A6 ) => ( 2000001 )
          • InitializableImmutableAdminUpgradeabilityProxy.d15e0053( )
          • TetherToken.transferFrom( _from=0x0226fc899C57d9fE0AeAfB08b14AC3AcC88B4d2A, _to=0xCa1C673aaaf41B6AA92820aeF1FD7745D8FB77A6, _value=1000000 )
          • TetherToken.approve( _spender=0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2, _value=1000000 )
          • InitializableImmutableAdminUpgradeabilityProxy.617ba037( )
            • PoolInstance.supply( asset=0xdAC17F958D2ee523a2206206994597C13D831ec7, amount=1000000, onBehalfOf=0xCa1C673aaaf41B6AA92820aeF1FD7745D8FB77A6, referralCode=0 )
              • SupplyLogic.1913f161( )
                • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
                • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
                • DefaultReserveInterestRateStrategyV2.calculateInterestRates( params=[{name:unbacked, type:uint256, order:1, indexed:false, value:0, valueString:0}, {name:liquidityAdded, type:uint256, order:2, indexed:false, value:1000000, valueString:1000000}, {name:liquidityTaken, type:uint256, order:3, indexed:false, value:0, valueString:0}, {name:totalDebt, type:uint256, order:4, indexed:false, value:3638579587181930, valueString:3638579587181930}, {name:reserveFactor, type:uint256, order:5, indexed:false, value:1000, valueString:1000}, {name:reserve, type:address, order:6, indexed:false, value:0xdAC17F958D2ee523a2206206994597C13D831ec7, valueString:0xdAC17F958D2ee523a2206206994597C13D831ec7}, {name:usingVirtualBalance, type:bool, order:7, indexed:false, value:true, valueString:True}, {name:virtualUnderlyingBalance, type:uint256, order:8, indexed:false, value:1504930508754293, valueString:1504930508754293}] ) => ( 26925385853678831809763853, 42290921096481349772480909 )
                • TetherToken.transferFrom( _from=0xCa1C673aaaf41B6AA92820aeF1FD7745D8FB77A6, _to=0x23878914EFE38d27C4D67Ab83ed1b93A74D4086a, _value=1000000 )
                • InitializableImmutableAdminUpgradeabilityProxy.b3f1c93d( )
                  File 1 of 10: TetherToken
                  pragma solidity ^0.4.17;
                  
                  /**
                   * @title SafeMath
                   * @dev Math operations with safety checks that throw on error
                   */
                  library SafeMath {
                      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                          if (a == 0) {
                              return 0;
                          }
                          uint256 c = a * b;
                          assert(c / a == b);
                          return c;
                      }
                  
                      function div(uint256 a, uint256 b) internal pure returns (uint256) {
                          // assert(b > 0); // Solidity automatically throws when dividing by 0
                          uint256 c = a / b;
                          // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                          return c;
                      }
                  
                      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                          assert(b <= a);
                          return a - b;
                      }
                  
                      function add(uint256 a, uint256 b) internal pure returns (uint256) {
                          uint256 c = a + b;
                          assert(c >= a);
                          return c;
                      }
                  }
                  
                  /**
                   * @title Ownable
                   * @dev The Ownable contract has an owner address, and provides basic authorization control
                   * functions, this simplifies the implementation of "user permissions".
                   */
                  contract Ownable {
                      address public owner;
                  
                      /**
                        * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                        * account.
                        */
                      function Ownable() public {
                          owner = msg.sender;
                      }
                  
                      /**
                        * @dev Throws if called by any account other than the owner.
                        */
                      modifier onlyOwner() {
                          require(msg.sender == owner);
                          _;
                      }
                  
                      /**
                      * @dev Allows the current owner to transfer control of the contract to a newOwner.
                      * @param newOwner The address to transfer ownership to.
                      */
                      function transferOwnership(address newOwner) public onlyOwner {
                          if (newOwner != address(0)) {
                              owner = newOwner;
                          }
                      }
                  
                  }
                  
                  /**
                   * @title ERC20Basic
                   * @dev Simpler version of ERC20 interface
                   * @dev see https://github.com/ethereum/EIPs/issues/20
                   */
                  contract ERC20Basic {
                      uint public _totalSupply;
                      function totalSupply() public constant returns (uint);
                      function balanceOf(address who) public constant returns (uint);
                      function transfer(address to, uint value) public;
                      event Transfer(address indexed from, address indexed to, uint value);
                  }
                  
                  /**
                   * @title ERC20 interface
                   * @dev see https://github.com/ethereum/EIPs/issues/20
                   */
                  contract ERC20 is ERC20Basic {
                      function allowance(address owner, address spender) public constant returns (uint);
                      function transferFrom(address from, address to, uint value) public;
                      function approve(address spender, uint value) public;
                      event Approval(address indexed owner, address indexed spender, uint value);
                  }
                  
                  /**
                   * @title Basic token
                   * @dev Basic version of StandardToken, with no allowances.
                   */
                  contract BasicToken is Ownable, ERC20Basic {
                      using SafeMath for uint;
                  
                      mapping(address => uint) public balances;
                  
                      // additional variables for use if transaction fees ever became necessary
                      uint public basisPointsRate = 0;
                      uint public maximumFee = 0;
                  
                      /**
                      * @dev Fix for the ERC20 short address attack.
                      */
                      modifier onlyPayloadSize(uint size) {
                          require(!(msg.data.length < size + 4));
                          _;
                      }
                  
                      /**
                      * @dev transfer token for a specified address
                      * @param _to The address to transfer to.
                      * @param _value The amount to be transferred.
                      */
                      function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
                          uint fee = (_value.mul(basisPointsRate)).div(10000);
                          if (fee > maximumFee) {
                              fee = maximumFee;
                          }
                          uint sendAmount = _value.sub(fee);
                          balances[msg.sender] = balances[msg.sender].sub(_value);
                          balances[_to] = balances[_to].add(sendAmount);
                          if (fee > 0) {
                              balances[owner] = balances[owner].add(fee);
                              Transfer(msg.sender, owner, fee);
                          }
                          Transfer(msg.sender, _to, sendAmount);
                      }
                  
                      /**
                      * @dev Gets the balance of the specified address.
                      * @param _owner The address to query the the balance of.
                      * @return An uint representing the amount owned by the passed address.
                      */
                      function balanceOf(address _owner) public constant returns (uint balance) {
                          return balances[_owner];
                      }
                  
                  }
                  
                  /**
                   * @title Standard ERC20 token
                   *
                   * @dev Implementation of the basic standard token.
                   * @dev https://github.com/ethereum/EIPs/issues/20
                   * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
                   */
                  contract StandardToken is BasicToken, ERC20 {
                  
                      mapping (address => mapping (address => uint)) public allowed;
                  
                      uint public constant MAX_UINT = 2**256 - 1;
                  
                      /**
                      * @dev Transfer tokens from one address to another
                      * @param _from address The address which you want to send tokens from
                      * @param _to address The address which you want to transfer to
                      * @param _value uint the amount of tokens to be transferred
                      */
                      function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
                          var _allowance = allowed[_from][msg.sender];
                  
                          // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
                          // if (_value > _allowance) throw;
                  
                          uint fee = (_value.mul(basisPointsRate)).div(10000);
                          if (fee > maximumFee) {
                              fee = maximumFee;
                          }
                          if (_allowance < MAX_UINT) {
                              allowed[_from][msg.sender] = _allowance.sub(_value);
                          }
                          uint sendAmount = _value.sub(fee);
                          balances[_from] = balances[_from].sub(_value);
                          balances[_to] = balances[_to].add(sendAmount);
                          if (fee > 0) {
                              balances[owner] = balances[owner].add(fee);
                              Transfer(_from, owner, fee);
                          }
                          Transfer(_from, _to, sendAmount);
                      }
                  
                      /**
                      * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                      * @param _spender The address which will spend the funds.
                      * @param _value The amount of tokens to be spent.
                      */
                      function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
                  
                          // To change the approve amount you first have to reduce the addresses`
                          //  allowance to zero by calling `approve(_spender, 0)` if it is not
                          //  already 0 to mitigate the race condition described here:
                          //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                          require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
                  
                          allowed[msg.sender][_spender] = _value;
                          Approval(msg.sender, _spender, _value);
                      }
                  
                      /**
                      * @dev Function to check the amount of tokens than an owner allowed to a spender.
                      * @param _owner address The address which owns the funds.
                      * @param _spender address The address which will spend the funds.
                      * @return A uint specifying the amount of tokens still available for the spender.
                      */
                      function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                          return allowed[_owner][_spender];
                      }
                  
                  }
                  
                  
                  /**
                   * @title Pausable
                   * @dev Base contract which allows children to implement an emergency stop mechanism.
                   */
                  contract Pausable is Ownable {
                    event Pause();
                    event Unpause();
                  
                    bool public paused = false;
                  
                  
                    /**
                     * @dev Modifier to make a function callable only when the contract is not paused.
                     */
                    modifier whenNotPaused() {
                      require(!paused);
                      _;
                    }
                  
                    /**
                     * @dev Modifier to make a function callable only when the contract is paused.
                     */
                    modifier whenPaused() {
                      require(paused);
                      _;
                    }
                  
                    /**
                     * @dev called by the owner to pause, triggers stopped state
                     */
                    function pause() onlyOwner whenNotPaused public {
                      paused = true;
                      Pause();
                    }
                  
                    /**
                     * @dev called by the owner to unpause, returns to normal state
                     */
                    function unpause() onlyOwner whenPaused public {
                      paused = false;
                      Unpause();
                    }
                  }
                  
                  contract BlackList is Ownable, BasicToken {
                  
                      /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
                      function getBlackListStatus(address _maker) external constant returns (bool) {
                          return isBlackListed[_maker];
                      }
                  
                      function getOwner() external constant returns (address) {
                          return owner;
                      }
                  
                      mapping (address => bool) public isBlackListed;
                      
                      function addBlackList (address _evilUser) public onlyOwner {
                          isBlackListed[_evilUser] = true;
                          AddedBlackList(_evilUser);
                      }
                  
                      function removeBlackList (address _clearedUser) public onlyOwner {
                          isBlackListed[_clearedUser] = false;
                          RemovedBlackList(_clearedUser);
                      }
                  
                      function destroyBlackFunds (address _blackListedUser) public onlyOwner {
                          require(isBlackListed[_blackListedUser]);
                          uint dirtyFunds = balanceOf(_blackListedUser);
                          balances[_blackListedUser] = 0;
                          _totalSupply -= dirtyFunds;
                          DestroyedBlackFunds(_blackListedUser, dirtyFunds);
                      }
                  
                      event DestroyedBlackFunds(address _blackListedUser, uint _balance);
                  
                      event AddedBlackList(address _user);
                  
                      event RemovedBlackList(address _user);
                  
                  }
                  
                  contract UpgradedStandardToken is StandardToken{
                      // those methods are called by the legacy contract
                      // and they must ensure msg.sender to be the contract address
                      function transferByLegacy(address from, address to, uint value) public;
                      function transferFromByLegacy(address sender, address from, address spender, uint value) public;
                      function approveByLegacy(address from, address spender, uint value) public;
                  }
                  
                  contract TetherToken is Pausable, StandardToken, BlackList {
                  
                      string public name;
                      string public symbol;
                      uint public decimals;
                      address public upgradedAddress;
                      bool public deprecated;
                  
                      //  The contract can be initialized with a number of tokens
                      //  All the tokens are deposited to the owner address
                      //
                      // @param _balance Initial supply of the contract
                      // @param _name Token Name
                      // @param _symbol Token symbol
                      // @param _decimals Token decimals
                      function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
                          _totalSupply = _initialSupply;
                          name = _name;
                          symbol = _symbol;
                          decimals = _decimals;
                          balances[owner] = _initialSupply;
                          deprecated = false;
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function transfer(address _to, uint _value) public whenNotPaused {
                          require(!isBlackListed[msg.sender]);
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
                          } else {
                              return super.transfer(_to, _value);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
                          require(!isBlackListed[_from]);
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
                          } else {
                              return super.transferFrom(_from, _to, _value);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function balanceOf(address who) public constant returns (uint) {
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).balanceOf(who);
                          } else {
                              return super.balanceOf(who);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
                          } else {
                              return super.approve(_spender, _value);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                          if (deprecated) {
                              return StandardToken(upgradedAddress).allowance(_owner, _spender);
                          } else {
                              return super.allowance(_owner, _spender);
                          }
                      }
                  
                      // deprecate current contract in favour of a new one
                      function deprecate(address _upgradedAddress) public onlyOwner {
                          deprecated = true;
                          upgradedAddress = _upgradedAddress;
                          Deprecate(_upgradedAddress);
                      }
                  
                      // deprecate current contract if favour of a new one
                      function totalSupply() public constant returns (uint) {
                          if (deprecated) {
                              return StandardToken(upgradedAddress).totalSupply();
                          } else {
                              return _totalSupply;
                          }
                      }
                  
                      // Issue a new amount of tokens
                      // these tokens are deposited into the owner address
                      //
                      // @param _amount Number of tokens to be issued
                      function issue(uint amount) public onlyOwner {
                          require(_totalSupply + amount > _totalSupply);
                          require(balances[owner] + amount > balances[owner]);
                  
                          balances[owner] += amount;
                          _totalSupply += amount;
                          Issue(amount);
                      }
                  
                      // Redeem tokens.
                      // These tokens are withdrawn from the owner address
                      // if the balance must be enough to cover the redeem
                      // or the call will fail.
                      // @param _amount Number of tokens to be issued
                      function redeem(uint amount) public onlyOwner {
                          require(_totalSupply >= amount);
                          require(balances[owner] >= amount);
                  
                          _totalSupply -= amount;
                          balances[owner] -= amount;
                          Redeem(amount);
                      }
                  
                      function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
                          // Ensure transparency by hardcoding limit beyond which fees can never be added
                          require(newBasisPoints < 20);
                          require(newMaxFee < 50);
                  
                          basisPointsRate = newBasisPoints;
                          maximumFee = newMaxFee.mul(10**decimals);
                  
                          Params(basisPointsRate, maximumFee);
                      }
                  
                      // Called when new token are issued
                      event Issue(uint amount);
                  
                      // Called when tokens are redeemed
                      event Redeem(uint amount);
                  
                      // Called when contract is deprecated
                      event Deprecate(address newAddress);
                  
                      // Called if contract ever adds fees
                      event Params(uint feeBasisPoints, uint maxFee);
                  }

                  File 2 of 10: ERC1967Proxy
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
                   */
                  interface IERC1967 {
                      /**
                       * @dev Emitted when the implementation is upgraded.
                       */
                      event Upgraded(address indexed implementation);
                      /**
                       * @dev Emitted when the admin account has changed.
                       */
                      event AdminChanged(address previousAdmin, address newAdmin);
                      /**
                       * @dev Emitted when the beacon is changed.
                       */
                      event BeaconUpgraded(address indexed beacon);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev This is the interface that {BeaconProxy} expects of its beacon.
                   */
                  interface IBeacon {
                      /**
                       * @dev Must return an address that can be used as a delegate call target.
                       *
                       * {UpgradeableBeacon} will check that this address is a contract.
                       */
                      function implementation() external view returns (address);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (proxy/ERC1967/ERC1967Proxy.sol)
                  pragma solidity ^0.8.20;
                  import {Proxy} from "../Proxy.sol";
                  import {ERC1967Utils} from "./ERC1967Utils.sol";
                  /**
                   * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
                   * implementation address that can be changed. This address is stored in storage in the location specified by
                   * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967], so that it doesn't conflict with the storage layout of the
                   * implementation behind the proxy.
                   */
                  contract ERC1967Proxy is Proxy {
                      /**
                       * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
                       *
                       * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
                       * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
                       *
                       * Requirements:
                       *
                       * - If `data` is empty, `msg.value` must be zero.
                       */
                      constructor(address implementation, bytes memory _data) payable {
                          ERC1967Utils.upgradeToAndCall(implementation, _data);
                      }
                      /**
                       * @dev Returns the current implementation address.
                       *
                       * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
                       * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                       * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                       */
                      function _implementation() internal view virtual override returns (address) {
                          return ERC1967Utils.getImplementation();
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (proxy/ERC1967/ERC1967Utils.sol)
                  pragma solidity ^0.8.21;
                  import {IBeacon} from "../beacon/IBeacon.sol";
                  import {IERC1967} from "../../interfaces/IERC1967.sol";
                  import {Address} from "../../utils/Address.sol";
                  import {StorageSlot} from "../../utils/StorageSlot.sol";
                  /**
                   * @dev This library provides getters and event emitting update functions for
                   * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
                   */
                  library ERC1967Utils {
                      /**
                       * @dev Storage slot with the address of the current implementation.
                       * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
                       */
                      // solhint-disable-next-line private-vars-leading-underscore
                      bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                      /**
                       * @dev The `implementation` of the proxy is invalid.
                       */
                      error ERC1967InvalidImplementation(address implementation);
                      /**
                       * @dev The `admin` of the proxy is invalid.
                       */
                      error ERC1967InvalidAdmin(address admin);
                      /**
                       * @dev The `beacon` of the proxy is invalid.
                       */
                      error ERC1967InvalidBeacon(address beacon);
                      /**
                       * @dev An upgrade function sees `msg.value > 0` that may be lost.
                       */
                      error ERC1967NonPayable();
                      /**
                       * @dev Returns the current implementation address.
                       */
                      function getImplementation() internal view returns (address) {
                          return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
                      }
                      /**
                       * @dev Stores a new address in the ERC-1967 implementation slot.
                       */
                      function _setImplementation(address newImplementation) private {
                          if (newImplementation.code.length == 0) {
                              revert ERC1967InvalidImplementation(newImplementation);
                          }
                          StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
                      }
                      /**
                       * @dev Performs implementation upgrade with additional setup call if data is nonempty.
                       * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                       * to avoid stuck value in the contract.
                       *
                       * Emits an {IERC1967-Upgraded} event.
                       */
                      function upgradeToAndCall(address newImplementation, bytes memory data) internal {
                          _setImplementation(newImplementation);
                          emit IERC1967.Upgraded(newImplementation);
                          if (data.length > 0) {
                              Address.functionDelegateCall(newImplementation, data);
                          } else {
                              _checkNonPayable();
                          }
                      }
                      /**
                       * @dev Storage slot with the admin of the contract.
                       * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
                       */
                      // solhint-disable-next-line private-vars-leading-underscore
                      bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                      /**
                       * @dev Returns the current admin.
                       *
                       * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
                       * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                       * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                       */
                      function getAdmin() internal view returns (address) {
                          return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
                      }
                      /**
                       * @dev Stores a new address in the ERC-1967 admin slot.
                       */
                      function _setAdmin(address newAdmin) private {
                          if (newAdmin == address(0)) {
                              revert ERC1967InvalidAdmin(address(0));
                          }
                          StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
                      }
                      /**
                       * @dev Changes the admin of the proxy.
                       *
                       * Emits an {IERC1967-AdminChanged} event.
                       */
                      function changeAdmin(address newAdmin) internal {
                          emit IERC1967.AdminChanged(getAdmin(), newAdmin);
                          _setAdmin(newAdmin);
                      }
                      /**
                       * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                       * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
                       */
                      // solhint-disable-next-line private-vars-leading-underscore
                      bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                      /**
                       * @dev Returns the current beacon.
                       */
                      function getBeacon() internal view returns (address) {
                          return StorageSlot.getAddressSlot(BEACON_SLOT).value;
                      }
                      /**
                       * @dev Stores a new beacon in the ERC-1967 beacon slot.
                       */
                      function _setBeacon(address newBeacon) private {
                          if (newBeacon.code.length == 0) {
                              revert ERC1967InvalidBeacon(newBeacon);
                          }
                          StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
                          address beaconImplementation = IBeacon(newBeacon).implementation();
                          if (beaconImplementation.code.length == 0) {
                              revert ERC1967InvalidImplementation(beaconImplementation);
                          }
                      }
                      /**
                       * @dev Change the beacon and trigger a setup call if data is nonempty.
                       * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                       * to avoid stuck value in the contract.
                       *
                       * Emits an {IERC1967-BeaconUpgraded} event.
                       *
                       * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
                       * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
                       * efficiency.
                       */
                      function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
                          _setBeacon(newBeacon);
                          emit IERC1967.BeaconUpgraded(newBeacon);
                          if (data.length > 0) {
                              Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                          } else {
                              _checkNonPayable();
                          }
                      }
                      /**
                       * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
                       * if an upgrade doesn't perform an initialization call.
                       */
                      function _checkNonPayable() private {
                          if (msg.value > 0) {
                              revert ERC1967NonPayable();
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
                   * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
                   * be specified by overriding the virtual {_implementation} function.
                   *
                   * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
                   * different contract through the {_delegate} function.
                   *
                   * The success and return data of the delegated call will be returned back to the caller of the proxy.
                   */
                  abstract contract Proxy {
                      /**
                       * @dev Delegates the current call to `implementation`.
                       *
                       * This function does not return to its internal call site, it will return directly to the external caller.
                       */
                      function _delegate(address implementation) internal virtual {
                          assembly {
                              // Copy msg.data. We take full control of memory in this inline assembly
                              // block because it will not return to Solidity code. We overwrite the
                              // Solidity scratch pad at memory position 0.
                              calldatacopy(0, 0, calldatasize())
                              // Call the implementation.
                              // out and outsize are 0 because we don't know the size yet.
                              let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                              // Copy the returned data.
                              returndatacopy(0, 0, returndatasize())
                              switch result
                              // delegatecall returns 0 on error.
                              case 0 {
                                  revert(0, returndatasize())
                              }
                              default {
                                  return(0, returndatasize())
                              }
                          }
                      }
                      /**
                       * @dev This is a virtual function that should be overridden so it returns the address to which the fallback
                       * function and {_fallback} should delegate.
                       */
                      function _implementation() internal view virtual returns (address);
                      /**
                       * @dev Delegates the current call to the address returned by `_implementation()`.
                       *
                       * This function does not return to its internal call site, it will return directly to the external caller.
                       */
                      function _fallback() internal virtual {
                          _delegate(_implementation());
                      }
                      /**
                       * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                       * function in the contract matches the call data.
                       */
                      fallback() external payable virtual {
                          _fallback();
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)
                  pragma solidity ^0.8.20;
                  import {Errors} from "./Errors.sol";
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library Address {
                      /**
                       * @dev There's no code at `target` (it is not a contract).
                       */
                      error AddressEmptyCode(address target);
                      /**
                       * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                       * `recipient`, forwarding all available gas and reverting on errors.
                       *
                       * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                       * of certain opcodes, possibly making contracts go over the 2300 gas limit
                       * imposed by `transfer`, making them unable to receive funds via
                       * `transfer`. {sendValue} removes this limitation.
                       *
                       * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                       */
                      function sendValue(address payable recipient, uint256 amount) internal {
                          if (address(this).balance < amount) {
                              revert Errors.InsufficientBalance(address(this).balance, amount);
                          }
                          (bool success, ) = recipient.call{value: amount}("");
                          if (!success) {
                              revert Errors.FailedCall();
                          }
                      }
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain `call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason or custom error, it is bubbled
                       * up by this function (like regular Solidity function calls). However, if
                       * the call reverted with no returned reason, this function reverts with a
                       * {Errors.FailedCall} error.
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, 0);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but also transferring `value` wei to `target`.
                       *
                       * Requirements:
                       *
                       * - the calling contract must have an ETH balance of at least `value`.
                       * - the called Solidity function must be `payable`.
                       */
                      function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                          if (address(this).balance < value) {
                              revert Errors.InsufficientBalance(address(this).balance, value);
                          }
                          (bool success, bytes memory returndata) = target.call{value: value}(data);
                          return verifyCallResultFromTarget(target, success, returndata);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a static call.
                       */
                      function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return verifyCallResultFromTarget(target, success, returndata);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a delegate call.
                       */
                      function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                          (bool success, bytes memory returndata) = target.delegatecall(data);
                          return verifyCallResultFromTarget(target, success, returndata);
                      }
                      /**
                       * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
                       * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
                       * of an unsuccessful call.
                       */
                      function verifyCallResultFromTarget(
                          address target,
                          bool success,
                          bytes memory returndata
                      ) internal view returns (bytes memory) {
                          if (!success) {
                              _revert(returndata);
                          } else {
                              // only check if target is a contract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              if (returndata.length == 0 && target.code.length == 0) {
                                  revert AddressEmptyCode(target);
                              }
                              return returndata;
                          }
                      }
                      /**
                       * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
                       * revert reason or with a default {Errors.FailedCall} error.
                       */
                      function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                          if (!success) {
                              _revert(returndata);
                          } else {
                              return returndata;
                          }
                      }
                      /**
                       * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
                       */
                      function _revert(bytes memory returndata) private pure {
                          // Look for revert reason and bubble it up if present
                          if (returndata.length > 0) {
                              // The easiest way to bubble the revert reason is using memory via assembly
                              assembly ("memory-safe") {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert Errors.FailedCall();
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev Collection of common custom errors used in multiple contracts
                   *
                   * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
                   * It is recommended to avoid relying on the error API for critical functionality.
                   *
                   * _Available since v5.1._
                   */
                  library Errors {
                      /**
                       * @dev The ETH balance of the account is not enough to perform the operation.
                       */
                      error InsufficientBalance(uint256 balance, uint256 needed);
                      /**
                       * @dev A call to an address target failed. The target may have reverted.
                       */
                      error FailedCall();
                      /**
                       * @dev The deployment failed.
                       */
                      error FailedDeployment();
                      /**
                       * @dev A necessary precompile is missing.
                       */
                      error MissingPrecompile(address);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
                  // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
                  pragma solidity ^0.8.20;
                  /**
                   * @dev Library for reading and writing primitive types to specific storage slots.
                   *
                   * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
                   * This library helps with reading and writing to such slots without the need for inline assembly.
                   *
                   * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
                   *
                   * Example usage to set ERC-1967 implementation slot:
                   * ```solidity
                   * contract ERC1967 {
                   *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
                   *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                   *
                   *     function _getImplementation() internal view returns (address) {
                   *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                   *     }
                   *
                   *     function _setImplementation(address newImplementation) internal {
                   *         require(newImplementation.code.length > 0);
                   *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                   *     }
                   * }
                   * ```
                   *
                   * TIP: Consider using this library along with {SlotDerivation}.
                   */
                  library StorageSlot {
                      struct AddressSlot {
                          address value;
                      }
                      struct BooleanSlot {
                          bool value;
                      }
                      struct Bytes32Slot {
                          bytes32 value;
                      }
                      struct Uint256Slot {
                          uint256 value;
                      }
                      struct Int256Slot {
                          int256 value;
                      }
                      struct StringSlot {
                          string value;
                      }
                      struct BytesSlot {
                          bytes value;
                      }
                      /**
                       * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                       */
                      function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns a `BooleanSlot` with member `value` located at `slot`.
                       */
                      function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
                       */
                      function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns a `Uint256Slot` with member `value` located at `slot`.
                       */
                      function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns a `Int256Slot` with member `value` located at `slot`.
                       */
                      function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns a `StringSlot` with member `value` located at `slot`.
                       */
                      function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                       */
                      function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := store.slot
                          }
                      }
                      /**
                       * @dev Returns a `BytesSlot` with member `value` located at `slot`.
                       */
                      function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                       */
                      function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := store.slot
                          }
                      }
                  }
                  

                  File 3 of 10: InitializableImmutableAdminUpgradeabilityProxy
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                      // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                      // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                      // for accounts without code, i.e. `keccak256('')`
                      bytes32 codehash;
                      bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        codehash := extcodehash(account)
                      }
                      return (codehash != accountHash && codehash != 0x0);
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, 'Address: insufficient balance');
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (bool success, ) = recipient.call{value: amount}('');
                      require(success, 'Address: unable to send value, recipient may have reverted');
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import './Proxy.sol';
                  import '../contracts/Address.sol';
                  /**
                   * @title BaseUpgradeabilityProxy
                   * @dev This contract implements a proxy that allows to change the
                   * implementation address to which it will delegate.
                   * Such a change is called an implementation upgrade.
                   */
                  contract BaseUpgradeabilityProxy is Proxy {
                    /**
                     * @dev Emitted when the implementation is upgraded.
                     * @param implementation Address of the new implementation.
                     */
                    event Upgraded(address indexed implementation);
                    /**
                     * @dev Storage slot with the address of the current implementation.
                     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                     * validated in the constructor.
                     */
                    bytes32 internal constant IMPLEMENTATION_SLOT =
                      0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                    /**
                     * @dev Returns the current implementation.
                     * @return impl Address of the current implementation
                     */
                    function _implementation() internal view override returns (address impl) {
                      bytes32 slot = IMPLEMENTATION_SLOT;
                      //solium-disable-next-line
                      assembly {
                        impl := sload(slot)
                      }
                    }
                    /**
                     * @dev Upgrades the proxy to a new implementation.
                     * @param newImplementation Address of the new implementation.
                     */
                    function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                    }
                    /**
                     * @dev Sets the implementation address of the proxy.
                     * @param newImplementation Address of the new implementation.
                     */
                    function _setImplementation(address newImplementation) internal {
                      require(
                        Address.isContract(newImplementation),
                        'Cannot set a proxy implementation to a non-contract address'
                      );
                      bytes32 slot = IMPLEMENTATION_SLOT;
                      //solium-disable-next-line
                      assembly {
                        sstore(slot, newImplementation)
                      }
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import './BaseUpgradeabilityProxy.sol';
                  /**
                   * @title InitializableUpgradeabilityProxy
                   * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
                   * implementation and init data.
                   */
                  contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
                    /**
                     * @dev Contract initializer.
                     * @param _logic Address of the initial implementation.
                     * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                     * It should include the signature and the parameters of the function to be called, as described in
                     * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                     * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                     */
                    function initialize(address _logic, bytes memory _data) public payable {
                      require(_implementation() == address(0));
                      assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                      _setImplementation(_logic);
                      if (_data.length > 0) {
                        (bool success, ) = _logic.delegatecall(_data);
                        require(success);
                      }
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  /**
                   * @title Proxy
                   * @dev Implements delegation of calls to other contracts, with proper
                   * forwarding of return values and bubbling of failures.
                   * It defines a fallback function that delegates all calls to the address
                   * returned by the abstract _implementation() internal function.
                   */
                  abstract contract Proxy {
                    /**
                     * @dev Fallback function.
                     * Will run if no other function in the contract matches the call data.
                     * Implemented entirely in `_fallback`.
                     */
                    fallback() external payable {
                      _fallback();
                    }
                    /**
                     * @return The Address of the implementation.
                     */
                    function _implementation() internal view virtual returns (address);
                    /**
                     * @dev Delegates execution to an implementation contract.
                     * This is a low level function that doesn't return to its internal call site.
                     * It will return to the external caller whatever the implementation returns.
                     * @param implementation Address to delegate.
                     */
                    function _delegate(address implementation) internal {
                      //solium-disable-next-line
                      assembly {
                        // Copy msg.data. We take full control of memory in this inline assembly
                        // block because it will not return to Solidity code. We overwrite the
                        // Solidity scratch pad at memory position 0.
                        calldatacopy(0, 0, calldatasize())
                        // Call the implementation.
                        // out and outsize are 0 because we don't know the size yet.
                        let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                        // Copy the returned data.
                        returndatacopy(0, 0, returndatasize())
                        switch result
                        // delegatecall returns 0 on error.
                        case 0 {
                          revert(0, returndatasize())
                        }
                        default {
                          return(0, returndatasize())
                        }
                      }
                    }
                    /**
                     * @dev Function that is run as the first thing in the fallback function.
                     * Can be redefined in derived contracts to add functionality.
                     * Redefinitions must call super._willFallback().
                     */
                    function _willFallback() internal virtual {}
                    /**
                     * @dev fallback implementation.
                     * Extracted to enable manual triggering.
                     */
                    function _fallback() internal {
                      _willFallback();
                      _delegate(_implementation());
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import {BaseUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
                  /**
                   * @title BaseImmutableAdminUpgradeabilityProxy
                   * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
                   * @notice This contract combines an upgradeability proxy with an authorization
                   * mechanism for administrative tasks.
                   * @dev The admin role is stored in an immutable, which helps saving transactions costs
                   * All external functions in this contract must be guarded by the
                   * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
                   * feature proposal that would enable this to be done automatically.
                   */
                  contract BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                    address internal immutable _admin;
                    /**
                     * @dev Constructor.
                     * @param admin The address of the admin
                     */
                    constructor(address admin) {
                      _admin = admin;
                    }
                    modifier ifAdmin() {
                      if (msg.sender == _admin) {
                        _;
                      } else {
                        _fallback();
                      }
                    }
                    /**
                     * @notice Return the admin address
                     * @return The address of the proxy admin.
                     */
                    function admin() external ifAdmin returns (address) {
                      return _admin;
                    }
                    /**
                     * @notice Return the implementation address
                     * @return The address of the implementation.
                     */
                    function implementation() external ifAdmin returns (address) {
                      return _implementation();
                    }
                    /**
                     * @notice Upgrade the backing implementation of the proxy.
                     * @dev Only the admin can call this function.
                     * @param newImplementation The address of the new implementation.
                     */
                    function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeTo(newImplementation);
                    }
                    /**
                     * @notice Upgrade the backing implementation of the proxy and call a function
                     * on the new implementation.
                     * @dev This is useful to initialize the proxied contract.
                     * @param newImplementation The address of the new implementation.
                     * @param data Data to send as msg.data in the low level call.
                     * It should include the signature and the parameters of the function to be called, as described in
                     * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                     */
                    function upgradeToAndCall(address newImplementation, bytes calldata data)
                      external
                      payable
                      ifAdmin
                    {
                      _upgradeTo(newImplementation);
                      (bool success, ) = newImplementation.delegatecall(data);
                      require(success);
                    }
                    /**
                     * @notice Only fall back when the sender is not the admin.
                     */
                    function _willFallback() internal virtual override {
                      require(msg.sender != _admin, 'Cannot call fallback function from the proxy admin');
                      super._willFallback();
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import {InitializableUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
                  import {Proxy} from '../../../dependencies/openzeppelin/upgradeability/Proxy.sol';
                  import {BaseImmutableAdminUpgradeabilityProxy} from './BaseImmutableAdminUpgradeabilityProxy.sol';
                  /**
                   * @title InitializableAdminUpgradeabilityProxy
                   * @author Aave
                   * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
                   */
                  contract InitializableImmutableAdminUpgradeabilityProxy is
                    BaseImmutableAdminUpgradeabilityProxy,
                    InitializableUpgradeabilityProxy
                  {
                    /**
                     * @dev Constructor.
                     * @param admin The address of the admin
                     */
                    constructor(address admin) BaseImmutableAdminUpgradeabilityProxy(admin) {
                      // Intentionally left blank
                    }
                    /// @inheritdoc BaseImmutableAdminUpgradeabilityProxy
                    function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                      BaseImmutableAdminUpgradeabilityProxy._willFallback();
                    }
                  }
                  

                  File 4 of 10: InitializableImmutableAdminUpgradeabilityProxy
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                      // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                      // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                      // for accounts without code, i.e. `keccak256('')`
                      bytes32 codehash;
                      bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        codehash := extcodehash(account)
                      }
                      return (codehash != accountHash && codehash != 0x0);
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, 'Address: insufficient balance');
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (bool success, ) = recipient.call{value: amount}('');
                      require(success, 'Address: unable to send value, recipient may have reverted');
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import './Proxy.sol';
                  import '../contracts/Address.sol';
                  /**
                   * @title BaseUpgradeabilityProxy
                   * @dev This contract implements a proxy that allows to change the
                   * implementation address to which it will delegate.
                   * Such a change is called an implementation upgrade.
                   */
                  contract BaseUpgradeabilityProxy is Proxy {
                    /**
                     * @dev Emitted when the implementation is upgraded.
                     * @param implementation Address of the new implementation.
                     */
                    event Upgraded(address indexed implementation);
                    /**
                     * @dev Storage slot with the address of the current implementation.
                     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                     * validated in the constructor.
                     */
                    bytes32 internal constant IMPLEMENTATION_SLOT =
                      0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                    /**
                     * @dev Returns the current implementation.
                     * @return impl Address of the current implementation
                     */
                    function _implementation() internal view override returns (address impl) {
                      bytes32 slot = IMPLEMENTATION_SLOT;
                      //solium-disable-next-line
                      assembly {
                        impl := sload(slot)
                      }
                    }
                    /**
                     * @dev Upgrades the proxy to a new implementation.
                     * @param newImplementation Address of the new implementation.
                     */
                    function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                    }
                    /**
                     * @dev Sets the implementation address of the proxy.
                     * @param newImplementation Address of the new implementation.
                     */
                    function _setImplementation(address newImplementation) internal {
                      require(
                        Address.isContract(newImplementation),
                        'Cannot set a proxy implementation to a non-contract address'
                      );
                      bytes32 slot = IMPLEMENTATION_SLOT;
                      //solium-disable-next-line
                      assembly {
                        sstore(slot, newImplementation)
                      }
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import './BaseUpgradeabilityProxy.sol';
                  /**
                   * @title InitializableUpgradeabilityProxy
                   * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
                   * implementation and init data.
                   */
                  contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
                    /**
                     * @dev Contract initializer.
                     * @param _logic Address of the initial implementation.
                     * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                     * It should include the signature and the parameters of the function to be called, as described in
                     * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                     * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                     */
                    function initialize(address _logic, bytes memory _data) public payable {
                      require(_implementation() == address(0));
                      assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                      _setImplementation(_logic);
                      if (_data.length > 0) {
                        (bool success, ) = _logic.delegatecall(_data);
                        require(success);
                      }
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  /**
                   * @title Proxy
                   * @dev Implements delegation of calls to other contracts, with proper
                   * forwarding of return values and bubbling of failures.
                   * It defines a fallback function that delegates all calls to the address
                   * returned by the abstract _implementation() internal function.
                   */
                  abstract contract Proxy {
                    /**
                     * @dev Fallback function.
                     * Will run if no other function in the contract matches the call data.
                     * Implemented entirely in `_fallback`.
                     */
                    fallback() external payable {
                      _fallback();
                    }
                    /**
                     * @return The Address of the implementation.
                     */
                    function _implementation() internal view virtual returns (address);
                    /**
                     * @dev Delegates execution to an implementation contract.
                     * This is a low level function that doesn't return to its internal call site.
                     * It will return to the external caller whatever the implementation returns.
                     * @param implementation Address to delegate.
                     */
                    function _delegate(address implementation) internal {
                      //solium-disable-next-line
                      assembly {
                        // Copy msg.data. We take full control of memory in this inline assembly
                        // block because it will not return to Solidity code. We overwrite the
                        // Solidity scratch pad at memory position 0.
                        calldatacopy(0, 0, calldatasize())
                        // Call the implementation.
                        // out and outsize are 0 because we don't know the size yet.
                        let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                        // Copy the returned data.
                        returndatacopy(0, 0, returndatasize())
                        switch result
                        // delegatecall returns 0 on error.
                        case 0 {
                          revert(0, returndatasize())
                        }
                        default {
                          return(0, returndatasize())
                        }
                      }
                    }
                    /**
                     * @dev Function that is run as the first thing in the fallback function.
                     * Can be redefined in derived contracts to add functionality.
                     * Redefinitions must call super._willFallback().
                     */
                    function _willFallback() internal virtual {}
                    /**
                     * @dev fallback implementation.
                     * Extracted to enable manual triggering.
                     */
                    function _fallback() internal {
                      _willFallback();
                      _delegate(_implementation());
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import {BaseUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
                  /**
                   * @title BaseImmutableAdminUpgradeabilityProxy
                   * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
                   * @notice This contract combines an upgradeability proxy with an authorization
                   * mechanism for administrative tasks.
                   * @dev The admin role is stored in an immutable, which helps saving transactions costs
                   * All external functions in this contract must be guarded by the
                   * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
                   * feature proposal that would enable this to be done automatically.
                   */
                  contract BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                    address internal immutable _admin;
                    /**
                     * @dev Constructor.
                     * @param admin The address of the admin
                     */
                    constructor(address admin) {
                      _admin = admin;
                    }
                    modifier ifAdmin() {
                      if (msg.sender == _admin) {
                        _;
                      } else {
                        _fallback();
                      }
                    }
                    /**
                     * @notice Return the admin address
                     * @return The address of the proxy admin.
                     */
                    function admin() external ifAdmin returns (address) {
                      return _admin;
                    }
                    /**
                     * @notice Return the implementation address
                     * @return The address of the implementation.
                     */
                    function implementation() external ifAdmin returns (address) {
                      return _implementation();
                    }
                    /**
                     * @notice Upgrade the backing implementation of the proxy.
                     * @dev Only the admin can call this function.
                     * @param newImplementation The address of the new implementation.
                     */
                    function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeTo(newImplementation);
                    }
                    /**
                     * @notice Upgrade the backing implementation of the proxy and call a function
                     * on the new implementation.
                     * @dev This is useful to initialize the proxied contract.
                     * @param newImplementation The address of the new implementation.
                     * @param data Data to send as msg.data in the low level call.
                     * It should include the signature and the parameters of the function to be called, as described in
                     * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                     */
                    function upgradeToAndCall(address newImplementation, bytes calldata data)
                      external
                      payable
                      ifAdmin
                    {
                      _upgradeTo(newImplementation);
                      (bool success, ) = newImplementation.delegatecall(data);
                      require(success);
                    }
                    /**
                     * @notice Only fall back when the sender is not the admin.
                     */
                    function _willFallback() internal virtual override {
                      require(msg.sender != _admin, 'Cannot call fallback function from the proxy admin');
                      super._willFallback();
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import {InitializableUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
                  import {Proxy} from '../../../dependencies/openzeppelin/upgradeability/Proxy.sol';
                  import {BaseImmutableAdminUpgradeabilityProxy} from './BaseImmutableAdminUpgradeabilityProxy.sol';
                  /**
                   * @title InitializableAdminUpgradeabilityProxy
                   * @author Aave
                   * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
                   */
                  contract InitializableImmutableAdminUpgradeabilityProxy is
                    BaseImmutableAdminUpgradeabilityProxy,
                    InitializableUpgradeabilityProxy
                  {
                    /**
                     * @dev Constructor.
                     * @param admin The address of the admin
                     */
                    constructor(address admin) BaseImmutableAdminUpgradeabilityProxy(admin) {
                      // Intentionally left blank
                    }
                    /// @inheritdoc BaseImmutableAdminUpgradeabilityProxy
                    function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                      BaseImmutableAdminUpgradeabilityProxy._willFallback();
                    }
                  }
                  

                  File 5 of 10: MultiProtocolTokenVault
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.3.0) (access/AccessControl.sol)
                  pragma solidity ^0.8.20;
                  import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
                  import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
                  import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
                  import {Initializable} from "../proxy/utils/Initializable.sol";
                  /**
                   * @dev Contract module that allows children to implement role-based access
                   * control mechanisms. This is a lightweight version that doesn't allow enumerating role
                   * members except through off-chain means by accessing the contract event logs. Some
                   * applications may benefit from on-chain enumerability, for those cases see
                   * {AccessControlEnumerable}.
                   *
                   * Roles are referred to by their `bytes32` identifier. These should be exposed
                   * in the external API and be unique. The best way to achieve this is by
                   * using `public constant` hash digests:
                   *
                   * ```solidity
                   * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
                   * ```
                   *
                   * Roles can be used to represent a set of permissions. To restrict access to a
                   * function call, use {hasRole}:
                   *
                   * ```solidity
                   * function foo() public {
                   *     require(hasRole(MY_ROLE, msg.sender));
                   *     ...
                   * }
                   * ```
                   *
                   * Roles can be granted and revoked dynamically via the {grantRole} and
                   * {revokeRole} functions. Each role has an associated admin role, and only
                   * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
                   *
                   * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
                   * that only accounts with this role will be able to grant or revoke other
                   * roles. More complex role relationships can be created by using
                   * {_setRoleAdmin}.
                   *
                   * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
                   * grant and revoke this role. Extra precautions should be taken to secure
                   * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
                   * to enforce additional security measures for this role.
                   */
                  abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
                      struct RoleData {
                          mapping(address account => bool) hasRole;
                          bytes32 adminRole;
                      }
                      bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                      /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
                      struct AccessControlStorage {
                          mapping(bytes32 role => RoleData) _roles;
                      }
                      // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
                      bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
                      function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
                          assembly {
                              $.slot := AccessControlStorageLocation
                          }
                      }
                      /**
                       * @dev Modifier that checks that an account has a specific role. Reverts
                       * with an {AccessControlUnauthorizedAccount} error including the required role.
                       */
                      modifier onlyRole(bytes32 role) {
                          _checkRole(role);
                          _;
                      }
                      function __AccessControl_init() internal onlyInitializing {
                      }
                      function __AccessControl_init_unchained() internal onlyInitializing {
                      }
                      /**
                       * @dev See {IERC165-supportsInterface}.
                       */
                      function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                          return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
                      }
                      /**
                       * @dev Returns `true` if `account` has been granted `role`.
                       */
                      function hasRole(bytes32 role, address account) public view virtual returns (bool) {
                          AccessControlStorage storage $ = _getAccessControlStorage();
                          return $._roles[role].hasRole[account];
                      }
                      /**
                       * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
                       * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
                       */
                      function _checkRole(bytes32 role) internal view virtual {
                          _checkRole(role, _msgSender());
                      }
                      /**
                       * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
                       * is missing `role`.
                       */
                      function _checkRole(bytes32 role, address account) internal view virtual {
                          if (!hasRole(role, account)) {
                              revert AccessControlUnauthorizedAccount(account, role);
                          }
                      }
                      /**
                       * @dev Returns the admin role that controls `role`. See {grantRole} and
                       * {revokeRole}.
                       *
                       * To change a role's admin, use {_setRoleAdmin}.
                       */
                      function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
                          AccessControlStorage storage $ = _getAccessControlStorage();
                          return $._roles[role].adminRole;
                      }
                      /**
                       * @dev Grants `role` to `account`.
                       *
                       * If `account` had not been already granted `role`, emits a {RoleGranted}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       *
                       * May emit a {RoleGranted} event.
                       */
                      function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
                          _grantRole(role, account);
                      }
                      /**
                       * @dev Revokes `role` from `account`.
                       *
                       * If `account` had been granted `role`, emits a {RoleRevoked} event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       *
                       * May emit a {RoleRevoked} event.
                       */
                      function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
                          _revokeRole(role, account);
                      }
                      /**
                       * @dev Revokes `role` from the calling account.
                       *
                       * Roles are often managed via {grantRole} and {revokeRole}: this function's
                       * purpose is to provide a mechanism for accounts to lose their privileges
                       * if they are compromised (such as when a trusted device is misplaced).
                       *
                       * If the calling account had been revoked `role`, emits a {RoleRevoked}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must be `callerConfirmation`.
                       *
                       * May emit a {RoleRevoked} event.
                       */
                      function renounceRole(bytes32 role, address callerConfirmation) public virtual {
                          if (callerConfirmation != _msgSender()) {
                              revert AccessControlBadConfirmation();
                          }
                          _revokeRole(role, callerConfirmation);
                      }
                      /**
                       * @dev Sets `adminRole` as ``role``'s admin role.
                       *
                       * Emits a {RoleAdminChanged} event.
                       */
                      function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                          AccessControlStorage storage $ = _getAccessControlStorage();
                          bytes32 previousAdminRole = getRoleAdmin(role);
                          $._roles[role].adminRole = adminRole;
                          emit RoleAdminChanged(role, previousAdminRole, adminRole);
                      }
                      /**
                       * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
                       *
                       * Internal function without access restriction.
                       *
                       * May emit a {RoleGranted} event.
                       */
                      function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
                          AccessControlStorage storage $ = _getAccessControlStorage();
                          if (!hasRole(role, account)) {
                              $._roles[role].hasRole[account] = true;
                              emit RoleGranted(role, account, _msgSender());
                              return true;
                          } else {
                              return false;
                          }
                      }
                      /**
                       * @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
                       *
                       * Internal function without access restriction.
                       *
                       * May emit a {RoleRevoked} event.
                       */
                      function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
                          AccessControlStorage storage $ = _getAccessControlStorage();
                          if (hasRole(role, account)) {
                              $._roles[role].hasRole[account] = false;
                              emit RoleRevoked(role, account, _msgSender());
                              return true;
                          } else {
                              return false;
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
                   * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
                   * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
                   * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
                   *
                   * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
                   * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
                   * case an upgrade adds a module that needs to be initialized.
                   *
                   * For example:
                   *
                   * [.hljs-theme-light.nopadding]
                   * ```solidity
                   * contract MyToken is ERC20Upgradeable {
                   *     function initialize() initializer public {
                   *         __ERC20_init("MyToken", "MTK");
                   *     }
                   * }
                   *
                   * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
                   *     function initializeV2() reinitializer(2) public {
                   *         __ERC20Permit_init("MyToken");
                   *     }
                   * }
                   * ```
                   *
                   * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
                   * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
                   *
                   * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
                   * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
                   *
                   * [CAUTION]
                   * ====
                   * Avoid leaving a contract uninitialized.
                   *
                   * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
                   * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
                   * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
                   *
                   * [.hljs-theme-light.nopadding]
                   * ```
                   * /// @custom:oz-upgrades-unsafe-allow constructor
                   * constructor() {
                   *     _disableInitializers();
                   * }
                   * ```
                   * ====
                   */
                  abstract contract Initializable {
                      /**
                       * @dev Storage of the initializable contract.
                       *
                       * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
                       * when using with upgradeable contracts.
                       *
                       * @custom:storage-location erc7201:openzeppelin.storage.Initializable
                       */
                      struct InitializableStorage {
                          /**
                           * @dev Indicates that the contract has been initialized.
                           */
                          uint64 _initialized;
                          /**
                           * @dev Indicates that the contract is in the process of being initialized.
                           */
                          bool _initializing;
                      }
                      // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
                      bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
                      /**
                       * @dev The contract is already initialized.
                       */
                      error InvalidInitialization();
                      /**
                       * @dev The contract is not initializing.
                       */
                      error NotInitializing();
                      /**
                       * @dev Triggered when the contract has been initialized or reinitialized.
                       */
                      event Initialized(uint64 version);
                      /**
                       * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                       * `onlyInitializing` functions can be used to initialize parent contracts.
                       *
                       * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
                       * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
                       * production.
                       *
                       * Emits an {Initialized} event.
                       */
                      modifier initializer() {
                          // solhint-disable-next-line var-name-mixedcase
                          InitializableStorage storage $ = _getInitializableStorage();
                          // Cache values to avoid duplicated sloads
                          bool isTopLevelCall = !$._initializing;
                          uint64 initialized = $._initialized;
                          // Allowed calls:
                          // - initialSetup: the contract is not in the initializing state and no previous version was
                          //                 initialized
                          // - construction: the contract is initialized at version 1 (no reinitialization) and the
                          //                 current contract is just being deployed
                          bool initialSetup = initialized == 0 && isTopLevelCall;
                          bool construction = initialized == 1 && address(this).code.length == 0;
                          if (!initialSetup && !construction) {
                              revert InvalidInitialization();
                          }
                          $._initialized = 1;
                          if (isTopLevelCall) {
                              $._initializing = true;
                          }
                          _;
                          if (isTopLevelCall) {
                              $._initializing = false;
                              emit Initialized(1);
                          }
                      }
                      /**
                       * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                       * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                       * used to initialize parent contracts.
                       *
                       * A reinitializer may be used after the original initialization step. This is essential to configure modules that
                       * are added through upgrades and that require initialization.
                       *
                       * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
                       * cannot be nested. If one is invoked in the context of another, execution will revert.
                       *
                       * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                       * a contract, executing them in the right order is up to the developer or operator.
                       *
                       * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
                       *
                       * Emits an {Initialized} event.
                       */
                      modifier reinitializer(uint64 version) {
                          // solhint-disable-next-line var-name-mixedcase
                          InitializableStorage storage $ = _getInitializableStorage();
                          if ($._initializing || $._initialized >= version) {
                              revert InvalidInitialization();
                          }
                          $._initialized = version;
                          $._initializing = true;
                          _;
                          $._initializing = false;
                          emit Initialized(version);
                      }
                      /**
                       * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                       * {initializer} and {reinitializer} modifiers, directly or indirectly.
                       */
                      modifier onlyInitializing() {
                          _checkInitializing();
                          _;
                      }
                      /**
                       * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
                       */
                      function _checkInitializing() internal view virtual {
                          if (!_isInitializing()) {
                              revert NotInitializing();
                          }
                      }
                      /**
                       * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                       * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                       * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                       * through proxies.
                       *
                       * Emits an {Initialized} event the first time it is successfully executed.
                       */
                      function _disableInitializers() internal virtual {
                          // solhint-disable-next-line var-name-mixedcase
                          InitializableStorage storage $ = _getInitializableStorage();
                          if ($._initializing) {
                              revert InvalidInitialization();
                          }
                          if ($._initialized != type(uint64).max) {
                              $._initialized = type(uint64).max;
                              emit Initialized(type(uint64).max);
                          }
                      }
                      /**
                       * @dev Returns the highest version that has been initialized. See {reinitializer}.
                       */
                      function _getInitializedVersion() internal view returns (uint64) {
                          return _getInitializableStorage()._initialized;
                      }
                      /**
                       * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
                       */
                      function _isInitializing() internal view returns (bool) {
                          return _getInitializableStorage()._initializing;
                      }
                      /**
                       * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
                       *
                       * NOTE: Consider following the ERC-7201 formula to derive storage locations.
                       */
                      function _initializableStorageSlot() internal pure virtual returns (bytes32) {
                          return INITIALIZABLE_STORAGE;
                      }
                      /**
                       * @dev Returns a pointer to the storage namespace.
                       */
                      // solhint-disable-next-line var-name-mixedcase
                      function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
                          bytes32 slot = _initializableStorageSlot();
                          assembly {
                              $.slot := slot
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/UUPSUpgradeable.sol)
                  pragma solidity ^0.8.22;
                  import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
                  import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
                  import {Initializable} from "./Initializable.sol";
                  /**
                   * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
                   * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
                   *
                   * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
                   * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
                   * `UUPSUpgradeable` with a custom implementation of upgrades.
                   *
                   * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
                   */
                  abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
                      /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
                      address private immutable __self = address(this);
                      /**
                       * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
                       * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
                       * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
                       * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
                       * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
                       * during an upgrade.
                       */
                      string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
                      /**
                       * @dev The call is from an unauthorized context.
                       */
                      error UUPSUnauthorizedCallContext();
                      /**
                       * @dev The storage `slot` is unsupported as a UUID.
                       */
                      error UUPSUnsupportedProxiableUUID(bytes32 slot);
                      /**
                       * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
                       * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
                       * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
                       * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
                       * fail.
                       */
                      modifier onlyProxy() {
                          _checkProxy();
                          _;
                      }
                      /**
                       * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
                       * callable on the implementing contract but not through proxies.
                       */
                      modifier notDelegated() {
                          _checkNotDelegated();
                          _;
                      }
                      function __UUPSUpgradeable_init() internal onlyInitializing {
                      }
                      function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
                      }
                      /**
                       * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
                       * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
                       *
                       * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                       * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                       * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
                       */
                      function proxiableUUID() external view virtual notDelegated returns (bytes32) {
                          return ERC1967Utils.IMPLEMENTATION_SLOT;
                      }
                      /**
                       * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
                       * encoded in `data`.
                       *
                       * Calls {_authorizeUpgrade}.
                       *
                       * Emits an {Upgraded} event.
                       *
                       * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
                       */
                      function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
                          _authorizeUpgrade(newImplementation);
                          _upgradeToAndCallUUPS(newImplementation, data);
                      }
                      /**
                       * @dev Reverts if the execution is not performed via delegatecall or the execution
                       * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
                       */
                      function _checkProxy() internal view virtual {
                          if (
                              address(this) == __self || // Must be called through delegatecall
                              ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
                          ) {
                              revert UUPSUnauthorizedCallContext();
                          }
                      }
                      /**
                       * @dev Reverts if the execution is performed via delegatecall.
                       * See {notDelegated}.
                       */
                      function _checkNotDelegated() internal view virtual {
                          if (address(this) != __self) {
                              // Must not be called through delegatecall
                              revert UUPSUnauthorizedCallContext();
                          }
                      }
                      /**
                       * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
                       * {upgradeToAndCall}.
                       *
                       * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
                       *
                       * ```solidity
                       * function _authorizeUpgrade(address) internal onlyOwner {}
                       * ```
                       */
                      function _authorizeUpgrade(address newImplementation) internal virtual;
                      /**
                       * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
                       *
                       * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
                       * is expected to be the implementation slot in ERC-1967.
                       *
                       * Emits an {IERC1967-Upgraded} event.
                       */
                      function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
                          try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                              if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                                  revert UUPSUnsupportedProxiableUUID(slot);
                              }
                              ERC1967Utils.upgradeToAndCall(newImplementation, data);
                          } catch {
                              // The implementation is not UUPS
                              revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/ERC20.sol)
                  pragma solidity ^0.8.20;
                  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                  import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
                  import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
                  import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
                  import {Initializable} from "../../proxy/utils/Initializable.sol";
                  /**
                   * @dev Implementation of the {IERC20} interface.
                   *
                   * This implementation is agnostic to the way tokens are created. This means
                   * that a supply mechanism has to be added in a derived contract using {_mint}.
                   *
                   * TIP: For a detailed writeup see our guide
                   * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
                   * to implement supply mechanisms].
                   *
                   * The default value of {decimals} is 18. To change this, you should override
                   * this function so it returns a different value.
                   *
                   * We have followed general OpenZeppelin Contracts guidelines: functions revert
                   * instead returning `false` on failure. This behavior is nonetheless
                   * conventional and does not conflict with the expectations of ERC-20
                   * applications.
                   */
                  abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
                      /// @custom:storage-location erc7201:openzeppelin.storage.ERC20
                      struct ERC20Storage {
                          mapping(address account => uint256) _balances;
                          mapping(address account => mapping(address spender => uint256)) _allowances;
                          uint256 _totalSupply;
                          string _name;
                          string _symbol;
                      }
                      // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
                      bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
                      function _getERC20Storage() private pure returns (ERC20Storage storage $) {
                          assembly {
                              $.slot := ERC20StorageLocation
                          }
                      }
                      /**
                       * @dev Sets the values for {name} and {symbol}.
                       *
                       * Both values are immutable: they can only be set once during construction.
                       */
                      function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
                          __ERC20_init_unchained(name_, symbol_);
                      }
                      function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
                          ERC20Storage storage $ = _getERC20Storage();
                          $._name = name_;
                          $._symbol = symbol_;
                      }
                      /**
                       * @dev Returns the name of the token.
                       */
                      function name() public view virtual returns (string memory) {
                          ERC20Storage storage $ = _getERC20Storage();
                          return $._name;
                      }
                      /**
                       * @dev Returns the symbol of the token, usually a shorter version of the
                       * name.
                       */
                      function symbol() public view virtual returns (string memory) {
                          ERC20Storage storage $ = _getERC20Storage();
                          return $._symbol;
                      }
                      /**
                       * @dev Returns the number of decimals used to get its user representation.
                       * For example, if `decimals` equals `2`, a balance of `505` tokens should
                       * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                       *
                       * Tokens usually opt for a value of 18, imitating the relationship between
                       * Ether and Wei. This is the default value returned by this function, unless
                       * it's overridden.
                       *
                       * NOTE: This information is only used for _display_ purposes: it in
                       * no way affects any of the arithmetic of the contract, including
                       * {IERC20-balanceOf} and {IERC20-transfer}.
                       */
                      function decimals() public view virtual returns (uint8) {
                          return 18;
                      }
                      /**
                       * @dev See {IERC20-totalSupply}.
                       */
                      function totalSupply() public view virtual returns (uint256) {
                          ERC20Storage storage $ = _getERC20Storage();
                          return $._totalSupply;
                      }
                      /**
                       * @dev See {IERC20-balanceOf}.
                       */
                      function balanceOf(address account) public view virtual returns (uint256) {
                          ERC20Storage storage $ = _getERC20Storage();
                          return $._balances[account];
                      }
                      /**
                       * @dev See {IERC20-transfer}.
                       *
                       * Requirements:
                       *
                       * - `to` cannot be the zero address.
                       * - the caller must have a balance of at least `value`.
                       */
                      function transfer(address to, uint256 value) public virtual returns (bool) {
                          address owner = _msgSender();
                          _transfer(owner, to, value);
                          return true;
                      }
                      /**
                       * @dev See {IERC20-allowance}.
                       */
                      function allowance(address owner, address spender) public view virtual returns (uint256) {
                          ERC20Storage storage $ = _getERC20Storage();
                          return $._allowances[owner][spender];
                      }
                      /**
                       * @dev See {IERC20-approve}.
                       *
                       * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
                       * `transferFrom`. This is semantically equivalent to an infinite approval.
                       *
                       * Requirements:
                       *
                       * - `spender` cannot be the zero address.
                       */
                      function approve(address spender, uint256 value) public virtual returns (bool) {
                          address owner = _msgSender();
                          _approve(owner, spender, value);
                          return true;
                      }
                      /**
                       * @dev See {IERC20-transferFrom}.
                       *
                       * Skips emitting an {Approval} event indicating an allowance update. This is not
                       * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
                       *
                       * NOTE: Does not update the allowance if the current allowance
                       * is the maximum `uint256`.
                       *
                       * Requirements:
                       *
                       * - `from` and `to` cannot be the zero address.
                       * - `from` must have a balance of at least `value`.
                       * - the caller must have allowance for ``from``'s tokens of at least
                       * `value`.
                       */
                      function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
                          address spender = _msgSender();
                          _spendAllowance(from, spender, value);
                          _transfer(from, to, value);
                          return true;
                      }
                      /**
                       * @dev Moves a `value` amount of tokens from `from` to `to`.
                       *
                       * This internal function is equivalent to {transfer}, and can be used to
                       * e.g. implement automatic token fees, slashing mechanisms, etc.
                       *
                       * Emits a {Transfer} event.
                       *
                       * NOTE: This function is not virtual, {_update} should be overridden instead.
                       */
                      function _transfer(address from, address to, uint256 value) internal {
                          if (from == address(0)) {
                              revert ERC20InvalidSender(address(0));
                          }
                          if (to == address(0)) {
                              revert ERC20InvalidReceiver(address(0));
                          }
                          _update(from, to, value);
                      }
                      /**
                       * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
                       * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
                       * this function.
                       *
                       * Emits a {Transfer} event.
                       */
                      function _update(address from, address to, uint256 value) internal virtual {
                          ERC20Storage storage $ = _getERC20Storage();
                          if (from == address(0)) {
                              // Overflow check required: The rest of the code assumes that totalSupply never overflows
                              $._totalSupply += value;
                          } else {
                              uint256 fromBalance = $._balances[from];
                              if (fromBalance < value) {
                                  revert ERC20InsufficientBalance(from, fromBalance, value);
                              }
                              unchecked {
                                  // Overflow not possible: value <= fromBalance <= totalSupply.
                                  $._balances[from] = fromBalance - value;
                              }
                          }
                          if (to == address(0)) {
                              unchecked {
                                  // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                                  $._totalSupply -= value;
                              }
                          } else {
                              unchecked {
                                  // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                                  $._balances[to] += value;
                              }
                          }
                          emit Transfer(from, to, value);
                      }
                      /**
                       * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
                       * Relies on the `_update` mechanism
                       *
                       * Emits a {Transfer} event with `from` set to the zero address.
                       *
                       * NOTE: This function is not virtual, {_update} should be overridden instead.
                       */
                      function _mint(address account, uint256 value) internal {
                          if (account == address(0)) {
                              revert ERC20InvalidReceiver(address(0));
                          }
                          _update(address(0), account, value);
                      }
                      /**
                       * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
                       * Relies on the `_update` mechanism.
                       *
                       * Emits a {Transfer} event with `to` set to the zero address.
                       *
                       * NOTE: This function is not virtual, {_update} should be overridden instead
                       */
                      function _burn(address account, uint256 value) internal {
                          if (account == address(0)) {
                              revert ERC20InvalidSender(address(0));
                          }
                          _update(account, address(0), value);
                      }
                      /**
                       * @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
                       *
                       * This internal function is equivalent to `approve`, and can be used to
                       * e.g. set automatic allowances for certain subsystems, etc.
                       *
                       * Emits an {Approval} event.
                       *
                       * Requirements:
                       *
                       * - `owner` cannot be the zero address.
                       * - `spender` cannot be the zero address.
                       *
                       * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
                       */
                      function _approve(address owner, address spender, uint256 value) internal {
                          _approve(owner, spender, value, true);
                      }
                      /**
                       * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
                       *
                       * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
                       * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
                       * `Approval` event during `transferFrom` operations.
                       *
                       * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
                       * true using the following override:
                       *
                       * ```solidity
                       * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
                       *     super._approve(owner, spender, value, true);
                       * }
                       * ```
                       *
                       * Requirements are the same as {_approve}.
                       */
                      function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
                          ERC20Storage storage $ = _getERC20Storage();
                          if (owner == address(0)) {
                              revert ERC20InvalidApprover(address(0));
                          }
                          if (spender == address(0)) {
                              revert ERC20InvalidSpender(address(0));
                          }
                          $._allowances[owner][spender] = value;
                          if (emitEvent) {
                              emit Approval(owner, spender, value);
                          }
                      }
                      /**
                       * @dev Updates `owner`'s allowance for `spender` based on spent `value`.
                       *
                       * Does not update the allowance value in case of infinite allowance.
                       * Revert if not enough allowance is available.
                       *
                       * Does not emit an {Approval} event.
                       */
                      function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
                          uint256 currentAllowance = allowance(owner, spender);
                          if (currentAllowance < type(uint256).max) {
                              if (currentAllowance < value) {
                                  revert ERC20InsufficientAllowance(spender, currentAllowance, value);
                              }
                              unchecked {
                                  _approve(owner, spender, currentAllowance - value, false);
                              }
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
                  pragma solidity ^0.8.20;
                  import {Initializable} from "../proxy/utils/Initializable.sol";
                  /**
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract ContextUpgradeable is Initializable {
                      function __Context_init() internal onlyInitializing {
                      }
                      function __Context_init_unchained() internal onlyInitializing {
                      }
                      function _msgSender() internal view virtual returns (address) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes calldata) {
                          return msg.data;
                      }
                      function _contextSuffixLength() internal view virtual returns (uint256) {
                          return 0;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
                  pragma solidity ^0.8.20;
                  import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
                  import {Initializable} from "../../proxy/utils/Initializable.sol";
                  /**
                   * @dev Implementation of the {IERC165} interface.
                   *
                   * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
                   * for the additional interface id that will be supported. For example:
                   *
                   * ```solidity
                   * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                   *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
                   * }
                   * ```
                   */
                  abstract contract ERC165Upgradeable is Initializable, IERC165 {
                      function __ERC165_init() internal onlyInitializing {
                      }
                      function __ERC165_init_unchained() internal onlyInitializing {
                      }
                      /**
                       * @dev See {IERC165-supportsInterface}.
                       */
                      function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                          return interfaceId == type(IERC165).interfaceId;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)
                  pragma solidity ^0.8.20;
                  import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
                  import {Initializable} from "../proxy/utils/Initializable.sol";
                  /**
                   * @dev Contract module which allows children to implement an emergency stop
                   * mechanism that can be triggered by an authorized account.
                   *
                   * This module is used through inheritance. It will make available the
                   * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
                   * the functions of your contract. Note that they will not be pausable by
                   * simply including this module, only once the modifiers are put in place.
                   */
                  abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
                      /// @custom:storage-location erc7201:openzeppelin.storage.Pausable
                      struct PausableStorage {
                          bool _paused;
                      }
                      // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
                      bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
                      function _getPausableStorage() private pure returns (PausableStorage storage $) {
                          assembly {
                              $.slot := PausableStorageLocation
                          }
                      }
                      /**
                       * @dev Emitted when the pause is triggered by `account`.
                       */
                      event Paused(address account);
                      /**
                       * @dev Emitted when the pause is lifted by `account`.
                       */
                      event Unpaused(address account);
                      /**
                       * @dev The operation failed because the contract is paused.
                       */
                      error EnforcedPause();
                      /**
                       * @dev The operation failed because the contract is not paused.
                       */
                      error ExpectedPause();
                      /**
                       * @dev Modifier to make a function callable only when the contract is not paused.
                       *
                       * Requirements:
                       *
                       * - The contract must not be paused.
                       */
                      modifier whenNotPaused() {
                          _requireNotPaused();
                          _;
                      }
                      /**
                       * @dev Modifier to make a function callable only when the contract is paused.
                       *
                       * Requirements:
                       *
                       * - The contract must be paused.
                       */
                      modifier whenPaused() {
                          _requirePaused();
                          _;
                      }
                      function __Pausable_init() internal onlyInitializing {
                      }
                      function __Pausable_init_unchained() internal onlyInitializing {
                      }
                      /**
                       * @dev Returns true if the contract is paused, and false otherwise.
                       */
                      function paused() public view virtual returns (bool) {
                          PausableStorage storage $ = _getPausableStorage();
                          return $._paused;
                      }
                      /**
                       * @dev Throws if the contract is paused.
                       */
                      function _requireNotPaused() internal view virtual {
                          if (paused()) {
                              revert EnforcedPause();
                          }
                      }
                      /**
                       * @dev Throws if the contract is not paused.
                       */
                      function _requirePaused() internal view virtual {
                          if (!paused()) {
                              revert ExpectedPause();
                          }
                      }
                      /**
                       * @dev Triggers stopped state.
                       *
                       * Requirements:
                       *
                       * - The contract must not be paused.
                       */
                      function _pause() internal virtual whenNotPaused {
                          PausableStorage storage $ = _getPausableStorage();
                          $._paused = true;
                          emit Paused(_msgSender());
                      }
                      /**
                       * @dev Returns to normal state.
                       *
                       * Requirements:
                       *
                       * - The contract must be paused.
                       */
                      function _unpause() internal virtual whenPaused {
                          PausableStorage storage $ = _getPausableStorage();
                          $._paused = false;
                          emit Unpaused(_msgSender());
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
                  pragma solidity ^0.8.20;
                  import {Initializable} from "../proxy/utils/Initializable.sol";
                  /**
                   * @dev Contract module that helps prevent reentrant calls to a function.
                   *
                   * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
                   * available, which can be applied to functions to make sure there are no nested
                   * (reentrant) calls to them.
                   *
                   * Note that because there is a single `nonReentrant` guard, functions marked as
                   * `nonReentrant` may not call one another. This can be worked around by making
                   * those functions `private`, and then adding `external` `nonReentrant` entry
                   * points to them.
                   *
                   * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
                   * consider using {ReentrancyGuardTransient} instead.
                   *
                   * TIP: If you would like to learn more about reentrancy and alternative ways
                   * to protect against it, check out our blog post
                   * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
                   */
                  abstract contract ReentrancyGuardUpgradeable is Initializable {
                      // Booleans are more expensive than uint256 or any type that takes up a full
                      // word because each write operation emits an extra SLOAD to first read the
                      // slot's contents, replace the bits taken up by the boolean, and then write
                      // back. This is the compiler's defense against contract upgrades and
                      // pointer aliasing, and it cannot be disabled.
                      // The values being non-zero value makes deployment a bit more expensive,
                      // but in exchange the refund on every call to nonReentrant will be lower in
                      // amount. Since refunds are capped to a percentage of the total
                      // transaction's gas, it is best to keep them low in cases like this one, to
                      // increase the likelihood of the full refund coming into effect.
                      uint256 private constant NOT_ENTERED = 1;
                      uint256 private constant ENTERED = 2;
                      /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
                      struct ReentrancyGuardStorage {
                          uint256 _status;
                      }
                      // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
                      bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
                      function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
                          assembly {
                              $.slot := ReentrancyGuardStorageLocation
                          }
                      }
                      /**
                       * @dev Unauthorized reentrant call.
                       */
                      error ReentrancyGuardReentrantCall();
                      function __ReentrancyGuard_init() internal onlyInitializing {
                          __ReentrancyGuard_init_unchained();
                      }
                      function __ReentrancyGuard_init_unchained() internal onlyInitializing {
                          ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                          $._status = NOT_ENTERED;
                      }
                      /**
                       * @dev Prevents a contract from calling itself, directly or indirectly.
                       * Calling a `nonReentrant` function from another `nonReentrant`
                       * function is not supported. It is possible to prevent this from happening
                       * by making the `nonReentrant` function external, and making it call a
                       * `private` function that does the actual work.
                       */
                      modifier nonReentrant() {
                          _nonReentrantBefore();
                          _;
                          _nonReentrantAfter();
                      }
                      function _nonReentrantBefore() private {
                          ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                          // On the first call to nonReentrant, _status will be NOT_ENTERED
                          if ($._status == ENTERED) {
                              revert ReentrancyGuardReentrantCall();
                          }
                          // Any calls to nonReentrant after this point will fail
                          $._status = ENTERED;
                      }
                      function _nonReentrantAfter() private {
                          ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                          // By storing the original value once again, a refund is triggered (see
                          // https://eips.ethereum.org/EIPS/eip-2200)
                          $._status = NOT_ENTERED;
                      }
                      /**
                       * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
                       * `nonReentrant` function in the call stack.
                       */
                      function _reentrancyGuardEntered() internal view returns (bool) {
                          ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                          return $._status == ENTERED;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev External interface of AccessControl declared to support ERC-165 detection.
                   */
                  interface IAccessControl {
                      /**
                       * @dev The `account` is missing a role.
                       */
                      error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
                      /**
                       * @dev The caller of a function is not the expected one.
                       *
                       * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
                       */
                      error AccessControlBadConfirmation();
                      /**
                       * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                       *
                       * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                       * {RoleAdminChanged} not being emitted signaling this.
                       */
                      event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                      /**
                       * @dev Emitted when `account` is granted `role`.
                       *
                       * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
                       * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
                       */
                      event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Emitted when `account` is revoked `role`.
                       *
                       * `sender` is the account that originated the contract call:
                       *   - if using `revokeRole`, it is the admin role bearer
                       *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                       */
                      event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Returns `true` if `account` has been granted `role`.
                       */
                      function hasRole(bytes32 role, address account) external view returns (bool);
                      /**
                       * @dev Returns the admin role that controls `role`. See {grantRole} and
                       * {revokeRole}.
                       *
                       * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                       */
                      function getRoleAdmin(bytes32 role) external view returns (bytes32);
                      /**
                       * @dev Grants `role` to `account`.
                       *
                       * If `account` had not been already granted `role`, emits a {RoleGranted}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function grantRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from `account`.
                       *
                       * If `account` had been granted `role`, emits a {RoleRevoked} event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function revokeRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from the calling account.
                       *
                       * Roles are often managed via {grantRole} and {revokeRole}: this function's
                       * purpose is to provide a mechanism for accounts to lose their privileges
                       * if they are compromised (such as when a trusted device is misplaced).
                       *
                       * If the calling account had been granted `role`, emits a {RoleRevoked}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must be `callerConfirmation`.
                       */
                      function renounceRole(bytes32 role, address callerConfirmation) external;
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC1822.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
                   * proxy whose upgrades are fully controlled by the current implementation.
                   */
                  interface IERC1822Proxiable {
                      /**
                       * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                       * address.
                       *
                       * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                       * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                       * function revert if invoked through a proxy.
                       */
                      function proxiableUUID() external view returns (bytes32);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev Standard ERC-20 Errors
                   * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
                   */
                  interface IERC20Errors {
                      /**
                       * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
                       * @param sender Address whose tokens are being transferred.
                       * @param balance Current balance for the interacting account.
                       * @param needed Minimum amount required to perform a transfer.
                       */
                      error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
                      /**
                       * @dev Indicates a failure with the token `sender`. Used in transfers.
                       * @param sender Address whose tokens are being transferred.
                       */
                      error ERC20InvalidSender(address sender);
                      /**
                       * @dev Indicates a failure with the token `receiver`. Used in transfers.
                       * @param receiver Address to which tokens are being transferred.
                       */
                      error ERC20InvalidReceiver(address receiver);
                      /**
                       * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
                       * @param spender Address that may be allowed to operate on tokens without being their owner.
                       * @param allowance Amount of tokens a `spender` is allowed to operate with.
                       * @param needed Minimum amount required to perform a transfer.
                       */
                      error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
                      /**
                       * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
                       * @param approver Address initiating an approval operation.
                       */
                      error ERC20InvalidApprover(address approver);
                      /**
                       * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
                       * @param spender Address that may be allowed to operate on tokens without being their owner.
                       */
                      error ERC20InvalidSpender(address spender);
                  }
                  /**
                   * @dev Standard ERC-721 Errors
                   * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
                   */
                  interface IERC721Errors {
                      /**
                       * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
                       * Used in balance queries.
                       * @param owner Address of the current owner of a token.
                       */
                      error ERC721InvalidOwner(address owner);
                      /**
                       * @dev Indicates a `tokenId` whose `owner` is the zero address.
                       * @param tokenId Identifier number of a token.
                       */
                      error ERC721NonexistentToken(uint256 tokenId);
                      /**
                       * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
                       * @param sender Address whose tokens are being transferred.
                       * @param tokenId Identifier number of a token.
                       * @param owner Address of the current owner of a token.
                       */
                      error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
                      /**
                       * @dev Indicates a failure with the token `sender`. Used in transfers.
                       * @param sender Address whose tokens are being transferred.
                       */
                      error ERC721InvalidSender(address sender);
                      /**
                       * @dev Indicates a failure with the token `receiver`. Used in transfers.
                       * @param receiver Address to which tokens are being transferred.
                       */
                      error ERC721InvalidReceiver(address receiver);
                      /**
                       * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
                       * @param operator Address that may be allowed to operate on tokens without being their owner.
                       * @param tokenId Identifier number of a token.
                       */
                      error ERC721InsufficientApproval(address operator, uint256 tokenId);
                      /**
                       * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
                       * @param approver Address initiating an approval operation.
                       */
                      error ERC721InvalidApprover(address approver);
                      /**
                       * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
                       * @param operator Address that may be allowed to operate on tokens without being their owner.
                       */
                      error ERC721InvalidOperator(address operator);
                  }
                  /**
                   * @dev Standard ERC-1155 Errors
                   * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
                   */
                  interface IERC1155Errors {
                      /**
                       * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
                       * @param sender Address whose tokens are being transferred.
                       * @param balance Current balance for the interacting account.
                       * @param needed Minimum amount required to perform a transfer.
                       * @param tokenId Identifier number of a token.
                       */
                      error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
                      /**
                       * @dev Indicates a failure with the token `sender`. Used in transfers.
                       * @param sender Address whose tokens are being transferred.
                       */
                      error ERC1155InvalidSender(address sender);
                      /**
                       * @dev Indicates a failure with the token `receiver`. Used in transfers.
                       * @param receiver Address to which tokens are being transferred.
                       */
                      error ERC1155InvalidReceiver(address receiver);
                      /**
                       * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
                       * @param operator Address that may be allowed to operate on tokens without being their owner.
                       * @param owner Address of the current owner of a token.
                       */
                      error ERC1155MissingApprovalForAll(address operator, address owner);
                      /**
                       * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
                       * @param approver Address initiating an approval operation.
                       */
                      error ERC1155InvalidApprover(address approver);
                      /**
                       * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
                       * @param operator Address that may be allowed to operate on tokens without being their owner.
                       */
                      error ERC1155InvalidOperator(address operator);
                      /**
                       * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
                       * Used in batch transfers.
                       * @param idsLength Length of the array of token identifiers
                       * @param valuesLength Length of the array of token amounts
                       */
                      error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
                  pragma solidity ^0.8.20;
                  import {IERC20} from "./IERC20.sol";
                  import {IERC165} from "./IERC165.sol";
                  /**
                   * @title IERC1363
                   * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
                   *
                   * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
                   * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
                   */
                  interface IERC1363 is IERC20, IERC165 {
                      /*
                       * Note: the ERC-165 identifier for this interface is 0xb0202a11.
                       * 0xb0202a11 ===
                       *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
                       *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
                       *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
                       *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
                       *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
                       *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
                       */
                      /**
                       * @dev Moves a `value` amount of tokens from the caller's account to `to`
                       * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
                       * @param to The address which you want to transfer to.
                       * @param value The amount of tokens to be transferred.
                       * @return A boolean value indicating whether the operation succeeded unless throwing.
                       */
                      function transferAndCall(address to, uint256 value) external returns (bool);
                      /**
                       * @dev Moves a `value` amount of tokens from the caller's account to `to`
                       * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
                       * @param to The address which you want to transfer to.
                       * @param value The amount of tokens to be transferred.
                       * @param data Additional data with no specified format, sent in call to `to`.
                       * @return A boolean value indicating whether the operation succeeded unless throwing.
                       */
                      function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
                      /**
                       * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
                       * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
                       * @param from The address which you want to send tokens from.
                       * @param to The address which you want to transfer to.
                       * @param value The amount of tokens to be transferred.
                       * @return A boolean value indicating whether the operation succeeded unless throwing.
                       */
                      function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
                      /**
                       * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
                       * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
                       * @param from The address which you want to send tokens from.
                       * @param to The address which you want to transfer to.
                       * @param value The amount of tokens to be transferred.
                       * @param data Additional data with no specified format, sent in call to `to`.
                       * @return A boolean value indicating whether the operation succeeded unless throwing.
                       */
                      function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
                      /**
                       * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
                       * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
                       * @param spender The address which will spend the funds.
                       * @param value The amount of tokens to be spent.
                       * @return A boolean value indicating whether the operation succeeded unless throwing.
                       */
                      function approveAndCall(address spender, uint256 value) external returns (bool);
                      /**
                       * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
                       * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
                       * @param spender The address which will spend the funds.
                       * @param value The amount of tokens to be spent.
                       * @param data Additional data with no specified format, sent in call to `spender`.
                       * @return A boolean value indicating whether the operation succeeded unless throwing.
                       */
                      function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
                  pragma solidity ^0.8.20;
                  import {IERC165} from "../utils/introspection/IERC165.sol";
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
                   */
                  interface IERC1967 {
                      /**
                       * @dev Emitted when the implementation is upgraded.
                       */
                      event Upgraded(address indexed implementation);
                      /**
                       * @dev Emitted when the admin account has changed.
                       */
                      event AdminChanged(address previousAdmin, address newAdmin);
                      /**
                       * @dev Emitted when the beacon is changed.
                       */
                      event BeaconUpgraded(address indexed beacon);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
                  pragma solidity ^0.8.20;
                  import {IERC20} from "../token/ERC20/IERC20.sol";
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev This is the interface that {BeaconProxy} expects of its beacon.
                   */
                  interface IBeacon {
                      /**
                       * @dev Must return an address that can be used as a delegate call target.
                       *
                       * {UpgradeableBeacon} will check that this address is a contract.
                       */
                      function implementation() external view returns (address);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (proxy/ERC1967/ERC1967Utils.sol)
                  pragma solidity ^0.8.21;
                  import {IBeacon} from "../beacon/IBeacon.sol";
                  import {IERC1967} from "../../interfaces/IERC1967.sol";
                  import {Address} from "../../utils/Address.sol";
                  import {StorageSlot} from "../../utils/StorageSlot.sol";
                  /**
                   * @dev This library provides getters and event emitting update functions for
                   * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
                   */
                  library ERC1967Utils {
                      /**
                       * @dev Storage slot with the address of the current implementation.
                       * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
                       */
                      // solhint-disable-next-line private-vars-leading-underscore
                      bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                      /**
                       * @dev The `implementation` of the proxy is invalid.
                       */
                      error ERC1967InvalidImplementation(address implementation);
                      /**
                       * @dev The `admin` of the proxy is invalid.
                       */
                      error ERC1967InvalidAdmin(address admin);
                      /**
                       * @dev The `beacon` of the proxy is invalid.
                       */
                      error ERC1967InvalidBeacon(address beacon);
                      /**
                       * @dev An upgrade function sees `msg.value > 0` that may be lost.
                       */
                      error ERC1967NonPayable();
                      /**
                       * @dev Returns the current implementation address.
                       */
                      function getImplementation() internal view returns (address) {
                          return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
                      }
                      /**
                       * @dev Stores a new address in the ERC-1967 implementation slot.
                       */
                      function _setImplementation(address newImplementation) private {
                          if (newImplementation.code.length == 0) {
                              revert ERC1967InvalidImplementation(newImplementation);
                          }
                          StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
                      }
                      /**
                       * @dev Performs implementation upgrade with additional setup call if data is nonempty.
                       * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                       * to avoid stuck value in the contract.
                       *
                       * Emits an {IERC1967-Upgraded} event.
                       */
                      function upgradeToAndCall(address newImplementation, bytes memory data) internal {
                          _setImplementation(newImplementation);
                          emit IERC1967.Upgraded(newImplementation);
                          if (data.length > 0) {
                              Address.functionDelegateCall(newImplementation, data);
                          } else {
                              _checkNonPayable();
                          }
                      }
                      /**
                       * @dev Storage slot with the admin of the contract.
                       * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
                       */
                      // solhint-disable-next-line private-vars-leading-underscore
                      bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                      /**
                       * @dev Returns the current admin.
                       *
                       * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
                       * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                       * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                       */
                      function getAdmin() internal view returns (address) {
                          return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
                      }
                      /**
                       * @dev Stores a new address in the ERC-1967 admin slot.
                       */
                      function _setAdmin(address newAdmin) private {
                          if (newAdmin == address(0)) {
                              revert ERC1967InvalidAdmin(address(0));
                          }
                          StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
                      }
                      /**
                       * @dev Changes the admin of the proxy.
                       *
                       * Emits an {IERC1967-AdminChanged} event.
                       */
                      function changeAdmin(address newAdmin) internal {
                          emit IERC1967.AdminChanged(getAdmin(), newAdmin);
                          _setAdmin(newAdmin);
                      }
                      /**
                       * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                       * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
                       */
                      // solhint-disable-next-line private-vars-leading-underscore
                      bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                      /**
                       * @dev Returns the current beacon.
                       */
                      function getBeacon() internal view returns (address) {
                          return StorageSlot.getAddressSlot(BEACON_SLOT).value;
                      }
                      /**
                       * @dev Stores a new beacon in the ERC-1967 beacon slot.
                       */
                      function _setBeacon(address newBeacon) private {
                          if (newBeacon.code.length == 0) {
                              revert ERC1967InvalidBeacon(newBeacon);
                          }
                          StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
                          address beaconImplementation = IBeacon(newBeacon).implementation();
                          if (beaconImplementation.code.length == 0) {
                              revert ERC1967InvalidImplementation(beaconImplementation);
                          }
                      }
                      /**
                       * @dev Change the beacon and trigger a setup call if data is nonempty.
                       * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                       * to avoid stuck value in the contract.
                       *
                       * Emits an {IERC1967-BeaconUpgraded} event.
                       *
                       * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
                       * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
                       * efficiency.
                       */
                      function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
                          _setBeacon(newBeacon);
                          emit IERC1967.BeaconUpgraded(newBeacon);
                          if (data.length > 0) {
                              Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                          } else {
                              _checkNonPayable();
                          }
                      }
                      /**
                       * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
                       * if an upgrade doesn't perform an initialization call.
                       */
                      function _checkNonPayable() private {
                          if (msg.value > 0) {
                              revert ERC1967NonPayable();
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
                  pragma solidity ^0.8.20;
                  import {IERC20} from "../IERC20.sol";
                  /**
                   * @dev Interface for the optional metadata functions from the ERC-20 standard.
                   */
                  interface IERC20Metadata is IERC20 {
                      /**
                       * @dev Returns the name of the token.
                       */
                      function name() external view returns (string memory);
                      /**
                       * @dev Returns the symbol of the token.
                       */
                      function symbol() external view returns (string memory);
                      /**
                       * @dev Returns the decimals places of the token.
                       */
                      function decimals() external view returns (uint8);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev Interface of the ERC-20 standard as defined in the ERC.
                   */
                  interface IERC20 {
                      /**
                       * @dev Emitted when `value` tokens are moved from one account (`from`) to
                       * another (`to`).
                       *
                       * Note that `value` may be zero.
                       */
                      event Transfer(address indexed from, address indexed to, uint256 value);
                      /**
                       * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                       * a call to {approve}. `value` is the new allowance.
                       */
                      event Approval(address indexed owner, address indexed spender, uint256 value);
                      /**
                       * @dev Returns the value of tokens in existence.
                       */
                      function totalSupply() external view returns (uint256);
                      /**
                       * @dev Returns the value of tokens owned by `account`.
                       */
                      function balanceOf(address account) external view returns (uint256);
                      /**
                       * @dev Moves a `value` amount of tokens from the caller's account to `to`.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transfer(address to, uint256 value) external returns (bool);
                      /**
                       * @dev Returns the remaining number of tokens that `spender` will be
                       * allowed to spend on behalf of `owner` through {transferFrom}. This is
                       * zero by default.
                       *
                       * This value changes when {approve} or {transferFrom} are called.
                       */
                      function allowance(address owner, address spender) external view returns (uint256);
                      /**
                       * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
                       * caller's tokens.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * IMPORTANT: Beware that changing an allowance with this method brings the risk
                       * that someone may use both the old and the new allowance by unfortunate
                       * transaction ordering. One possible solution to mitigate this race
                       * condition is to first reduce the spender's allowance to 0 and set the
                       * desired value afterwards:
                       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                       *
                       * Emits an {Approval} event.
                       */
                      function approve(address spender, uint256 value) external returns (bool);
                      /**
                       * @dev Moves a `value` amount of tokens from `from` to `to` using the
                       * allowance mechanism. `value` is then deducted from the caller's
                       * allowance.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transferFrom(address from, address to, uint256 value) external returns (bool);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol)
                  pragma solidity ^0.8.20;
                  import {IERC20} from "../IERC20.sol";
                  import {IERC1363} from "../../../interfaces/IERC1363.sol";
                  import {Address} from "../../../utils/Address.sol";
                  /**
                   * @title SafeERC20
                   * @dev Wrappers around ERC-20 operations that throw on failure (when the token
                   * contract returns false). Tokens that return no value (and instead revert or
                   * throw on failure) are also supported, non-reverting calls are assumed to be
                   * successful.
                   * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                   * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                   */
                  library SafeERC20 {
                      /**
                       * @dev An operation with an ERC-20 token failed.
                       */
                      error SafeERC20FailedOperation(address token);
                      /**
                       * @dev Indicates a failed `decreaseAllowance` request.
                       */
                      error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
                      /**
                       * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                       * non-reverting calls are assumed to be successful.
                       */
                      function safeTransfer(IERC20 token, address to, uint256 value) internal {
                          _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
                      }
                      /**
                       * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                       * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                       */
                      function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                          _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
                      }
                      /**
                       * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                       * non-reverting calls are assumed to be successful.
                       *
                       * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
                       * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
                       * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
                       * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
                       */
                      function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                          uint256 oldAllowance = token.allowance(address(this), spender);
                          forceApprove(token, spender, oldAllowance + value);
                      }
                      /**
                       * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
                       * value, non-reverting calls are assumed to be successful.
                       *
                       * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
                       * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
                       * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
                       * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
                       */
                      function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
                          unchecked {
                              uint256 currentAllowance = token.allowance(address(this), spender);
                              if (currentAllowance < requestedDecrease) {
                                  revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                              }
                              forceApprove(token, spender, currentAllowance - requestedDecrease);
                          }
                      }
                      /**
                       * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                       * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
                       * to be set to zero before setting it to a non-zero value, such as USDT.
                       *
                       * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
                       * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
                       * set here.
                       */
                      function forceApprove(IERC20 token, address spender, uint256 value) internal {
                          bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
                          if (!_callOptionalReturnBool(token, approvalCall)) {
                              _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                              _callOptionalReturn(token, approvalCall);
                          }
                      }
                      /**
                       * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
                       * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
                       * targeting contracts.
                       *
                       * Reverts if the returned value is other than `true`.
                       */
                      function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
                          if (to.code.length == 0) {
                              safeTransfer(token, to, value);
                          } else if (!token.transferAndCall(to, value, data)) {
                              revert SafeERC20FailedOperation(address(token));
                          }
                      }
                      /**
                       * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
                       * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
                       * targeting contracts.
                       *
                       * Reverts if the returned value is other than `true`.
                       */
                      function transferFromAndCallRelaxed(
                          IERC1363 token,
                          address from,
                          address to,
                          uint256 value,
                          bytes memory data
                      ) internal {
                          if (to.code.length == 0) {
                              safeTransferFrom(token, from, to, value);
                          } else if (!token.transferFromAndCall(from, to, value, data)) {
                              revert SafeERC20FailedOperation(address(token));
                          }
                      }
                      /**
                       * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
                       * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
                       * targeting contracts.
                       *
                       * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
                       * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
                       * once without retrying, and relies on the returned value to be true.
                       *
                       * Reverts if the returned value is other than `true`.
                       */
                      function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
                          if (to.code.length == 0) {
                              forceApprove(token, to, value);
                          } else if (!token.approveAndCall(to, value, data)) {
                              revert SafeERC20FailedOperation(address(token));
                          }
                      }
                      /**
                       * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                       * on the return value: the return value is optional (but if data is returned, it must not be false).
                       * @param token The token targeted by the call.
                       * @param data The call data (encoded using abi.encode or one of its variants).
                       *
                       * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
                       */
                      function _callOptionalReturn(IERC20 token, bytes memory data) private {
                          uint256 returnSize;
                          uint256 returnValue;
                          assembly ("memory-safe") {
                              let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
                              // bubble errors
                              if iszero(success) {
                                  let ptr := mload(0x40)
                                  returndatacopy(ptr, 0, returndatasize())
                                  revert(ptr, returndatasize())
                              }
                              returnSize := returndatasize()
                              returnValue := mload(0)
                          }
                          if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
                              revert SafeERC20FailedOperation(address(token));
                          }
                      }
                      /**
                       * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                       * on the return value: the return value is optional (but if data is returned, it must not be false).
                       * @param token The token targeted by the call.
                       * @param data The call data (encoded using abi.encode or one of its variants).
                       *
                       * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
                       */
                      function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                          bool success;
                          uint256 returnSize;
                          uint256 returnValue;
                          assembly ("memory-safe") {
                              success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
                              returnSize := returndatasize()
                              returnValue := mload(0)
                          }
                          return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)
                  pragma solidity ^0.8.20;
                  import {Errors} from "./Errors.sol";
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library Address {
                      /**
                       * @dev There's no code at `target` (it is not a contract).
                       */
                      error AddressEmptyCode(address target);
                      /**
                       * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                       * `recipient`, forwarding all available gas and reverting on errors.
                       *
                       * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                       * of certain opcodes, possibly making contracts go over the 2300 gas limit
                       * imposed by `transfer`, making them unable to receive funds via
                       * `transfer`. {sendValue} removes this limitation.
                       *
                       * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                       */
                      function sendValue(address payable recipient, uint256 amount) internal {
                          if (address(this).balance < amount) {
                              revert Errors.InsufficientBalance(address(this).balance, amount);
                          }
                          (bool success, ) = recipient.call{value: amount}("");
                          if (!success) {
                              revert Errors.FailedCall();
                          }
                      }
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain `call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason or custom error, it is bubbled
                       * up by this function (like regular Solidity function calls). However, if
                       * the call reverted with no returned reason, this function reverts with a
                       * {Errors.FailedCall} error.
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, 0);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but also transferring `value` wei to `target`.
                       *
                       * Requirements:
                       *
                       * - the calling contract must have an ETH balance of at least `value`.
                       * - the called Solidity function must be `payable`.
                       */
                      function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                          if (address(this).balance < value) {
                              revert Errors.InsufficientBalance(address(this).balance, value);
                          }
                          (bool success, bytes memory returndata) = target.call{value: value}(data);
                          return verifyCallResultFromTarget(target, success, returndata);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a static call.
                       */
                      function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return verifyCallResultFromTarget(target, success, returndata);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a delegate call.
                       */
                      function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                          (bool success, bytes memory returndata) = target.delegatecall(data);
                          return verifyCallResultFromTarget(target, success, returndata);
                      }
                      /**
                       * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
                       * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
                       * of an unsuccessful call.
                       */
                      function verifyCallResultFromTarget(
                          address target,
                          bool success,
                          bytes memory returndata
                      ) internal view returns (bytes memory) {
                          if (!success) {
                              _revert(returndata);
                          } else {
                              // only check if target is a contract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              if (returndata.length == 0 && target.code.length == 0) {
                                  revert AddressEmptyCode(target);
                              }
                              return returndata;
                          }
                      }
                      /**
                       * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
                       * revert reason or with a default {Errors.FailedCall} error.
                       */
                      function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                          if (!success) {
                              _revert(returndata);
                          } else {
                              return returndata;
                          }
                      }
                      /**
                       * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
                       */
                      function _revert(bytes memory returndata) private pure {
                          // Look for revert reason and bubble it up if present
                          if (returndata.length > 0) {
                              // The easiest way to bubble the revert reason is using memory via assembly
                              assembly ("memory-safe") {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert Errors.FailedCall();
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev Collection of common custom errors used in multiple contracts
                   *
                   * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
                   * It is recommended to avoid relying on the error API for critical functionality.
                   *
                   * _Available since v5.1._
                   */
                  library Errors {
                      /**
                       * @dev The ETH balance of the account is not enough to perform the operation.
                       */
                      error InsufficientBalance(uint256 balance, uint256 needed);
                      /**
                       * @dev A call to an address target failed. The target may have reverted.
                       */
                      error FailedCall();
                      /**
                       * @dev The deployment failed.
                       */
                      error FailedDeployment();
                      /**
                       * @dev A necessary precompile is missing.
                       */
                      error MissingPrecompile(address);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
                  pragma solidity ^0.8.20;
                  /**
                   * @dev Interface of the ERC-165 standard, as defined in the
                   * https://eips.ethereum.org/EIPS/eip-165[ERC].
                   *
                   * Implementers can declare support of contract interfaces, which can then be
                   * queried by others ({ERC165Checker}).
                   *
                   * For an implementation, see {ERC165}.
                   */
                  interface IERC165 {
                      /**
                       * @dev Returns true if this contract implements the interface defined by
                       * `interfaceId`. See the corresponding
                       * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
                       * to learn more about how these ids are created.
                       *
                       * This function call must use less than 30 000 gas.
                       */
                      function supportsInterface(bytes4 interfaceId) external view returns (bool);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
                  // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
                  pragma solidity ^0.8.20;
                  /**
                   * @dev Library for reading and writing primitive types to specific storage slots.
                   *
                   * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
                   * This library helps with reading and writing to such slots without the need for inline assembly.
                   *
                   * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
                   *
                   * Example usage to set ERC-1967 implementation slot:
                   * ```solidity
                   * contract ERC1967 {
                   *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
                   *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                   *
                   *     function _getImplementation() internal view returns (address) {
                   *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                   *     }
                   *
                   *     function _setImplementation(address newImplementation) internal {
                   *         require(newImplementation.code.length > 0);
                   *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                   *     }
                   * }
                   * ```
                   *
                   * TIP: Consider using this library along with {SlotDerivation}.
                   */
                  library StorageSlot {
                      struct AddressSlot {
                          address value;
                      }
                      struct BooleanSlot {
                          bool value;
                      }
                      struct Bytes32Slot {
                          bytes32 value;
                      }
                      struct Uint256Slot {
                          uint256 value;
                      }
                      struct Int256Slot {
                          int256 value;
                      }
                      struct StringSlot {
                          string value;
                      }
                      struct BytesSlot {
                          bytes value;
                      }
                      /**
                       * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                       */
                      function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns a `BooleanSlot` with member `value` located at `slot`.
                       */
                      function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
                       */
                      function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns a `Uint256Slot` with member `value` located at `slot`.
                       */
                      function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns a `Int256Slot` with member `value` located at `slot`.
                       */
                      function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns a `StringSlot` with member `value` located at `slot`.
                       */
                      function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                       */
                      function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := store.slot
                          }
                      }
                      /**
                       * @dev Returns a `BytesSlot` with member `value` located at `slot`.
                       */
                      function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                       */
                      function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                          assembly ("memory-safe") {
                              r.slot := store.slot
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
                  import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                  import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                  import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
                  import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
                  import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
                  import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
                  import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
                  // Aave V3 Interfaces
                  interface IPool {
                      function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
                      function withdraw(address asset, uint256 amount, address to) external returns (uint256);
                  }
                  interface IAToken {
                      function balanceOf(address user) external view returns (uint256);
                  }
                  // Compound V3 Interfaces
                  interface IComet {
                      function supply(address asset, uint256 amount) external;
                      function withdraw(address asset, uint256 amount) external;
                      function balanceOf(address account) external view returns (uint256);
                  }
                  /**
                   * @title MultiProtocolTokenVault
                   * @dev Upgradeable ERC20 share token that deposits ERC20 tokens into yield protocols (Aave or Compound)
                   * Admin can switch between protocols, users receive shares based on net value
                   * Uses SafeERC20 to handle non-standard tokens like USDT
                   */
                  contract MultiProtocolTokenVault is
                      Initializable,
                      ERC20Upgradeable,
                      AccessControlUpgradeable,
                      PausableUpgradeable,
                      ReentrancyGuardUpgradeable,
                      UUPSUpgradeable
                  {
                      using SafeERC20 for IERC20;
                      bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
                      bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
                      bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
                      enum Protocol { NONE, AAVE, COMPOUND }
                      // Protocol configuration
                      Protocol public currentProtocol;
                      // Contract addresses
                      IERC20 public underlyingToken;
                      IPool public aavePool;
                      IAToken public aToken;
                      IComet public compoundComet;
                      // Migration state
                      bool public isMigrating;
                      // Custom errors
                      error ZeroAddress();
                      error ZeroDepositAmount();
                      error InsufficientDepositAmount();
                      error ZeroShares();
                      error InsufficientShares();
                      error NoSharesInCirculation();
                      error InsufficientBalance();
                      error TokenTransferFailed();
                      error ProtocolNotSet();
                      error MigrationInProgress();
                      error InvalidProtocol();
                      error WithdrawFromProtocolFailed();
                      error SupplyToProtocolFailed();
                      error InsufficientAllowance();
                      // Events
                      event Deposit(address indexed user, uint256 tokenAmount, uint256 sharesReceived);
                      event Withdraw(address indexed user, uint256 sharesAmount, uint256 tokenReceived);
                      event ProtocolSwitched(Protocol indexed oldProtocol, Protocol indexed newProtocol);
                      event MigrationStarted(Protocol indexed from, Protocol indexed to);
                      event MigrationCompleted(Protocol indexed from, Protocol indexed to, uint256 amount);
                      event OperatorAdded(address indexed operator, address indexed admin);
                      event OperatorRemoved(address indexed operator, address indexed admin);
                      /// @custom:oz-upgrades-unsafe-allow constructor
                      constructor() {
                          _disableInitializers();
                      }
                      /**
                       * @dev Initialize function
                       * @param _name Token name
                       * @param _symbol Token symbol
                       * @param _admin Admin address
                       * @param _underlyingToken Underlying ERC20 token address
                       * @param _aavePool Aave V3 Pool address
                       * @param _aToken aToken address for the underlying token
                       * @param _compoundComet Compound V3 Comet address
                       */
                      function initialize(
                          string memory _name,
                          string memory _symbol,
                          address _admin,
                          address _underlyingToken,
                          address _aavePool,
                          address _aToken,
                          address _compoundComet
                      ) public initializer {
                          if (_underlyingToken == address(0) || _admin == address(0)) revert ZeroAddress();
                          __ERC20_init(_name, _symbol);
                          __AccessControl_init();
                          __Pausable_init();
                          __ReentrancyGuard_init();
                          __UUPSUpgradeable_init();
                          // Set contract addresses
                          underlyingToken = IERC20(_underlyingToken);
                          aavePool = IPool(_aavePool);
                          aToken = IAToken(_aToken);
                          compoundComet = IComet(_compoundComet);
                          // Set initial protocol to NONE
                          currentProtocol = Protocol.NONE;
                          // Set roles
                          _grantRole(DEFAULT_ADMIN_ROLE, _admin);
                          _grantRole(ADMIN_ROLE, _admin);
                          _grantRole(OPERATOR_ROLE, _admin);  // Admin is also an operator by default
                          _grantRole(UPGRADER_ROLE, _admin);
                      }
                      /**
                       * @dev Deposit tokens and receive shares based on net value
                       * @param _amount Amount of underlying tokens to deposit
                       */
                      function deposit(uint256 _amount) external nonReentrant whenNotPaused {
                          if (_amount == 0) revert ZeroDepositAmount();
                          if (isMigrating) revert MigrationInProgress();
                          if (currentProtocol == Protocol.NONE) revert ProtocolNotSet();
                          // Check allowance
                          if (underlyingToken.allowance(msg.sender, address(this)) < _amount) {
                              revert InsufficientAllowance();
                          }
                          uint256 shares;
                          uint256 totalAssets = getTotalAssets();
                          if (totalSupply() == 0 || totalAssets == 0) {
                              shares = _amount;
                          } else {
                              shares = (_amount * totalSupply()) / totalAssets;
                          }
                          if (shares == 0) revert InsufficientDepositAmount();
                          // Transfer tokens from user
                          underlyingToken.safeTransferFrom(msg.sender, address(this), _amount);
                          // Deposit tokens into current protocol
                          _depositToProtocol(_amount);
                          _mint(msg.sender, shares);
                          emit Deposit(msg.sender, _amount, shares);
                      }
                      /**
                       * @dev Withdraw tokens by burning shares
                       * @param _shares Number of shares to burn
                       */
                      function withdraw(uint256 _shares) external nonReentrant whenNotPaused {
                          if (_shares == 0) revert ZeroShares();
                          if (balanceOf(msg.sender) < _shares) revert InsufficientShares();
                          if (isMigrating) revert MigrationInProgress();
                          uint256 totalShares = totalSupply();
                          if (totalShares == 0) revert NoSharesInCirculation();
                          uint256 totalAssets = getTotalAssets();
                          uint256 withdrawAmount = (_shares * totalAssets) / totalShares;
                          if (withdrawAmount == 0) revert InsufficientBalance();
                          // Burn shares first
                          _burn(msg.sender, _shares);
                          // Withdraw from protocol
                          _withdrawFromProtocol(withdrawAmount);
                          // Transfer tokens to user
                          underlyingToken.safeTransfer(msg.sender, withdrawAmount);
                          emit Withdraw(msg.sender, _shares, withdrawAmount);
                      }
                      /**
                       * @dev Switch yield protocol (operator only)
                       * @param _newProtocol New protocol to switch to
                       */
                      function switchProtocol(Protocol _newProtocol) external onlyRole(OPERATOR_ROLE) whenNotPaused {
                          if (_newProtocol == Protocol.NONE || _newProtocol == currentProtocol) revert InvalidProtocol();
                          if (isMigrating) revert MigrationInProgress();
                          Protocol oldProtocol = currentProtocol;
                          // If we have assets in current protocol, migrate them
                          if (currentProtocol != Protocol.NONE) {
                              uint256 totalAssets = getTotalAssets();
                              if (totalAssets > 0) {
                                  isMigrating = true;
                                  emit MigrationStarted(oldProtocol, _newProtocol);
                                  // Withdraw all from current protocol
                                  _withdrawAllFromProtocol();
                                  // Update protocol
                                  currentProtocol = _newProtocol;
                                  // Deposit all to new protocol
                                  uint256 tokenBalance = underlyingToken.balanceOf(address(this));
                                  if (tokenBalance > 0) {
                                      _depositToProtocol(tokenBalance);
                                  }
                                  isMigrating = false;
                                  emit MigrationCompleted(oldProtocol, _newProtocol, tokenBalance);
                              } else {
                                  currentProtocol = _newProtocol;
                              }
                          } else {
                              currentProtocol = _newProtocol;
                          }
                          emit ProtocolSwitched(oldProtocol, _newProtocol);
                      }
                      /**
                       * @dev Get total assets under management
                       */
                      function getTotalAssets() public view returns (uint256) {
                          if (currentProtocol == Protocol.AAVE) {
                              return aToken.balanceOf(address(this));
                          } else if (currentProtocol == Protocol.COMPOUND) {
                              return compoundComet.balanceOf(address(this));
                          } else {
                              return underlyingToken.balanceOf(address(this));
                          }
                      }
                      /**
                       * @dev Get net value per share (in underlying token units)
                       */
                      function getNetValue() external view returns (uint256) {
                          uint256 totalShares = totalSupply();
                          if (totalShares == 0) {
                              return 10**decimals(); // 1:1 ratio initially
                          }
                          return (getTotalAssets() * 10**decimals()) / totalShares;
                      }
                      /**
                       * @dev Calculate shares for given token amount
                       */
                      function calculateShares(uint256 _tokenAmount) external view returns (uint256) {
                          uint256 totalAssets = getTotalAssets();
                          if (totalSupply() == 0 || totalAssets == 0) {
                              return _tokenAmount;
                          }
                          return (_tokenAmount * totalSupply()) / totalAssets;
                      }
                      /**
                       * @dev Calculate token amount for given shares
                       */
                      function calculateTokens(uint256 _shares) external view returns (uint256) {
                          if (totalSupply() == 0) {
                              return 0;
                          }
                          return (_shares * getTotalAssets()) / totalSupply();
                      }
                      /**
                       * @dev Get underlying token address
                       */
                      function getUnderlyingToken() external view returns (address) {
                          return address(underlyingToken);
                      }
                      /**
                       * @dev Internal function to deposit to current protocol
                       */
                      function _depositToProtocol(uint256 _amount) internal {
                          if (currentProtocol == Protocol.AAVE) {
                              // Approve and supply to Aave
                              underlyingToken.forceApprove(address(aavePool), _amount);
                              aavePool.supply(address(underlyingToken), _amount, address(this), 0);
                          } else if (currentProtocol == Protocol.COMPOUND) {
                              // Approve and supply to Compound
                              underlyingToken.forceApprove(address(compoundComet), _amount);
                              compoundComet.supply(address(underlyingToken), _amount);
                          }
                      }
                      /**
                       * @dev Internal function to withdraw from current protocol
                       */
                      function _withdrawFromProtocol(uint256 _amount) internal {
                          if (currentProtocol == Protocol.AAVE) {
                              aavePool.withdraw(address(underlyingToken), _amount, address(this));
                          } else if (currentProtocol == Protocol.COMPOUND) {
                              compoundComet.withdraw(address(underlyingToken), _amount);
                          }
                      }
                      /**
                       * @dev Internal function to withdraw all from current protocol
                       */
                      function _withdrawAllFromProtocol() internal {
                          uint256 totalAssets = getTotalAssets();
                          if (totalAssets > 0) {
                              if (currentProtocol == Protocol.AAVE) {
                                  // For Aave, withdraw max amount (type(uint256).max means all)
                                  aavePool.withdraw(address(underlyingToken), type(uint256).max, address(this));
                              } else if (currentProtocol == Protocol.COMPOUND) {
                                  // For Compound, withdraw exact balance
                                  compoundComet.withdraw(address(underlyingToken), totalAssets);
                              }
                          }
                      }
                      /**
                       * @dev Set protocol initially (admin only, can only be called when protocol is NONE)
                       */
                      function setInitialProtocol(Protocol _protocol) external onlyRole(ADMIN_ROLE) {
                          if (currentProtocol != Protocol.NONE) revert InvalidProtocol();
                          if (_protocol == Protocol.NONE) revert InvalidProtocol();
                          currentProtocol = _protocol;
                          emit ProtocolSwitched(Protocol.NONE, _protocol);
                      }
                      /**
                       * @dev Update Aave addresses (admin only)
                       */
                      function updateAaveAddresses(address _aavePool, address _aToken) external onlyRole(ADMIN_ROLE) {
                          if (_aavePool == address(0) || _aToken == address(0)) revert ZeroAddress();
                          aavePool = IPool(_aavePool);
                          aToken = IAToken(_aToken);
                      }
                      /**
                       * @dev Update Compound address (admin only)
                       */
                      function updateCompoundAddress(address _compoundComet) external onlyRole(ADMIN_ROLE) {
                          if (_compoundComet == address(0)) revert ZeroAddress();
                          compoundComet = IComet(_compoundComet);
                      }
                      /**
                       * @dev Emergency withdraw tokens (admin only, for emergency situations)
                       */
                      function emergencyWithdraw(address _token, uint256 _amount, address _to) external onlyRole(ADMIN_ROLE) {
                          if (_token == address(0) || _to == address(0)) revert ZeroAddress();
                          IERC20(_token).safeTransfer(_to, _amount);
                      }
                      /**
                       * @dev Approve spending for protocols (admin only, for gas optimization)
                       */
                      function approveProtocol(address _protocol, uint256 _amount) external onlyRole(ADMIN_ROLE) {
                          if (_protocol == address(0)) revert ZeroAddress();
                          underlyingToken.forceApprove(_protocol, _amount);
                      }
                      /**
                       * @dev Add operator role to an address (admin only)
                       * @param _operator Address to grant operator role
                       */
                      function addOperator(address _operator) external onlyRole(ADMIN_ROLE) {
                          if (_operator == address(0)) revert ZeroAddress();
                          _grantRole(OPERATOR_ROLE, _operator);
                          emit OperatorAdded(_operator, msg.sender);
                      }
                      /**
                       * @dev Remove operator role from an address (admin only)
                       * @param _operator Address to revoke operator role
                       */
                      function removeOperator(address _operator) external onlyRole(ADMIN_ROLE) {
                          if (_operator == address(0)) revert ZeroAddress();
                          _revokeRole(OPERATOR_ROLE, _operator);
                          emit OperatorRemoved(_operator, msg.sender);
                      }
                      /**
                       * @dev Check if an address has operator role
                       * @param _operator Address to check
                       * @return true if address has operator role
                       */
                      function isOperator(address _operator) external view returns (bool) {
                          return hasRole(OPERATOR_ROLE, _operator);
                      }
                      // Management functions
                      function pause() external onlyRole(ADMIN_ROLE) {
                          _pause();
                      }
                      function unpause() external onlyRole(ADMIN_ROLE) {
                          _unpause();
                      }
                      function _authorizeUpgrade(address newImplementation)
                          internal
                          override
                          onlyRole(UPGRADER_ROLE)
                      {}
                  }
                  

                  File 6 of 10: AToken
                  // SPDX-License-Identifier: LGPL-3.0-or-later
                  pragma solidity 0.8.10;
                  import {IERC20} from '../../openzeppelin/contracts/IERC20.sol';
                  /// @title Gnosis Protocol v2 Safe ERC20 Transfer Library
                  /// @author Gnosis Developers
                  /// @dev Gas-efficient version of Openzeppelin's SafeERC20 contract.
                  library GPv2SafeERC20 {
                    /// @dev Wrapper around a call to the ERC20 function `transfer` that reverts
                    /// also when the token returns `false`.
                    function safeTransfer(
                      IERC20 token,
                      address to,
                      uint256 value
                    ) internal {
                      bytes4 selector_ = token.transfer.selector;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        let freeMemoryPointer := mload(0x40)
                        mstore(freeMemoryPointer, selector_)
                        mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                        mstore(add(freeMemoryPointer, 36), value)
                        if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)) {
                          returndatacopy(0, 0, returndatasize())
                          revert(0, returndatasize())
                        }
                      }
                      require(getLastTransferResult(token), 'GPv2: failed transfer');
                    }
                    /// @dev Wrapper around a call to the ERC20 function `transferFrom` that
                    /// reverts also when the token returns `false`.
                    function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 value
                    ) internal {
                      bytes4 selector_ = token.transferFrom.selector;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        let freeMemoryPointer := mload(0x40)
                        mstore(freeMemoryPointer, selector_)
                        mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
                        mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                        mstore(add(freeMemoryPointer, 68), value)
                        if iszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)) {
                          returndatacopy(0, 0, returndatasize())
                          revert(0, returndatasize())
                        }
                      }
                      require(getLastTransferResult(token), 'GPv2: failed transferFrom');
                    }
                    /// @dev Verifies that the last return was a successful `transfer*` call.
                    /// This is done by checking that the return data is either empty, or
                    /// is a valid ABI encoded boolean.
                    function getLastTransferResult(IERC20 token) private view returns (bool success) {
                      // NOTE: Inspecting previous return data requires assembly. Note that
                      // we write the return data to memory 0 in the case where the return
                      // data size is 32, this is OK since the first 64 bytes of memory are
                      // reserved by Solidy as a scratch space that can be used within
                      // assembly blocks.
                      // <https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html>
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        /// @dev Revert with an ABI encoded Solidity error with a message
                        /// that fits into 32-bytes.
                        ///
                        /// An ABI encoded Solidity error has the following memory layout:
                        ///
                        /// ------------+----------------------------------
                        ///  byte range | value
                        /// ------------+----------------------------------
                        ///  0x00..0x04 |        selector("Error(string)")
                        ///  0x04..0x24 |      string offset (always 0x20)
                        ///  0x24..0x44 |                    string length
                        ///  0x44..0x64 | string value, padded to 32-bytes
                        function revertWithMessage(length, message) {
                          mstore(0x00, '\\x08\\xc3\\x79\\xa0')
                          mstore(0x04, 0x20)
                          mstore(0x24, length)
                          mstore(0x44, message)
                          revert(0x00, 0x64)
                        }
                        switch returndatasize()
                        // Non-standard ERC20 transfer without return.
                        case 0 {
                          // NOTE: When the return data size is 0, verify that there
                          // is code at the address. This is done in order to maintain
                          // compatibility with Solidity calling conventions.
                          // <https://docs.soliditylang.org/en/v0.7.6/control-structures.html#external-function-calls>
                          if iszero(extcodesize(token)) {
                            revertWithMessage(20, 'GPv2: not a contract')
                          }
                          success := 1
                        }
                        // Standard ERC20 transfer returning boolean success value.
                        case 32 {
                          returndatacopy(0, 0, returndatasize())
                          // NOTE: For ABI encoding v1, any non-zero value is accepted
                          // as `true` for a boolean. In order to stay compatible with
                          // OpenZeppelin's `SafeERC20` library which is known to work
                          // with the existing ERC20 implementation we care about,
                          // make sure we return success for any non-zero return value
                          // from the `transfer*` call.
                          success := iszero(iszero(mload(0)))
                        }
                        default {
                          revertWithMessage(31, 'GPv2: malformed transfer result')
                        }
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity 0.8.10;
                  /*
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with GSN meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                    function _msgSender() internal view virtual returns (address payable) {
                      return payable(msg.sender);
                    }
                    function _msgData() internal view virtual returns (bytes memory) {
                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                      return msg.data;
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  /**
                   * @dev Interface of the ERC20 standard as defined in the EIP.
                   */
                  interface IERC20 {
                    /**
                     * @dev Returns the amount of tokens in existence.
                     */
                    function totalSupply() external view returns (uint256);
                    /**
                     * @dev Returns the amount of tokens owned by `account`.
                     */
                    function balanceOf(address account) external view returns (uint256);
                    /**
                     * @dev Moves `amount` tokens from the caller's account to `recipient`.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transfer(address recipient, uint256 amount) external returns (bool);
                    /**
                     * @dev Returns the remaining number of tokens that `spender` will be
                     * allowed to spend on behalf of `owner` through {transferFrom}. This is
                     * zero by default.
                     *
                     * This value changes when {approve} or {transferFrom} are called.
                     */
                    function allowance(address owner, address spender) external view returns (uint256);
                    /**
                     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * IMPORTANT: Beware that changing an allowance with this method brings the risk
                     * that someone may use both the old and the new allowance by unfortunate
                     * transaction ordering. One possible solution to mitigate this race
                     * condition is to first reduce the spender's allowance to 0 and set the
                     * desired value afterwards:
                     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                     *
                     * Emits an {Approval} event.
                     */
                    function approve(address spender, uint256 amount) external returns (bool);
                    /**
                     * @dev Moves `amount` tokens from `sender` to `recipient` using the
                     * allowance mechanism. `amount` is then deducted from the caller's
                     * allowance.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transferFrom(
                      address sender,
                      address recipient,
                      uint256 amount
                    ) external returns (bool);
                    /**
                     * @dev Emitted when `value` tokens are moved from one account (`from`) to
                     * another (`to`).
                     *
                     * Note that `value` may be zero.
                     */
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    /**
                     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                     * a call to {approve}. `value` is the new allowance.
                     */
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import {IERC20} from './IERC20.sol';
                  interface IERC20Detailed is IERC20 {
                    function name() external view returns (string memory);
                    function symbol() external view returns (string memory);
                    function decimals() external view returns (uint8);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                  pragma solidity 0.8.10;
                  /**
                   * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                   * checks.
                   *
                   * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                   * easily result in undesired exploitation or bugs, since developers usually
                   * assume that overflows raise errors. `SafeCast` restores this intuition by
                   * reverting the transaction when such an operation overflows.
                   *
                   * Using this library instead of the unchecked operations eliminates an entire
                   * class of bugs, so it's recommended to use it always.
                   *
                   * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                   * all math on `uint256` and `int256` and then downcasting.
                   */
                  library SafeCast {
                    /**
                     * @dev Returns the downcasted uint224 from uint256, reverting on
                     * overflow (when the input is greater than largest uint224).
                     *
                     * Counterpart to Solidity's `uint224` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 224 bits
                     */
                    function toUint224(uint256 value) internal pure returns (uint224) {
                      require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                      return uint224(value);
                    }
                    /**
                     * @dev Returns the downcasted uint128 from uint256, reverting on
                     * overflow (when the input is greater than largest uint128).
                     *
                     * Counterpart to Solidity's `uint128` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 128 bits
                     */
                    function toUint128(uint256 value) internal pure returns (uint128) {
                      require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                      return uint128(value);
                    }
                    /**
                     * @dev Returns the downcasted uint96 from uint256, reverting on
                     * overflow (when the input is greater than largest uint96).
                     *
                     * Counterpart to Solidity's `uint96` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 96 bits
                     */
                    function toUint96(uint256 value) internal pure returns (uint96) {
                      require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                      return uint96(value);
                    }
                    /**
                     * @dev Returns the downcasted uint64 from uint256, reverting on
                     * overflow (when the input is greater than largest uint64).
                     *
                     * Counterpart to Solidity's `uint64` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 64 bits
                     */
                    function toUint64(uint256 value) internal pure returns (uint64) {
                      require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                      return uint64(value);
                    }
                    /**
                     * @dev Returns the downcasted uint32 from uint256, reverting on
                     * overflow (when the input is greater than largest uint32).
                     *
                     * Counterpart to Solidity's `uint32` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 32 bits
                     */
                    function toUint32(uint256 value) internal pure returns (uint32) {
                      require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                      return uint32(value);
                    }
                    /**
                     * @dev Returns the downcasted uint16 from uint256, reverting on
                     * overflow (when the input is greater than largest uint16).
                     *
                     * Counterpart to Solidity's `uint16` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 16 bits
                     */
                    function toUint16(uint256 value) internal pure returns (uint16) {
                      require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                      return uint16(value);
                    }
                    /**
                     * @dev Returns the downcasted uint8 from uint256, reverting on
                     * overflow (when the input is greater than largest uint8).
                     *
                     * Counterpart to Solidity's `uint8` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 8 bits.
                     */
                    function toUint8(uint256 value) internal pure returns (uint8) {
                      require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                      return uint8(value);
                    }
                    /**
                     * @dev Converts a signed int256 into an unsigned uint256.
                     *
                     * Requirements:
                     *
                     * - input must be greater than or equal to 0.
                     */
                    function toUint256(int256 value) internal pure returns (uint256) {
                      require(value >= 0, 'SafeCast: value must be positive');
                      return uint256(value);
                    }
                    /**
                     * @dev Returns the downcasted int128 from int256, reverting on
                     * overflow (when the input is less than smallest int128 or
                     * greater than largest int128).
                     *
                     * Counterpart to Solidity's `int128` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 128 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt128(int256 value) internal pure returns (int128) {
                      require(
                        value >= type(int128).min && value <= type(int128).max,
                        "SafeCast: value doesn't fit in 128 bits"
                      );
                      return int128(value);
                    }
                    /**
                     * @dev Returns the downcasted int64 from int256, reverting on
                     * overflow (when the input is less than smallest int64 or
                     * greater than largest int64).
                     *
                     * Counterpart to Solidity's `int64` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 64 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt64(int256 value) internal pure returns (int64) {
                      require(
                        value >= type(int64).min && value <= type(int64).max,
                        "SafeCast: value doesn't fit in 64 bits"
                      );
                      return int64(value);
                    }
                    /**
                     * @dev Returns the downcasted int32 from int256, reverting on
                     * overflow (when the input is less than smallest int32 or
                     * greater than largest int32).
                     *
                     * Counterpart to Solidity's `int32` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 32 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt32(int256 value) internal pure returns (int32) {
                      require(
                        value >= type(int32).min && value <= type(int32).max,
                        "SafeCast: value doesn't fit in 32 bits"
                      );
                      return int32(value);
                    }
                    /**
                     * @dev Returns the downcasted int16 from int256, reverting on
                     * overflow (when the input is less than smallest int16 or
                     * greater than largest int16).
                     *
                     * Counterpart to Solidity's `int16` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 16 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt16(int256 value) internal pure returns (int16) {
                      require(
                        value >= type(int16).min && value <= type(int16).max,
                        "SafeCast: value doesn't fit in 16 bits"
                      );
                      return int16(value);
                    }
                    /**
                     * @dev Returns the downcasted int8 from int256, reverting on
                     * overflow (when the input is less than smallest int8 or
                     * greater than largest int8).
                     *
                     * Counterpart to Solidity's `int8` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 8 bits.
                     *
                     * _Available since v3.1._
                     */
                    function toInt8(int256 value) internal pure returns (int8) {
                      require(
                        value >= type(int8).min && value <= type(int8).max,
                        "SafeCast: value doesn't fit in 8 bits"
                      );
                      return int8(value);
                    }
                    /**
                     * @dev Converts an unsigned uint256 into a signed int256.
                     *
                     * Requirements:
                     *
                     * - input must be less than or equal to maxInt256.
                     */
                    function toInt256(uint256 value) internal pure returns (int256) {
                      // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                      require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                      return int256(value);
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity ^0.8.0;
                  import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                  /**
                   * @title IACLManager
                   * @author Aave
                   * @notice Defines the basic interface for the ACL Manager
                   */
                  interface IACLManager {
                    /**
                     * @notice Returns the contract address of the PoolAddressesProvider
                     * @return The address of the PoolAddressesProvider
                     */
                    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                    /**
                     * @notice Returns the identifier of the PoolAdmin role
                     * @return The id of the PoolAdmin role
                     */
                    function POOL_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the EmergencyAdmin role
                     * @return The id of the EmergencyAdmin role
                     */
                    function EMERGENCY_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the RiskAdmin role
                     * @return The id of the RiskAdmin role
                     */
                    function RISK_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the FlashBorrower role
                     * @return The id of the FlashBorrower role
                     */
                    function FLASH_BORROWER_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the Bridge role
                     * @return The id of the Bridge role
                     */
                    function BRIDGE_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the AssetListingAdmin role
                     * @return The id of the AssetListingAdmin role
                     */
                    function ASSET_LISTING_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Set the role as admin of a specific role.
                     * @dev By default the admin role for all roles is `DEFAULT_ADMIN_ROLE`.
                     * @param role The role to be managed by the admin role
                     * @param adminRole The admin role
                     */
                    function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
                    /**
                     * @notice Adds a new admin as PoolAdmin
                     * @param admin The address of the new admin
                     */
                    function addPoolAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as PoolAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removePoolAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is PoolAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is PoolAdmin, false otherwise
                     */
                    function isPoolAdmin(address admin) external view returns (bool);
                    /**
                     * @notice Adds a new admin as EmergencyAdmin
                     * @param admin The address of the new admin
                     */
                    function addEmergencyAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as EmergencyAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removeEmergencyAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is EmergencyAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is EmergencyAdmin, false otherwise
                     */
                    function isEmergencyAdmin(address admin) external view returns (bool);
                    /**
                     * @notice Adds a new admin as RiskAdmin
                     * @param admin The address of the new admin
                     */
                    function addRiskAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as RiskAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removeRiskAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is RiskAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is RiskAdmin, false otherwise
                     */
                    function isRiskAdmin(address admin) external view returns (bool);
                    /**
                     * @notice Adds a new address as FlashBorrower
                     * @param borrower The address of the new FlashBorrower
                     */
                    function addFlashBorrower(address borrower) external;
                    /**
                     * @notice Removes an address as FlashBorrower
                     * @param borrower The address of the FlashBorrower to remove
                     */
                    function removeFlashBorrower(address borrower) external;
                    /**
                     * @notice Returns true if the address is FlashBorrower, false otherwise
                     * @param borrower The address to check
                     * @return True if the given address is FlashBorrower, false otherwise
                     */
                    function isFlashBorrower(address borrower) external view returns (bool);
                    /**
                     * @notice Adds a new address as Bridge
                     * @param bridge The address of the new Bridge
                     */
                    function addBridge(address bridge) external;
                    /**
                     * @notice Removes an address as Bridge
                     * @param bridge The address of the bridge to remove
                     */
                    function removeBridge(address bridge) external;
                    /**
                     * @notice Returns true if the address is Bridge, false otherwise
                     * @param bridge The address to check
                     * @return True if the given address is Bridge, false otherwise
                     */
                    function isBridge(address bridge) external view returns (bool);
                    /**
                     * @notice Adds a new admin as AssetListingAdmin
                     * @param admin The address of the new admin
                     */
                    function addAssetListingAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as AssetListingAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removeAssetListingAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is AssetListingAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is AssetListingAdmin, false otherwise
                     */
                    function isAssetListingAdmin(address admin) external view returns (bool);
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity ^0.8.0;
                  import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                  import {IInitializableAToken} from './IInitializableAToken.sol';
                  /**
                   * @title IAToken
                   * @author Aave
                   * @notice Defines the basic interface for an AToken.
                   */
                  interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken {
                    /**
                     * @dev Emitted during the transfer action
                     * @param from The user whose tokens are being transferred
                     * @param to The recipient
                     * @param value The scaled amount being transferred
                     * @param index The next liquidity index of the reserve
                     */
                    event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index);
                    /**
                     * @notice Mints `amount` aTokens to `user`
                     * @param caller The address performing the mint
                     * @param onBehalfOf The address of the user that will receive the minted aTokens
                     * @param amount The amount of tokens getting minted
                     * @param index The next liquidity index of the reserve
                     * @return `true` if the the previous balance of the user was 0
                     */
                    function mint(
                      address caller,
                      address onBehalfOf,
                      uint256 amount,
                      uint256 index
                    ) external returns (bool);
                    /**
                     * @notice Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
                     * @dev In some instances, the mint event could be emitted from a burn transaction
                     * if the amount to burn is less than the interest that the user accrued
                     * @param from The address from which the aTokens will be burned
                     * @param receiverOfUnderlying The address that will receive the underlying
                     * @param amount The amount being burned
                     * @param index The next liquidity index of the reserve
                     */
                    function burn(
                      address from,
                      address receiverOfUnderlying,
                      uint256 amount,
                      uint256 index
                    ) external;
                    /**
                     * @notice Mints aTokens to the reserve treasury
                     * @param amount The amount of tokens getting minted
                     * @param index The next liquidity index of the reserve
                     */
                    function mintToTreasury(uint256 amount, uint256 index) external;
                    /**
                     * @notice Transfers aTokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken
                     * @param from The address getting liquidated, current owner of the aTokens
                     * @param to The recipient
                     * @param value The amount of tokens getting transferred
                     */
                    function transferOnLiquidation(
                      address from,
                      address to,
                      uint256 value
                    ) external;
                    /**
                     * @notice Transfers the underlying asset to `target`.
                     * @dev Used by the Pool to transfer assets in borrow(), withdraw() and flashLoan()
                     * @param target The recipient of the underlying
                     * @param amount The amount getting transferred
                     */
                    function transferUnderlyingTo(address target, uint256 amount) external;
                    /**
                     * @notice Handles the underlying received by the aToken after the transfer has been completed.
                     * @dev The default implementation is empty as with standard ERC20 tokens, nothing needs to be done after the
                     * transfer is concluded. However in the future there may be aTokens that allow for example to stake the underlying
                     * to receive LM rewards. In that case, `handleRepayment()` would perform the staking of the underlying asset.
                     * @param user The user executing the repayment
                     * @param onBehalfOf The address of the user who will get his debt reduced/removed
                     * @param amount The amount getting repaid
                     */
                    function handleRepayment(
                      address user,
                      address onBehalfOf,
                      uint256 amount
                    ) external;
                    /**
                     * @notice Allow passing a signed message to approve spending
                     * @dev implements the permit function as for
                     * https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
                     * @param owner The owner of the funds
                     * @param spender The spender
                     * @param value The amount
                     * @param deadline The deadline timestamp, type(uint256).max for max deadline
                     * @param v Signature param
                     * @param s Signature param
                     * @param r Signature param
                     */
                    function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                    ) external;
                    /**
                     * @notice Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH)
                     * @return The address of the underlying asset
                     */
                    function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                    /**
                     * @notice Returns the address of the Aave treasury, receiving the fees on this aToken.
                     * @return Address of the Aave treasury
                     */
                    function RESERVE_TREASURY_ADDRESS() external view returns (address);
                    /**
                     * @notice Get the domain separator for the token
                     * @dev Return cached value if chainId matches cache, otherwise recomputes separator
                     * @return The domain separator of the token at current chain
                     */
                    function DOMAIN_SEPARATOR() external view returns (bytes32);
                    /**
                     * @notice Returns the nonce for owner.
                     * @param owner The address of the owner
                     * @return The nonce of the owner
                     */
                    function nonces(address owner) external view returns (uint256);
                    /**
                     * @notice Rescue and transfer tokens locked in this contract
                     * @param token The address of the token
                     * @param to The address of the recipient
                     * @param amount The amount of token to transfer
                     */
                    function rescueTokens(
                      address token,
                      address to,
                      uint256 amount
                    ) external;
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity ^0.8.0;
                  /**
                   * @title IAaveIncentivesController
                   * @author Aave
                   * @notice Defines the basic interface for an Aave Incentives Controller.
                   * @dev It only contains one single function, needed as a hook on aToken and debtToken transfers.
                   */
                  interface IAaveIncentivesController {
                    /**
                     * @dev Called by the corresponding asset on transfer hook in order to update the rewards distribution.
                     * @dev The units of `totalSupply` and `userBalance` should be the same.
                     * @param user The address of the user whose asset balance has changed
                     * @param totalSupply The total supply of the asset prior to user balance change
                     * @param userBalance The previous user balance prior to balance change
                     */
                    function handleAction(
                      address user,
                      uint256 totalSupply,
                      uint256 userBalance
                    ) external;
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity ^0.8.0;
                  import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
                  import {IPool} from './IPool.sol';
                  /**
                   * @title IInitializableAToken
                   * @author Aave
                   * @notice Interface for the initialize function on AToken
                   */
                  interface IInitializableAToken {
                    /**
                     * @dev Emitted when an aToken is initialized
                     * @param underlyingAsset The address of the underlying asset
                     * @param pool The address of the associated pool
                     * @param treasury The address of the treasury
                     * @param incentivesController The address of the incentives controller for this aToken
                     * @param aTokenDecimals The decimals of the underlying
                     * @param aTokenName The name of the aToken
                     * @param aTokenSymbol The symbol of the aToken
                     * @param params A set of encoded parameters for additional initialization
                     */
                    event Initialized(
                      address indexed underlyingAsset,
                      address indexed pool,
                      address treasury,
                      address incentivesController,
                      uint8 aTokenDecimals,
                      string aTokenName,
                      string aTokenSymbol,
                      bytes params
                    );
                    /**
                     * @notice Initializes the aToken
                     * @param pool The pool contract that is initializing this contract
                     * @param treasury The address of the Aave treasury, receiving the fees on this aToken
                     * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
                     * @param incentivesController The smart contract managing potential incentives distribution
                     * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's
                     * @param aTokenName The name of the aToken
                     * @param aTokenSymbol The symbol of the aToken
                     * @param params A set of encoded parameters for additional initialization
                     */
                    function initialize(
                      IPool pool,
                      address treasury,
                      address underlyingAsset,
                      IAaveIncentivesController incentivesController,
                      uint8 aTokenDecimals,
                      string calldata aTokenName,
                      string calldata aTokenSymbol,
                      bytes calldata params
                    ) external;
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity ^0.8.0;
                  import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                  import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                  /**
                   * @title IPool
                   * @author Aave
                   * @notice Defines the basic interface for an Aave Pool.
                   */
                  interface IPool {
                    /**
                     * @dev Emitted on mintUnbacked()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address initiating the supply
                     * @param onBehalfOf The beneficiary of the supplied assets, receiving the aTokens
                     * @param amount The amount of supplied assets
                     * @param referralCode The referral code used
                     */
                    event MintUnbacked(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted on backUnbacked()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param backer The address paying for the backing
                     * @param amount The amount added as backing
                     * @param fee The amount paid in fees
                     */
                    event BackUnbacked(address indexed reserve, address indexed backer, uint256 amount, uint256 fee);
                    /**
                     * @dev Emitted on supply()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address initiating the supply
                     * @param onBehalfOf The beneficiary of the supply, receiving the aTokens
                     * @param amount The amount supplied
                     * @param referralCode The referral code used
                     */
                    event Supply(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted on withdraw()
                     * @param reserve The address of the underlying asset being withdrawn
                     * @param user The address initiating the withdrawal, owner of aTokens
                     * @param to The address that will receive the underlying
                     * @param amount The amount to be withdrawn
                     */
                    event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
                    /**
                     * @dev Emitted on borrow() and flashLoan() when debt needs to be opened
                     * @param reserve The address of the underlying asset being borrowed
                     * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just
                     * initiator of the transaction on flashLoan()
                     * @param onBehalfOf The address that will be getting the debt
                     * @param amount The amount borrowed out
                     * @param interestRateMode The rate mode: 1 for Stable, 2 for Variable
                     * @param borrowRate The numeric rate at which the user has borrowed, expressed in ray
                     * @param referralCode The referral code used
                     */
                    event Borrow(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      DataTypes.InterestRateMode interestRateMode,
                      uint256 borrowRate,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted on repay()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The beneficiary of the repayment, getting his debt reduced
                     * @param repayer The address of the user initiating the repay(), providing the funds
                     * @param amount The amount repaid
                     * @param useATokens True if the repayment is done using aTokens, `false` if done with underlying asset directly
                     */
                    event Repay(
                      address indexed reserve,
                      address indexed user,
                      address indexed repayer,
                      uint256 amount,
                      bool useATokens
                    );
                    /**
                     * @dev Emitted on swapBorrowRateMode()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address of the user swapping his rate mode
                     * @param interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable
                     */
                    event SwapBorrowRateMode(
                      address indexed reserve,
                      address indexed user,
                      DataTypes.InterestRateMode interestRateMode
                    );
                    /**
                     * @dev Emitted on borrow(), repay() and liquidationCall() when using isolated assets
                     * @param asset The address of the underlying asset of the reserve
                     * @param totalDebt The total isolation mode debt for the reserve
                     */
                    event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                    /**
                     * @dev Emitted when the user selects a certain asset category for eMode
                     * @param user The address of the user
                     * @param categoryId The category id
                     */
                    event UserEModeSet(address indexed user, uint8 categoryId);
                    /**
                     * @dev Emitted on setUserUseReserveAsCollateral()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address of the user enabling the usage as collateral
                     */
                    event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                    /**
                     * @dev Emitted on setUserUseReserveAsCollateral()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address of the user enabling the usage as collateral
                     */
                    event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                    /**
                     * @dev Emitted on rebalanceStableBorrowRate()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address of the user for which the rebalance has been executed
                     */
                    event RebalanceStableBorrowRate(address indexed reserve, address indexed user);
                    /**
                     * @dev Emitted on flashLoan()
                     * @param target The address of the flash loan receiver contract
                     * @param initiator The address initiating the flash loan
                     * @param asset The address of the asset being flash borrowed
                     * @param amount The amount flash borrowed
                     * @param interestRateMode The flashloan mode: 0 for regular flashloan, 1 for Stable debt, 2 for Variable debt
                     * @param premium The fee flash borrowed
                     * @param referralCode The referral code used
                     */
                    event FlashLoan(
                      address indexed target,
                      address initiator,
                      address indexed asset,
                      uint256 amount,
                      DataTypes.InterestRateMode interestRateMode,
                      uint256 premium,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted when a borrower is liquidated.
                     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                     * @param user The address of the borrower getting liquidated
                     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                     * @param liquidatedCollateralAmount The amount of collateral received by the liquidator
                     * @param liquidator The address of the liquidator
                     * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants
                     * to receive the underlying collateral asset directly
                     */
                    event LiquidationCall(
                      address indexed collateralAsset,
                      address indexed debtAsset,
                      address indexed user,
                      uint256 debtToCover,
                      uint256 liquidatedCollateralAmount,
                      address liquidator,
                      bool receiveAToken
                    );
                    /**
                     * @dev Emitted when the state of a reserve is updated.
                     * @param reserve The address of the underlying asset of the reserve
                     * @param liquidityRate The next liquidity rate
                     * @param stableBorrowRate The next stable borrow rate
                     * @param variableBorrowRate The next variable borrow rate
                     * @param liquidityIndex The next liquidity index
                     * @param variableBorrowIndex The next variable borrow index
                     */
                    event ReserveDataUpdated(
                      address indexed reserve,
                      uint256 liquidityRate,
                      uint256 stableBorrowRate,
                      uint256 variableBorrowRate,
                      uint256 liquidityIndex,
                      uint256 variableBorrowIndex
                    );
                    /**
                     * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest.
                     * @param reserve The address of the reserve
                     * @param amountMinted The amount minted to the treasury
                     */
                    event MintedToTreasury(address indexed reserve, uint256 amountMinted);
                    /**
                     * @notice Mints an `amount` of aTokens to the `onBehalfOf`
                     * @param asset The address of the underlying asset to mint
                     * @param amount The amount to mint
                     * @param onBehalfOf The address that will receive the aTokens
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function mintUnbacked(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode
                    ) external;
                    /**
                     * @notice Back the current unbacked underlying with `amount` and pay `fee`.
                     * @param asset The address of the underlying asset to back
                     * @param amount The amount to back
                     * @param fee The amount paid in fees
                     * @return The backed amount
                     */
                    function backUnbacked(
                      address asset,
                      uint256 amount,
                      uint256 fee
                    ) external returns (uint256);
                    /**
                     * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                     * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                     * @param asset The address of the underlying asset to supply
                     * @param amount The amount to be supplied
                     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                     *   is a different wallet
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function supply(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode
                    ) external;
                    /**
                     * @notice Supply with transfer approval of asset to be supplied done via permit function
                     * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                     * @param asset The address of the underlying asset to supply
                     * @param amount The amount to be supplied
                     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                     *   is a different wallet
                     * @param deadline The deadline timestamp that the permit is valid
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     * @param permitV The V parameter of ERC712 permit sig
                     * @param permitR The R parameter of ERC712 permit sig
                     * @param permitS The S parameter of ERC712 permit sig
                     */
                    function supplyWithPermit(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode,
                      uint256 deadline,
                      uint8 permitV,
                      bytes32 permitR,
                      bytes32 permitS
                    ) external;
                    /**
                     * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
                     * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
                     * @param asset The address of the underlying asset to withdraw
                     * @param amount The underlying amount to be withdrawn
                     *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
                     * @param to The address that will receive the underlying, same as msg.sender if the user
                     *   wants to receive it on his own wallet, or a different address if the beneficiary is a
                     *   different wallet
                     * @return The final amount withdrawn
                     */
                    function withdraw(
                      address asset,
                      uint256 amount,
                      address to
                    ) external returns (uint256);
                    /**
                     * @notice Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
                     * already supplied enough collateral, or he was given enough allowance by a credit delegator on the
                     * corresponding debt token (StableDebtToken or VariableDebtToken)
                     * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
                     *   and 100 stable/variable debt tokens, depending on the `interestRateMode`
                     * @param asset The address of the underlying asset to borrow
                     * @param amount The amount to be borrowed
                     * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
                     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     * @param onBehalfOf The address of the user who will receive the debt. Should be the address of the borrower itself
                     * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
                     * if he has been given credit delegation allowance
                     */
                    function borrow(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      uint16 referralCode,
                      address onBehalfOf
                    ) external;
                    /**
                     * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
                     * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
                     * @param asset The address of the borrowed underlying asset previously borrowed
                     * @param amount The amount to repay
                     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                     * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
                     * @param onBehalfOf The address of the user who will get his debt reduced/removed. Should be the address of the
                     * user calling the function if he wants to reduce/remove his own debt, or the address of any other
                     * other borrower whose debt should be removed
                     * @return The final amount repaid
                     */
                    function repay(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      address onBehalfOf
                    ) external returns (uint256);
                    /**
                     * @notice Repay with transfer approval of asset to be repaid done via permit function
                     * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                     * @param asset The address of the borrowed underlying asset previously borrowed
                     * @param amount The amount to repay
                     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                     * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
                     * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
                     * user calling the function if he wants to reduce/remove his own debt, or the address of any other
                     * other borrower whose debt should be removed
                     * @param deadline The deadline timestamp that the permit is valid
                     * @param permitV The V parameter of ERC712 permit sig
                     * @param permitR The R parameter of ERC712 permit sig
                     * @param permitS The S parameter of ERC712 permit sig
                     * @return The final amount repaid
                     */
                    function repayWithPermit(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      address onBehalfOf,
                      uint256 deadline,
                      uint8 permitV,
                      bytes32 permitR,
                      bytes32 permitS
                    ) external returns (uint256);
                    /**
                     * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the
                     * equivalent debt tokens
                     * - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable/stable debt tokens
                     * @dev  Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken
                     * balance is not enough to cover the whole debt
                     * @param asset The address of the borrowed underlying asset previously borrowed
                     * @param amount The amount to repay
                     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                     * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
                     * @return The final amount repaid
                     */
                    function repayWithATokens(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode
                    ) external returns (uint256);
                    /**
                     * @notice Allows a borrower to swap his debt between stable and variable mode, or vice versa
                     * @param asset The address of the underlying asset borrowed
                     * @param interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable
                     */
                    function swapBorrowRateMode(address asset, uint256 interestRateMode) external;
                    /**
                     * @notice Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
                     * - Users can be rebalanced if the following conditions are satisfied:
                     *     1. Usage ratio is above 95%
                     *     2. the current supply APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too
                     *        much has been borrowed at a stable rate and suppliers are not earning enough
                     * @param asset The address of the underlying asset borrowed
                     * @param user The address of the user to be rebalanced
                     */
                    function rebalanceStableBorrowRate(address asset, address user) external;
                    /**
                     * @notice Allows suppliers to enable/disable a specific supplied asset as collateral
                     * @param asset The address of the underlying asset supplied
                     * @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise
                     */
                    function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
                    /**
                     * @notice Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
                     * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
                     *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
                     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                     * @param user The address of the borrower getting liquidated
                     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                     * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants
                     * to receive the underlying collateral asset directly
                     */
                    function liquidationCall(
                      address collateralAsset,
                      address debtAsset,
                      address user,
                      uint256 debtToCover,
                      bool receiveAToken
                    ) external;
                    /**
                     * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                     * as long as the amount taken plus a fee is returned.
                     * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
                     * into consideration. For further details please visit https://developers.aave.com
                     * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanReceiver interface
                     * @param assets The addresses of the assets being flash-borrowed
                     * @param amounts The amounts of the assets being flash-borrowed
                     * @param interestRateModes Types of the debt to open if the flash loan is not returned:
                     *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
                     *   1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                     *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                     * @param onBehalfOf The address  that will receive the debt in the case of using on `modes` 1 or 2
                     * @param params Variadic packed params to pass to the receiver as extra information
                     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function flashLoan(
                      address receiverAddress,
                      address[] calldata assets,
                      uint256[] calldata amounts,
                      uint256[] calldata interestRateModes,
                      address onBehalfOf,
                      bytes calldata params,
                      uint16 referralCode
                    ) external;
                    /**
                     * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                     * as long as the amount taken plus a fee is returned.
                     * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
                     * into consideration. For further details please visit https://developers.aave.com
                     * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanSimpleReceiver interface
                     * @param asset The address of the asset being flash-borrowed
                     * @param amount The amount of the asset being flash-borrowed
                     * @param params Variadic packed params to pass to the receiver as extra information
                     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function flashLoanSimple(
                      address receiverAddress,
                      address asset,
                      uint256 amount,
                      bytes calldata params,
                      uint16 referralCode
                    ) external;
                    /**
                     * @notice Returns the user account data across all the reserves
                     * @param user The address of the user
                     * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
                     * @return totalDebtBase The total debt of the user in the base currency used by the price feed
                     * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
                     * @return currentLiquidationThreshold The liquidation threshold of the user
                     * @return ltv The loan to value of The user
                     * @return healthFactor The current health factor of the user
                     */
                    function getUserAccountData(address user)
                      external
                      view
                      returns (
                        uint256 totalCollateralBase,
                        uint256 totalDebtBase,
                        uint256 availableBorrowsBase,
                        uint256 currentLiquidationThreshold,
                        uint256 ltv,
                        uint256 healthFactor
                      );
                    /**
                     * @notice Initializes a reserve, activating it, assigning an aToken and debt tokens and an
                     * interest rate strategy
                     * @dev Only callable by the PoolConfigurator contract
                     * @param asset The address of the underlying asset of the reserve
                     * @param aTokenAddress The address of the aToken that will be assigned to the reserve
                     * @param stableDebtAddress The address of the StableDebtToken that will be assigned to the reserve
                     * @param variableDebtAddress The address of the VariableDebtToken that will be assigned to the reserve
                     * @param interestRateStrategyAddress The address of the interest rate strategy contract
                     */
                    function initReserve(
                      address asset,
                      address aTokenAddress,
                      address stableDebtAddress,
                      address variableDebtAddress,
                      address interestRateStrategyAddress
                    ) external;
                    /**
                     * @notice Drop a reserve
                     * @dev Only callable by the PoolConfigurator contract
                     * @param asset The address of the underlying asset of the reserve
                     */
                    function dropReserve(address asset) external;
                    /**
                     * @notice Updates the address of the interest rate strategy contract
                     * @dev Only callable by the PoolConfigurator contract
                     * @param asset The address of the underlying asset of the reserve
                     * @param rateStrategyAddress The address of the interest rate strategy contract
                     */
                    function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress)
                      external;
                    /**
                     * @notice Sets the configuration bitmap of the reserve as a whole
                     * @dev Only callable by the PoolConfigurator contract
                     * @param asset The address of the underlying asset of the reserve
                     * @param configuration The new configuration bitmap
                     */
                    function setConfiguration(address asset, DataTypes.ReserveConfigurationMap calldata configuration)
                      external;
                    /**
                     * @notice Returns the configuration of the reserve
                     * @param asset The address of the underlying asset of the reserve
                     * @return The configuration of the reserve
                     */
                    function getConfiguration(address asset)
                      external
                      view
                      returns (DataTypes.ReserveConfigurationMap memory);
                    /**
                     * @notice Returns the configuration of the user across all the reserves
                     * @param user The user address
                     * @return The configuration of the user
                     */
                    function getUserConfiguration(address user)
                      external
                      view
                      returns (DataTypes.UserConfigurationMap memory);
                    /**
                     * @notice Returns the normalized income of the reserve
                     * @param asset The address of the underlying asset of the reserve
                     * @return The reserve's normalized income
                     */
                    function getReserveNormalizedIncome(address asset) external view returns (uint256);
                    /**
                     * @notice Returns the normalized variable debt per unit of asset
                     * @dev WARNING: This function is intended to be used primarily by the protocol itself to get a
                     * "dynamic" variable index based on time, current stored index and virtual rate at the current
                     * moment (approx. a borrower would get if opening a position). This means that is always used in
                     * combination with variable debt supply/balances.
                     * If using this function externally, consider that is possible to have an increasing normalized
                     * variable debt that is not equivalent to how the variable debt index would be updated in storage
                     * (e.g. only updates with non-zero variable debt supply)
                     * @param asset The address of the underlying asset of the reserve
                     * @return The reserve normalized variable debt
                     */
                    function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);
                    /**
                     * @notice Returns the state and configuration of the reserve
                     * @param asset The address of the underlying asset of the reserve
                     * @return The state and configuration data of the reserve
                     */
                    function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);
                    /**
                     * @notice Validates and finalizes an aToken transfer
                     * @dev Only callable by the overlying aToken of the `asset`
                     * @param asset The address of the underlying asset of the aToken
                     * @param from The user from which the aTokens are transferred
                     * @param to The user receiving the aTokens
                     * @param amount The amount being transferred/withdrawn
                     * @param balanceFromBefore The aToken balance of the `from` user before the transfer
                     * @param balanceToBefore The aToken balance of the `to` user before the transfer
                     */
                    function finalizeTransfer(
                      address asset,
                      address from,
                      address to,
                      uint256 amount,
                      uint256 balanceFromBefore,
                      uint256 balanceToBefore
                    ) external;
                    /**
                     * @notice Returns the list of the underlying assets of all the initialized reserves
                     * @dev It does not include dropped reserves
                     * @return The addresses of the underlying assets of the initialized reserves
                     */
                    function getReservesList() external view returns (address[] memory);
                    /**
                     * @notice Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypes.ReserveData struct
                     * @param id The id of the reserve as stored in the DataTypes.ReserveData struct
                     * @return The address of the reserve associated with id
                     */
                    function getReserveAddressById(uint16 id) external view returns (address);
                    /**
                     * @notice Returns the PoolAddressesProvider connected to this contract
                     * @return The address of the PoolAddressesProvider
                     */
                    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                    /**
                     * @notice Updates the protocol fee on the bridging
                     * @param bridgeProtocolFee The part of the premium sent to the protocol treasury
                     */
                    function updateBridgeProtocolFee(uint256 bridgeProtocolFee) external;
                    /**
                     * @notice Updates flash loan premiums. Flash loan premium consists of two parts:
                     * - A part is sent to aToken holders as extra, one time accumulated interest
                     * - A part is collected by the protocol treasury
                     * @dev The total premium is calculated on the total borrowed amount
                     * @dev The premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal`
                     * @dev Only callable by the PoolConfigurator contract
                     * @param flashLoanPremiumTotal The total premium, expressed in bps
                     * @param flashLoanPremiumToProtocol The part of the premium sent to the protocol treasury, expressed in bps
                     */
                    function updateFlashloanPremiums(
                      uint128 flashLoanPremiumTotal,
                      uint128 flashLoanPremiumToProtocol
                    ) external;
                    /**
                     * @notice Configures a new category for the eMode.
                     * @dev In eMode, the protocol allows very high borrowing power to borrow assets of the same category.
                     * The category 0 is reserved as it's the default for volatile assets
                     * @param id The id of the category
                     * @param config The configuration of the category
                     */
                    function configureEModeCategory(uint8 id, DataTypes.EModeCategory memory config) external;
                    /**
                     * @notice Returns the data of an eMode category
                     * @param id The id of the category
                     * @return The configuration data of the category
                     */
                    function getEModeCategoryData(uint8 id) external view returns (DataTypes.EModeCategory memory);
                    /**
                     * @notice Allows a user to use the protocol in eMode
                     * @param categoryId The id of the category
                     */
                    function setUserEMode(uint8 categoryId) external;
                    /**
                     * @notice Returns the eMode the user is using
                     * @param user The address of the user
                     * @return The eMode id
                     */
                    function getUserEMode(address user) external view returns (uint256);
                    /**
                     * @notice Resets the isolation mode total debt of the given asset to zero
                     * @dev It requires the given asset has zero debt ceiling
                     * @param asset The address of the underlying asset to reset the isolationModeTotalDebt
                     */
                    function resetIsolationModeTotalDebt(address asset) external;
                    /**
                     * @notice Returns the percentage of available liquidity that can be borrowed at once at stable rate
                     * @return The percentage of available liquidity to borrow, expressed in bps
                     */
                    function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() external view returns (uint256);
                    /**
                     * @notice Returns the total fee on flash loans
                     * @return The total fee on flashloans
                     */
                    function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint128);
                    /**
                     * @notice Returns the part of the bridge fees sent to protocol
                     * @return The bridge fee sent to the protocol treasury
                     */
                    function BRIDGE_PROTOCOL_FEE() external view returns (uint256);
                    /**
                     * @notice Returns the part of the flashloan fees sent to protocol
                     * @return The flashloan fee sent to the protocol treasury
                     */
                    function FLASHLOAN_PREMIUM_TO_PROTOCOL() external view returns (uint128);
                    /**
                     * @notice Returns the maximum number of reserves supported to be listed in this Pool
                     * @return The maximum number of reserves supported
                     */
                    function MAX_NUMBER_RESERVES() external view returns (uint16);
                    /**
                     * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens
                     * @param assets The list of reserves for which the minting needs to be executed
                     */
                    function mintToTreasury(address[] calldata assets) external;
                    /**
                     * @notice Rescue and transfer tokens locked in this contract
                     * @param token The address of the token
                     * @param to The address of the recipient
                     * @param amount The amount of token to transfer
                     */
                    function rescueTokens(
                      address token,
                      address to,
                      uint256 amount
                    ) external;
                    /**
                     * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                     * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                     * @dev Deprecated: Use the `supply` function instead
                     * @param asset The address of the underlying asset to supply
                     * @param amount The amount to be supplied
                     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                     *   is a different wallet
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function deposit(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode
                    ) external;
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity ^0.8.0;
                  /**
                   * @title IPoolAddressesProvider
                   * @author Aave
                   * @notice Defines the basic interface for a Pool Addresses Provider.
                   */
                  interface IPoolAddressesProvider {
                    /**
                     * @dev Emitted when the market identifier is updated.
                     * @param oldMarketId The old id of the market
                     * @param newMarketId The new id of the market
                     */
                    event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);
                    /**
                     * @dev Emitted when the pool is updated.
                     * @param oldAddress The old address of the Pool
                     * @param newAddress The new address of the Pool
                     */
                    event PoolUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the pool configurator is updated.
                     * @param oldAddress The old address of the PoolConfigurator
                     * @param newAddress The new address of the PoolConfigurator
                     */
                    event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the price oracle is updated.
                     * @param oldAddress The old address of the PriceOracle
                     * @param newAddress The new address of the PriceOracle
                     */
                    event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the ACL manager is updated.
                     * @param oldAddress The old address of the ACLManager
                     * @param newAddress The new address of the ACLManager
                     */
                    event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the ACL admin is updated.
                     * @param oldAddress The old address of the ACLAdmin
                     * @param newAddress The new address of the ACLAdmin
                     */
                    event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the price oracle sentinel is updated.
                     * @param oldAddress The old address of the PriceOracleSentinel
                     * @param newAddress The new address of the PriceOracleSentinel
                     */
                    event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the pool data provider is updated.
                     * @param oldAddress The old address of the PoolDataProvider
                     * @param newAddress The new address of the PoolDataProvider
                     */
                    event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when a new proxy is created.
                     * @param id The identifier of the proxy
                     * @param proxyAddress The address of the created proxy contract
                     * @param implementationAddress The address of the implementation contract
                     */
                    event ProxyCreated(
                      bytes32 indexed id,
                      address indexed proxyAddress,
                      address indexed implementationAddress
                    );
                    /**
                     * @dev Emitted when a new non-proxied contract address is registered.
                     * @param id The identifier of the contract
                     * @param oldAddress The address of the old contract
                     * @param newAddress The address of the new contract
                     */
                    event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the implementation of the proxy registered with id is updated
                     * @param id The identifier of the contract
                     * @param proxyAddress The address of the proxy contract
                     * @param oldImplementationAddress The address of the old implementation contract
                     * @param newImplementationAddress The address of the new implementation contract
                     */
                    event AddressSetAsProxy(
                      bytes32 indexed id,
                      address indexed proxyAddress,
                      address oldImplementationAddress,
                      address indexed newImplementationAddress
                    );
                    /**
                     * @notice Returns the id of the Aave market to which this contract points to.
                     * @return The market id
                     */
                    function getMarketId() external view returns (string memory);
                    /**
                     * @notice Associates an id with a specific PoolAddressesProvider.
                     * @dev This can be used to create an onchain registry of PoolAddressesProviders to
                     * identify and validate multiple Aave markets.
                     * @param newMarketId The market id
                     */
                    function setMarketId(string calldata newMarketId) external;
                    /**
                     * @notice Returns an address by its identifier.
                     * @dev The returned address might be an EOA or a contract, potentially proxied
                     * @dev It returns ZERO if there is no registered address with the given id
                     * @param id The id
                     * @return The address of the registered for the specified id
                     */
                    function getAddress(bytes32 id) external view returns (address);
                    /**
                     * @notice General function to update the implementation of a proxy registered with
                     * certain `id`. If there is no proxy registered, it will instantiate one and
                     * set as implementation the `newImplementationAddress`.
                     * @dev IMPORTANT Use this function carefully, only for ids that don't have an explicit
                     * setter function, in order to avoid unexpected consequences
                     * @param id The id
                     * @param newImplementationAddress The address of the new implementation
                     */
                    function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;
                    /**
                     * @notice Sets an address for an id replacing the address saved in the addresses map.
                     * @dev IMPORTANT Use this function carefully, as it will do a hard replacement
                     * @param id The id
                     * @param newAddress The address to set
                     */
                    function setAddress(bytes32 id, address newAddress) external;
                    /**
                     * @notice Returns the address of the Pool proxy.
                     * @return The Pool proxy address
                     */
                    function getPool() external view returns (address);
                    /**
                     * @notice Updates the implementation of the Pool, or creates a proxy
                     * setting the new `pool` implementation when the function is called for the first time.
                     * @param newPoolImpl The new Pool implementation
                     */
                    function setPoolImpl(address newPoolImpl) external;
                    /**
                     * @notice Returns the address of the PoolConfigurator proxy.
                     * @return The PoolConfigurator proxy address
                     */
                    function getPoolConfigurator() external view returns (address);
                    /**
                     * @notice Updates the implementation of the PoolConfigurator, or creates a proxy
                     * setting the new `PoolConfigurator` implementation when the function is called for the first time.
                     * @param newPoolConfiguratorImpl The new PoolConfigurator implementation
                     */
                    function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;
                    /**
                     * @notice Returns the address of the price oracle.
                     * @return The address of the PriceOracle
                     */
                    function getPriceOracle() external view returns (address);
                    /**
                     * @notice Updates the address of the price oracle.
                     * @param newPriceOracle The address of the new PriceOracle
                     */
                    function setPriceOracle(address newPriceOracle) external;
                    /**
                     * @notice Returns the address of the ACL manager.
                     * @return The address of the ACLManager
                     */
                    function getACLManager() external view returns (address);
                    /**
                     * @notice Updates the address of the ACL manager.
                     * @param newAclManager The address of the new ACLManager
                     */
                    function setACLManager(address newAclManager) external;
                    /**
                     * @notice Returns the address of the ACL admin.
                     * @return The address of the ACL admin
                     */
                    function getACLAdmin() external view returns (address);
                    /**
                     * @notice Updates the address of the ACL admin.
                     * @param newAclAdmin The address of the new ACL admin
                     */
                    function setACLAdmin(address newAclAdmin) external;
                    /**
                     * @notice Returns the address of the price oracle sentinel.
                     * @return The address of the PriceOracleSentinel
                     */
                    function getPriceOracleSentinel() external view returns (address);
                    /**
                     * @notice Updates the address of the price oracle sentinel.
                     * @param newPriceOracleSentinel The address of the new PriceOracleSentinel
                     */
                    function setPriceOracleSentinel(address newPriceOracleSentinel) external;
                    /**
                     * @notice Returns the address of the data provider.
                     * @return The address of the DataProvider
                     */
                    function getPoolDataProvider() external view returns (address);
                    /**
                     * @notice Updates the address of the data provider.
                     * @param newDataProvider The address of the new DataProvider
                     */
                    function setPoolDataProvider(address newDataProvider) external;
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity ^0.8.0;
                  /**
                   * @title IScaledBalanceToken
                   * @author Aave
                   * @notice Defines the basic interface for a scaled-balance token.
                   */
                  interface IScaledBalanceToken {
                    /**
                     * @dev Emitted after the mint action
                     * @param caller The address performing the mint
                     * @param onBehalfOf The address of the user that will receive the minted tokens
                     * @param value The scaled-up amount being minted (based on user entered amount and balance increase from interest)
                     * @param balanceIncrease The increase in scaled-up balance since the last action of 'onBehalfOf'
                     * @param index The next liquidity index of the reserve
                     */
                    event Mint(
                      address indexed caller,
                      address indexed onBehalfOf,
                      uint256 value,
                      uint256 balanceIncrease,
                      uint256 index
                    );
                    /**
                     * @dev Emitted after the burn action
                     * @dev If the burn function does not involve a transfer of the underlying asset, the target defaults to zero address
                     * @param from The address from which the tokens will be burned
                     * @param target The address that will receive the underlying, if any
                     * @param value The scaled-up amount being burned (user entered amount - balance increase from interest)
                     * @param balanceIncrease The increase in scaled-up balance since the last action of 'from'
                     * @param index The next liquidity index of the reserve
                     */
                    event Burn(
                      address indexed from,
                      address indexed target,
                      uint256 value,
                      uint256 balanceIncrease,
                      uint256 index
                    );
                    /**
                     * @notice Returns the scaled balance of the user.
                     * @dev The scaled balance is the sum of all the updated stored balance divided by the reserve's liquidity index
                     * at the moment of the update
                     * @param user The user whose balance is calculated
                     * @return The scaled balance of the user
                     */
                    function scaledBalanceOf(address user) external view returns (uint256);
                    /**
                     * @notice Returns the scaled balance of the user and the scaled total supply.
                     * @param user The address of the user
                     * @return The scaled balance of the user
                     * @return The scaled total supply
                     */
                    function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
                    /**
                     * @notice Returns the scaled total supply of the scaled balance token. Represents sum(debt/index)
                     * @return The scaled total supply
                     */
                    function scaledTotalSupply() external view returns (uint256);
                    /**
                     * @notice Returns last index interest was accrued to the user's balance
                     * @param user The address of the user
                     * @return The last index interest was accrued to the user's balance, expressed in ray
                     */
                    function getPreviousIndex(address user) external view returns (uint256);
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  /**
                   * @title VersionedInitializable
                   * @author Aave, inspired by the OpenZeppelin Initializable contract
                   * @notice Helper contract to implement initializer functions. To use it, replace
                   * the constructor with a function that has the `initializer` modifier.
                   * @dev WARNING: Unlike constructors, initializer functions must be manually
                   * invoked. This applies both to deploying an Initializable contract, as well
                   * as extending an Initializable contract via inheritance.
                   * WARNING: When used with inheritance, manual care must be taken to not invoke
                   * a parent initializer twice, or ensure that all initializers are idempotent,
                   * because this is not dealt with automatically as with constructors.
                   */
                  abstract contract VersionedInitializable {
                    /**
                     * @dev Indicates that the contract has been initialized.
                     */
                    uint256 private lastInitializedRevision = 0;
                    /**
                     * @dev Indicates that the contract is in the process of being initialized.
                     */
                    bool private initializing;
                    /**
                     * @dev Modifier to use in the initializer function of a contract.
                     */
                    modifier initializer() {
                      uint256 revision = getRevision();
                      require(
                        initializing || isConstructor() || revision > lastInitializedRevision,
                        'Contract instance has already been initialized'
                      );
                      bool isTopLevelCall = !initializing;
                      if (isTopLevelCall) {
                        initializing = true;
                        lastInitializedRevision = revision;
                      }
                      _;
                      if (isTopLevelCall) {
                        initializing = false;
                      }
                    }
                    /**
                     * @notice Returns the revision number of the contract
                     * @dev Needs to be defined in the inherited class as a constant.
                     * @return The revision number
                     */
                    function getRevision() internal pure virtual returns (uint256);
                    /**
                     * @notice Returns true if and only if the function is running in the constructor
                     * @return True if the function is running in the constructor
                     */
                    function isConstructor() private view returns (bool) {
                      // extcodesize checks the size of the code stored in an address, and
                      // address returns the current address. Since the code is still not
                      // deployed when running a constructor, any checks on its code size will
                      // yield zero, making it an effective way to detect if a contract is
                      // under construction or not.
                      uint256 cs;
                      //solium-disable-next-line
                      assembly {
                        cs := extcodesize(address())
                      }
                      return cs == 0;
                    }
                    // Reserved storage space to allow for layout changes in the future.
                    uint256[50] private ______gap;
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  /**
                   * @title Errors library
                   * @author Aave
                   * @notice Defines the error messages emitted by the different contracts of the Aave protocol
                   */
                  library Errors {
                    string public constant CALLER_NOT_POOL_ADMIN = '1'; // 'The caller of the function is not a pool admin'
                    string public constant CALLER_NOT_EMERGENCY_ADMIN = '2'; // 'The caller of the function is not an emergency admin'
                    string public constant CALLER_NOT_POOL_OR_EMERGENCY_ADMIN = '3'; // 'The caller of the function is not a pool or emergency admin'
                    string public constant CALLER_NOT_RISK_OR_POOL_ADMIN = '4'; // 'The caller of the function is not a risk or pool admin'
                    string public constant CALLER_NOT_ASSET_LISTING_OR_POOL_ADMIN = '5'; // 'The caller of the function is not an asset listing or pool admin'
                    string public constant CALLER_NOT_BRIDGE = '6'; // 'The caller of the function is not a bridge'
                    string public constant ADDRESSES_PROVIDER_NOT_REGISTERED = '7'; // 'Pool addresses provider is not registered'
                    string public constant INVALID_ADDRESSES_PROVIDER_ID = '8'; // 'Invalid id for the pool addresses provider'
                    string public constant NOT_CONTRACT = '9'; // 'Address is not a contract'
                    string public constant CALLER_NOT_POOL_CONFIGURATOR = '10'; // 'The caller of the function is not the pool configurator'
                    string public constant CALLER_NOT_ATOKEN = '11'; // 'The caller of the function is not an AToken'
                    string public constant INVALID_ADDRESSES_PROVIDER = '12'; // 'The address of the pool addresses provider is invalid'
                    string public constant INVALID_FLASHLOAN_EXECUTOR_RETURN = '13'; // 'Invalid return value of the flashloan executor function'
                    string public constant RESERVE_ALREADY_ADDED = '14'; // 'Reserve has already been added to reserve list'
                    string public constant NO_MORE_RESERVES_ALLOWED = '15'; // 'Maximum amount of reserves in the pool reached'
                    string public constant EMODE_CATEGORY_RESERVED = '16'; // 'Zero eMode category is reserved for volatile heterogeneous assets'
                    string public constant INVALID_EMODE_CATEGORY_ASSIGNMENT = '17'; // 'Invalid eMode category assignment to asset'
                    string public constant RESERVE_LIQUIDITY_NOT_ZERO = '18'; // 'The liquidity of the reserve needs to be 0'
                    string public constant FLASHLOAN_PREMIUM_INVALID = '19'; // 'Invalid flashloan premium'
                    string public constant INVALID_RESERVE_PARAMS = '20'; // 'Invalid risk parameters for the reserve'
                    string public constant INVALID_EMODE_CATEGORY_PARAMS = '21'; // 'Invalid risk parameters for the eMode category'
                    string public constant BRIDGE_PROTOCOL_FEE_INVALID = '22'; // 'Invalid bridge protocol fee'
                    string public constant CALLER_MUST_BE_POOL = '23'; // 'The caller of this function must be a pool'
                    string public constant INVALID_MINT_AMOUNT = '24'; // 'Invalid amount to mint'
                    string public constant INVALID_BURN_AMOUNT = '25'; // 'Invalid amount to burn'
                    string public constant INVALID_AMOUNT = '26'; // 'Amount must be greater than 0'
                    string public constant RESERVE_INACTIVE = '27'; // 'Action requires an active reserve'
                    string public constant RESERVE_FROZEN = '28'; // 'Action cannot be performed because the reserve is frozen'
                    string public constant RESERVE_PAUSED = '29'; // 'Action cannot be performed because the reserve is paused'
                    string public constant BORROWING_NOT_ENABLED = '30'; // 'Borrowing is not enabled'
                    string public constant STABLE_BORROWING_NOT_ENABLED = '31'; // 'Stable borrowing is not enabled'
                    string public constant NOT_ENOUGH_AVAILABLE_USER_BALANCE = '32'; // 'User cannot withdraw more than the available balance'
                    string public constant INVALID_INTEREST_RATE_MODE_SELECTED = '33'; // 'Invalid interest rate mode selected'
                    string public constant COLLATERAL_BALANCE_IS_ZERO = '34'; // 'The collateral balance is 0'
                    string public constant HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '35'; // 'Health factor is lesser than the liquidation threshold'
                    string public constant COLLATERAL_CANNOT_COVER_NEW_BORROW = '36'; // 'There is not enough collateral to cover a new borrow'
                    string public constant COLLATERAL_SAME_AS_BORROWING_CURRENCY = '37'; // 'Collateral is (mostly) the same currency that is being borrowed'
                    string public constant AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE = '38'; // 'The requested amount is greater than the max loan size in stable rate mode'
                    string public constant NO_DEBT_OF_SELECTED_TYPE = '39'; // 'For repayment of a specific type of debt, the user needs to have debt that type'
                    string public constant NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '40'; // 'To repay on behalf of a user an explicit amount to repay is needed'
                    string public constant NO_OUTSTANDING_STABLE_DEBT = '41'; // 'User does not have outstanding stable rate debt on this reserve'
                    string public constant NO_OUTSTANDING_VARIABLE_DEBT = '42'; // 'User does not have outstanding variable rate debt on this reserve'
                    string public constant UNDERLYING_BALANCE_ZERO = '43'; // 'The underlying balance needs to be greater than 0'
                    string public constant INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '44'; // 'Interest rate rebalance conditions were not met'
                    string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45'; // 'Health factor is not below the threshold'
                    string public constant COLLATERAL_CANNOT_BE_LIQUIDATED = '46'; // 'The collateral chosen cannot be liquidated'
                    string public constant SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47'; // 'User did not borrow the specified currency'
                    string public constant INCONSISTENT_FLASHLOAN_PARAMS = '49'; // 'Inconsistent flashloan parameters'
                    string public constant BORROW_CAP_EXCEEDED = '50'; // 'Borrow cap is exceeded'
                    string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'
                    string public constant UNBACKED_MINT_CAP_EXCEEDED = '52'; // 'Unbacked mint cap is exceeded'
                    string public constant DEBT_CEILING_EXCEEDED = '53'; // 'Debt ceiling is exceeded'
                    string public constant UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54'; // 'Claimable rights over underlying not zero (aToken supply or accruedToTreasury)'
                    string public constant STABLE_DEBT_NOT_ZERO = '55'; // 'Stable debt supply is not zero'
                    string public constant VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56'; // 'Variable debt supply is not zero'
                    string public constant LTV_VALIDATION_FAILED = '57'; // 'Ltv validation failed'
                    string public constant INCONSISTENT_EMODE_CATEGORY = '58'; // 'Inconsistent eMode category'
                    string public constant PRICE_ORACLE_SENTINEL_CHECK_FAILED = '59'; // 'Price oracle sentinel validation failed'
                    string public constant ASSET_NOT_BORROWABLE_IN_ISOLATION = '60'; // 'Asset is not borrowable in isolation mode'
                    string public constant RESERVE_ALREADY_INITIALIZED = '61'; // 'Reserve has already been initialized'
                    string public constant USER_IN_ISOLATION_MODE = '62'; // 'User is in isolation mode'
                    string public constant INVALID_LTV = '63'; // 'Invalid ltv parameter for the reserve'
                    string public constant INVALID_LIQ_THRESHOLD = '64'; // 'Invalid liquidity threshold parameter for the reserve'
                    string public constant INVALID_LIQ_BONUS = '65'; // 'Invalid liquidity bonus parameter for the reserve'
                    string public constant INVALID_DECIMALS = '66'; // 'Invalid decimals parameter of the underlying asset of the reserve'
                    string public constant INVALID_RESERVE_FACTOR = '67'; // 'Invalid reserve factor parameter for the reserve'
                    string public constant INVALID_BORROW_CAP = '68'; // 'Invalid borrow cap for the reserve'
                    string public constant INVALID_SUPPLY_CAP = '69'; // 'Invalid supply cap for the reserve'
                    string public constant INVALID_LIQUIDATION_PROTOCOL_FEE = '70'; // 'Invalid liquidation protocol fee for the reserve'
                    string public constant INVALID_EMODE_CATEGORY = '71'; // 'Invalid eMode category for the reserve'
                    string public constant INVALID_UNBACKED_MINT_CAP = '72'; // 'Invalid unbacked mint cap for the reserve'
                    string public constant INVALID_DEBT_CEILING = '73'; // 'Invalid debt ceiling for the reserve
                    string public constant INVALID_RESERVE_INDEX = '74'; // 'Invalid reserve index'
                    string public constant ACL_ADMIN_CANNOT_BE_ZERO = '75'; // 'ACL admin cannot be set to the zero address'
                    string public constant INCONSISTENT_PARAMS_LENGTH = '76'; // 'Array parameters that should be equal length are not'
                    string public constant ZERO_ADDRESS_NOT_VALID = '77'; // 'Zero address not valid'
                    string public constant INVALID_EXPIRATION = '78'; // 'Invalid expiration'
                    string public constant INVALID_SIGNATURE = '79'; // 'Invalid signature'
                    string public constant OPERATION_NOT_SUPPORTED = '80'; // 'Operation not supported'
                    string public constant DEBT_CEILING_NOT_ZERO = '81'; // 'Debt ceiling is not zero'
                    string public constant ASSET_NOT_LISTED = '82'; // 'Asset is not listed'
                    string public constant INVALID_OPTIMAL_USAGE_RATIO = '83'; // 'Invalid optimal usage ratio'
                    string public constant INVALID_OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO = '84'; // 'Invalid optimal stable to total debt ratio'
                    string public constant UNDERLYING_CANNOT_BE_RESCUED = '85'; // 'The underlying asset cannot be rescued'
                    string public constant ADDRESSES_PROVIDER_ALREADY_ADDED = '86'; // 'Reserve has already been added to reserve list'
                    string public constant POOL_ADDRESSES_DO_NOT_MATCH = '87'; // 'The token implementation pool address and the pool address provided by the initializing pool do not match'
                    string public constant STABLE_BORROWING_ENABLED = '88'; // 'Stable borrowing is enabled'
                    string public constant SILOED_BORROWING_VIOLATION = '89'; // 'User is trying to borrow multiple assets including a siloed one'
                    string public constant RESERVE_DEBT_NOT_ZERO = '90'; // the total debt of the reserve needs to be 0
                    string public constant FLASHLOAN_DISABLED = '91'; // FlashLoaning for this asset is disabled
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  /**
                   * @title WadRayMath library
                   * @author Aave
                   * @notice Provides functions to perform calculations with Wad and Ray units
                   * @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers
                   * with 27 digits of precision)
                   * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                   */
                  library WadRayMath {
                    // HALF_WAD and HALF_RAY expressed with extended notation as constant with operations are not supported in Yul assembly
                    uint256 internal constant WAD = 1e18;
                    uint256 internal constant HALF_WAD = 0.5e18;
                    uint256 internal constant RAY = 1e27;
                    uint256 internal constant HALF_RAY = 0.5e27;
                    uint256 internal constant WAD_RAY_RATIO = 1e9;
                    /**
                     * @dev Multiplies two wad, rounding half up to the nearest wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @param b Wad
                     * @return c = a*b, in wad
                     */
                    function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b
                      assembly {
                        if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, b), HALF_WAD), WAD)
                      }
                    }
                    /**
                     * @dev Divides two wad, rounding half up to the nearest wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @param b Wad
                     * @return c = a/b, in wad
                     */
                    function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - halfB) / WAD
                      assembly {
                        if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, WAD), div(b, 2)), b)
                      }
                    }
                    /**
                     * @notice Multiplies two ray, rounding half up to the nearest ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @param b Ray
                     * @return c = a raymul b
                     */
                    function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - HALF_RAY) / b
                      assembly {
                        if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, b), HALF_RAY), RAY)
                      }
                    }
                    /**
                     * @notice Divides two ray, rounding half up to the nearest ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @param b Ray
                     * @return c = a raydiv b
                     */
                    function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - halfB) / RAY
                      assembly {
                        if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, RAY), div(b, 2)), b)
                      }
                    }
                    /**
                     * @dev Casts ray down to wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @return b = a converted to wad, rounded half up to the nearest wad
                     */
                    function rayToWad(uint256 a) internal pure returns (uint256 b) {
                      assembly {
                        b := div(a, WAD_RAY_RATIO)
                        let remainder := mod(a, WAD_RAY_RATIO)
                        if iszero(lt(remainder, div(WAD_RAY_RATIO, 2))) {
                          b := add(b, 1)
                        }
                      }
                    }
                    /**
                     * @dev Converts wad up to ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @return b = a converted in ray
                     */
                    function wadToRay(uint256 a) internal pure returns (uint256 b) {
                      // to avoid overflow, b/WAD_RAY_RATIO == a
                      assembly {
                        b := mul(a, WAD_RAY_RATIO)
                        if iszero(eq(div(b, WAD_RAY_RATIO), a)) {
                          revert(0, 0)
                        }
                      }
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  library DataTypes {
                    struct ReserveData {
                      //stores the reserve configuration
                      ReserveConfigurationMap configuration;
                      //the liquidity index. Expressed in ray
                      uint128 liquidityIndex;
                      //the current supply rate. Expressed in ray
                      uint128 currentLiquidityRate;
                      //variable borrow index. Expressed in ray
                      uint128 variableBorrowIndex;
                      //the current variable borrow rate. Expressed in ray
                      uint128 currentVariableBorrowRate;
                      //the current stable borrow rate. Expressed in ray
                      uint128 currentStableBorrowRate;
                      //timestamp of last update
                      uint40 lastUpdateTimestamp;
                      //the id of the reserve. Represents the position in the list of the active reserves
                      uint16 id;
                      //aToken address
                      address aTokenAddress;
                      //stableDebtToken address
                      address stableDebtTokenAddress;
                      //variableDebtToken address
                      address variableDebtTokenAddress;
                      //address of the interest rate strategy
                      address interestRateStrategyAddress;
                      //the current treasury balance, scaled
                      uint128 accruedToTreasury;
                      //the outstanding unbacked aTokens minted through the bridging feature
                      uint128 unbacked;
                      //the outstanding debt borrowed against this asset in isolation mode
                      uint128 isolationModeTotalDebt;
                    }
                    struct ReserveConfigurationMap {
                      //bit 0-15: LTV
                      //bit 16-31: Liq. threshold
                      //bit 32-47: Liq. bonus
                      //bit 48-55: Decimals
                      //bit 56: reserve is active
                      //bit 57: reserve is frozen
                      //bit 58: borrowing is enabled
                      //bit 59: stable rate borrowing enabled
                      //bit 60: asset is paused
                      //bit 61: borrowing in isolation mode is enabled
                      //bit 62-63: reserved
                      //bit 64-79: reserve factor
                      //bit 80-115 borrow cap in whole tokens, borrowCap == 0 => no cap
                      //bit 116-151 supply cap in whole tokens, supplyCap == 0 => no cap
                      //bit 152-167 liquidation protocol fee
                      //bit 168-175 eMode category
                      //bit 176-211 unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
                      //bit 212-251 debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
                      //bit 252-255 unused
                      uint256 data;
                    }
                    struct UserConfigurationMap {
                      /**
                       * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.
                       * The first bit indicates if an asset is used as collateral by the user, the second whether an
                       * asset is borrowed by the user.
                       */
                      uint256 data;
                    }
                    struct EModeCategory {
                      // each eMode category has a custom ltv and liquidation threshold
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                      // each eMode category may or may not have a custom oracle to override the individual assets price oracles
                      address priceSource;
                      string label;
                    }
                    enum InterestRateMode {
                      NONE,
                      STABLE,
                      VARIABLE
                    }
                    struct ReserveCache {
                      uint256 currScaledVariableDebt;
                      uint256 nextScaledVariableDebt;
                      uint256 currPrincipalStableDebt;
                      uint256 currAvgStableBorrowRate;
                      uint256 currTotalStableDebt;
                      uint256 nextAvgStableBorrowRate;
                      uint256 nextTotalStableDebt;
                      uint256 currLiquidityIndex;
                      uint256 nextLiquidityIndex;
                      uint256 currVariableBorrowIndex;
                      uint256 nextVariableBorrowIndex;
                      uint256 currLiquidityRate;
                      uint256 currVariableBorrowRate;
                      uint256 reserveFactor;
                      ReserveConfigurationMap reserveConfiguration;
                      address aTokenAddress;
                      address stableDebtTokenAddress;
                      address variableDebtTokenAddress;
                      uint40 reserveLastUpdateTimestamp;
                      uint40 stableDebtLastUpdateTimestamp;
                    }
                    struct ExecuteLiquidationCallParams {
                      uint256 reservesCount;
                      uint256 debtToCover;
                      address collateralAsset;
                      address debtAsset;
                      address user;
                      bool receiveAToken;
                      address priceOracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                    }
                    struct ExecuteSupplyParams {
                      address asset;
                      uint256 amount;
                      address onBehalfOf;
                      uint16 referralCode;
                    }
                    struct ExecuteBorrowParams {
                      address asset;
                      address user;
                      address onBehalfOf;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      uint16 referralCode;
                      bool releaseUnderlying;
                      uint256 maxStableRateBorrowSizePercent;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                    }
                    struct ExecuteRepayParams {
                      address asset;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      address onBehalfOf;
                      bool useATokens;
                    }
                    struct ExecuteWithdrawParams {
                      address asset;
                      uint256 amount;
                      address to;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                    }
                    struct ExecuteSetUserEModeParams {
                      uint256 reservesCount;
                      address oracle;
                      uint8 categoryId;
                    }
                    struct FinalizeTransferParams {
                      address asset;
                      address from;
                      address to;
                      uint256 amount;
                      uint256 balanceFromBefore;
                      uint256 balanceToBefore;
                      uint256 reservesCount;
                      address oracle;
                      uint8 fromEModeCategory;
                    }
                    struct FlashloanParams {
                      address receiverAddress;
                      address[] assets;
                      uint256[] amounts;
                      uint256[] interestRateModes;
                      address onBehalfOf;
                      bytes params;
                      uint16 referralCode;
                      uint256 flashLoanPremiumToProtocol;
                      uint256 flashLoanPremiumTotal;
                      uint256 maxStableRateBorrowSizePercent;
                      uint256 reservesCount;
                      address addressesProvider;
                      uint8 userEModeCategory;
                      bool isAuthorizedFlashBorrower;
                    }
                    struct FlashloanSimpleParams {
                      address receiverAddress;
                      address asset;
                      uint256 amount;
                      bytes params;
                      uint16 referralCode;
                      uint256 flashLoanPremiumToProtocol;
                      uint256 flashLoanPremiumTotal;
                    }
                    struct FlashLoanRepaymentParams {
                      uint256 amount;
                      uint256 totalPremium;
                      uint256 flashLoanPremiumToProtocol;
                      address asset;
                      address receiverAddress;
                      uint16 referralCode;
                    }
                    struct CalculateUserAccountDataParams {
                      UserConfigurationMap userConfig;
                      uint256 reservesCount;
                      address user;
                      address oracle;
                      uint8 userEModeCategory;
                    }
                    struct ValidateBorrowParams {
                      ReserveCache reserveCache;
                      UserConfigurationMap userConfig;
                      address asset;
                      address userAddress;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      uint256 maxStableLoanPercent;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                      bool isolationModeActive;
                      address isolationModeCollateralAddress;
                      uint256 isolationModeDebtCeiling;
                    }
                    struct ValidateLiquidationCallParams {
                      ReserveCache debtReserveCache;
                      uint256 totalDebt;
                      uint256 healthFactor;
                      address priceOracleSentinel;
                    }
                    struct CalculateInterestRatesParams {
                      uint256 unbacked;
                      uint256 liquidityAdded;
                      uint256 liquidityTaken;
                      uint256 totalStableDebt;
                      uint256 totalVariableDebt;
                      uint256 averageStableBorrowRate;
                      uint256 reserveFactor;
                      address reserve;
                      address aToken;
                    }
                    struct InitReserveParams {
                      address asset;
                      address aTokenAddress;
                      address stableDebtAddress;
                      address variableDebtAddress;
                      address interestRateStrategyAddress;
                      uint16 reservesCount;
                      uint16 maxNumberReserves;
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.10;
                  import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {GPv2SafeERC20} from '../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {SafeCast} from '../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
                  import {Errors} from '../libraries/helpers/Errors.sol';
                  import {WadRayMath} from '../libraries/math/WadRayMath.sol';
                  import {IPool} from '../../interfaces/IPool.sol';
                  import {IAToken} from '../../interfaces/IAToken.sol';
                  import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
                  import {IInitializableAToken} from '../../interfaces/IInitializableAToken.sol';
                  import {ScaledBalanceTokenBase} from './base/ScaledBalanceTokenBase.sol';
                  import {IncentivizedERC20} from './base/IncentivizedERC20.sol';
                  import {EIP712Base} from './base/EIP712Base.sol';
                  /**
                   * @title Aave ERC20 AToken
                   * @author Aave
                   * @notice Implementation of the interest bearing token for the Aave protocol
                   */
                  contract AToken is VersionedInitializable, ScaledBalanceTokenBase, EIP712Base, IAToken {
                    using WadRayMath for uint256;
                    using SafeCast for uint256;
                    using GPv2SafeERC20 for IERC20;
                    bytes32 public constant PERMIT_TYPEHASH =
                      keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');
                    uint256 public constant ATOKEN_REVISION = 0x1;
                    address internal _treasury;
                    address internal _underlyingAsset;
                    /// @inheritdoc VersionedInitializable
                    function getRevision() internal pure virtual override returns (uint256) {
                      return ATOKEN_REVISION;
                    }
                    /**
                     * @dev Constructor.
                     * @param pool The address of the Pool contract
                     */
                    constructor(IPool pool)
                      ScaledBalanceTokenBase(pool, 'ATOKEN_IMPL', 'ATOKEN_IMPL', 0)
                      EIP712Base()
                    {
                      // Intentionally left blank
                    }
                    /// @inheritdoc IInitializableAToken
                    function initialize(
                      IPool initializingPool,
                      address treasury,
                      address underlyingAsset,
                      IAaveIncentivesController incentivesController,
                      uint8 aTokenDecimals,
                      string calldata aTokenName,
                      string calldata aTokenSymbol,
                      bytes calldata params
                    ) public virtual override initializer {
                      require(initializingPool == POOL, Errors.POOL_ADDRESSES_DO_NOT_MATCH);
                      _setName(aTokenName);
                      _setSymbol(aTokenSymbol);
                      _setDecimals(aTokenDecimals);
                      _treasury = treasury;
                      _underlyingAsset = underlyingAsset;
                      _incentivesController = incentivesController;
                      _domainSeparator = _calculateDomainSeparator();
                      emit Initialized(
                        underlyingAsset,
                        address(POOL),
                        treasury,
                        address(incentivesController),
                        aTokenDecimals,
                        aTokenName,
                        aTokenSymbol,
                        params
                      );
                    }
                    /// @inheritdoc IAToken
                    function mint(
                      address caller,
                      address onBehalfOf,
                      uint256 amount,
                      uint256 index
                    ) external virtual override onlyPool returns (bool) {
                      return _mintScaled(caller, onBehalfOf, amount, index);
                    }
                    /// @inheritdoc IAToken
                    function burn(
                      address from,
                      address receiverOfUnderlying,
                      uint256 amount,
                      uint256 index
                    ) external virtual override onlyPool {
                      _burnScaled(from, receiverOfUnderlying, amount, index);
                      if (receiverOfUnderlying != address(this)) {
                        IERC20(_underlyingAsset).safeTransfer(receiverOfUnderlying, amount);
                      }
                    }
                    /// @inheritdoc IAToken
                    function mintToTreasury(uint256 amount, uint256 index) external virtual override onlyPool {
                      if (amount == 0) {
                        return;
                      }
                      _mintScaled(address(POOL), _treasury, amount, index);
                    }
                    /// @inheritdoc IAToken
                    function transferOnLiquidation(
                      address from,
                      address to,
                      uint256 value
                    ) external virtual override onlyPool {
                      // Being a normal transfer, the Transfer() and BalanceTransfer() are emitted
                      // so no need to emit a specific event here
                      _transfer(from, to, value, false);
                    }
                    /// @inheritdoc IERC20
                    function balanceOf(address user)
                      public
                      view
                      virtual
                      override(IncentivizedERC20, IERC20)
                      returns (uint256)
                    {
                      return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(_underlyingAsset));
                    }
                    /// @inheritdoc IERC20
                    function totalSupply() public view virtual override(IncentivizedERC20, IERC20) returns (uint256) {
                      uint256 currentSupplyScaled = super.totalSupply();
                      if (currentSupplyScaled == 0) {
                        return 0;
                      }
                      return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(_underlyingAsset));
                    }
                    /// @inheritdoc IAToken
                    function RESERVE_TREASURY_ADDRESS() external view override returns (address) {
                      return _treasury;
                    }
                    /// @inheritdoc IAToken
                    function UNDERLYING_ASSET_ADDRESS() external view override returns (address) {
                      return _underlyingAsset;
                    }
                    /// @inheritdoc IAToken
                    function transferUnderlyingTo(address target, uint256 amount) external virtual override onlyPool {
                      IERC20(_underlyingAsset).safeTransfer(target, amount);
                    }
                    /// @inheritdoc IAToken
                    function handleRepayment(
                      address user,
                      address onBehalfOf,
                      uint256 amount
                    ) external virtual override onlyPool {
                      // Intentionally left blank
                    }
                    /// @inheritdoc IAToken
                    function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                    ) external override {
                      require(owner != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                      //solium-disable-next-line
                      require(block.timestamp <= deadline, Errors.INVALID_EXPIRATION);
                      uint256 currentValidNonce = _nonces[owner];
                      bytes32 digest = keccak256(
                        abi.encodePacked(
                          '\\x19\\x01',
                          DOMAIN_SEPARATOR(),
                          keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
                        )
                      );
                      require(owner == ecrecover(digest, v, r, s), Errors.INVALID_SIGNATURE);
                      _nonces[owner] = currentValidNonce + 1;
                      _approve(owner, spender, value);
                    }
                    /**
                     * @notice Transfers the aTokens between two users. Validates the transfer
                     * (ie checks for valid HF after the transfer) if required
                     * @param from The source address
                     * @param to The destination address
                     * @param amount The amount getting transferred
                     * @param validate True if the transfer needs to be validated, false otherwise
                     */
                    function _transfer(
                      address from,
                      address to,
                      uint256 amount,
                      bool validate
                    ) internal virtual {
                      address underlyingAsset = _underlyingAsset;
                      uint256 index = POOL.getReserveNormalizedIncome(underlyingAsset);
                      uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index);
                      uint256 toBalanceBefore = super.balanceOf(to).rayMul(index);
                      super._transfer(from, to, amount, index);
                      if (validate) {
                        POOL.finalizeTransfer(underlyingAsset, from, to, amount, fromBalanceBefore, toBalanceBefore);
                      }
                      emit BalanceTransfer(from, to, amount.rayDiv(index), index);
                    }
                    /**
                     * @notice Overrides the parent _transfer to force validated transfer() and transferFrom()
                     * @param from The source address
                     * @param to The destination address
                     * @param amount The amount getting transferred
                     */
                    function _transfer(
                      address from,
                      address to,
                      uint128 amount
                    ) internal virtual override {
                      _transfer(from, to, amount, true);
                    }
                    /**
                     * @dev Overrides the base function to fully implement IAToken
                     * @dev see `EIP712Base.DOMAIN_SEPARATOR()` for more detailed documentation
                     */
                    function DOMAIN_SEPARATOR() public view override(IAToken, EIP712Base) returns (bytes32) {
                      return super.DOMAIN_SEPARATOR();
                    }
                    /**
                     * @dev Overrides the base function to fully implement IAToken
                     * @dev see `EIP712Base.nonces()` for more detailed documentation
                     */
                    function nonces(address owner) public view override(IAToken, EIP712Base) returns (uint256) {
                      return super.nonces(owner);
                    }
                    /// @inheritdoc EIP712Base
                    function _EIP712BaseId() internal view override returns (string memory) {
                      return name();
                    }
                    /// @inheritdoc IAToken
                    function rescueTokens(
                      address token,
                      address to,
                      uint256 amount
                    ) external override onlyPoolAdmin {
                      require(token != _underlyingAsset, Errors.UNDERLYING_CANNOT_BE_RESCUED);
                      IERC20(token).safeTransfer(to, amount);
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.10;
                  /**
                   * @title EIP712Base
                   * @author Aave
                   * @notice Base contract implementation of EIP712.
                   */
                  abstract contract EIP712Base {
                    bytes public constant EIP712_REVISION = bytes('1');
                    bytes32 internal constant EIP712_DOMAIN =
                      keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');
                    // Map of address nonces (address => nonce)
                    mapping(address => uint256) internal _nonces;
                    bytes32 internal _domainSeparator;
                    uint256 internal immutable _chainId;
                    /**
                     * @dev Constructor.
                     */
                    constructor() {
                      _chainId = block.chainid;
                    }
                    /**
                     * @notice Get the domain separator for the token
                     * @dev Return cached value if chainId matches cache, otherwise recomputes separator
                     * @return The domain separator of the token at current chain
                     */
                    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
                      if (block.chainid == _chainId) {
                        return _domainSeparator;
                      }
                      return _calculateDomainSeparator();
                    }
                    /**
                     * @notice Returns the nonce value for address specified as parameter
                     * @param owner The address for which the nonce is being returned
                     * @return The nonce value for the input address`
                     */
                    function nonces(address owner) public view virtual returns (uint256) {
                      return _nonces[owner];
                    }
                    /**
                     * @notice Compute the current domain separator
                     * @return The domain separator for the token
                     */
                    function _calculateDomainSeparator() internal view returns (bytes32) {
                      return
                        keccak256(
                          abi.encode(
                            EIP712_DOMAIN,
                            keccak256(bytes(_EIP712BaseId())),
                            keccak256(EIP712_REVISION),
                            block.chainid,
                            address(this)
                          )
                        );
                    }
                    /**
                     * @notice Returns the user readable name of signing domain (e.g. token name)
                     * @return The name of the signing domain
                     */
                    function _EIP712BaseId() internal view virtual returns (string memory);
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.10;
                  import {Context} from '../../../dependencies/openzeppelin/contracts/Context.sol';
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {IERC20Detailed} from '../../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                  import {Errors} from '../../libraries/helpers/Errors.sol';
                  import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
                  import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                  import {IPool} from '../../../interfaces/IPool.sol';
                  import {IACLManager} from '../../../interfaces/IACLManager.sol';
                  /**
                   * @title IncentivizedERC20
                   * @author Aave, inspired by the Openzeppelin ERC20 implementation
                   * @notice Basic ERC20 implementation
                   */
                  abstract contract IncentivizedERC20 is Context, IERC20Detailed {
                    using WadRayMath for uint256;
                    using SafeCast for uint256;
                    /**
                     * @dev Only pool admin can call functions marked by this modifier.
                     */
                    modifier onlyPoolAdmin() {
                      IACLManager aclManager = IACLManager(_addressesProvider.getACLManager());
                      require(aclManager.isPoolAdmin(msg.sender), Errors.CALLER_NOT_POOL_ADMIN);
                      _;
                    }
                    /**
                     * @dev Only pool can call functions marked by this modifier.
                     */
                    modifier onlyPool() {
                      require(_msgSender() == address(POOL), Errors.CALLER_MUST_BE_POOL);
                      _;
                    }
                    /**
                     * @dev UserState - additionalData is a flexible field.
                     * ATokens and VariableDebtTokens use this field store the index of the
                     * user's last supply/withdrawal/borrow/repayment. StableDebtTokens use
                     * this field to store the user's stable rate.
                     */
                    struct UserState {
                      uint128 balance;
                      uint128 additionalData;
                    }
                    // Map of users address and their state data (userAddress => userStateData)
                    mapping(address => UserState) internal _userState;
                    // Map of allowances (delegator => delegatee => allowanceAmount)
                    mapping(address => mapping(address => uint256)) private _allowances;
                    uint256 internal _totalSupply;
                    string private _name;
                    string private _symbol;
                    uint8 private _decimals;
                    IAaveIncentivesController internal _incentivesController;
                    IPoolAddressesProvider internal immutable _addressesProvider;
                    IPool public immutable POOL;
                    /**
                     * @dev Constructor.
                     * @param pool The reference to the main Pool contract
                     * @param name The name of the token
                     * @param symbol The symbol of the token
                     * @param decimals The number of decimals of the token
                     */
                    constructor(
                      IPool pool,
                      string memory name,
                      string memory symbol,
                      uint8 decimals
                    ) {
                      _addressesProvider = pool.ADDRESSES_PROVIDER();
                      _name = name;
                      _symbol = symbol;
                      _decimals = decimals;
                      POOL = pool;
                    }
                    /// @inheritdoc IERC20Detailed
                    function name() public view override returns (string memory) {
                      return _name;
                    }
                    /// @inheritdoc IERC20Detailed
                    function symbol() external view override returns (string memory) {
                      return _symbol;
                    }
                    /// @inheritdoc IERC20Detailed
                    function decimals() external view override returns (uint8) {
                      return _decimals;
                    }
                    /// @inheritdoc IERC20
                    function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                    }
                    /// @inheritdoc IERC20
                    function balanceOf(address account) public view virtual override returns (uint256) {
                      return _userState[account].balance;
                    }
                    /**
                     * @notice Returns the address of the Incentives Controller contract
                     * @return The address of the Incentives Controller
                     */
                    function getIncentivesController() external view virtual returns (IAaveIncentivesController) {
                      return _incentivesController;
                    }
                    /**
                     * @notice Sets a new Incentives Controller
                     * @param controller the new Incentives controller
                     */
                    function setIncentivesController(IAaveIncentivesController controller) external onlyPoolAdmin {
                      _incentivesController = controller;
                    }
                    /// @inheritdoc IERC20
                    function transfer(address recipient, uint256 amount) external virtual override returns (bool) {
                      uint128 castAmount = amount.toUint128();
                      _transfer(_msgSender(), recipient, castAmount);
                      return true;
                    }
                    /// @inheritdoc IERC20
                    function allowance(address owner, address spender)
                      external
                      view
                      virtual
                      override
                      returns (uint256)
                    {
                      return _allowances[owner][spender];
                    }
                    /// @inheritdoc IERC20
                    function approve(address spender, uint256 amount) external virtual override returns (bool) {
                      _approve(_msgSender(), spender, amount);
                      return true;
                    }
                    /// @inheritdoc IERC20
                    function transferFrom(
                      address sender,
                      address recipient,
                      uint256 amount
                    ) external virtual override returns (bool) {
                      uint128 castAmount = amount.toUint128();
                      _approve(sender, _msgSender(), _allowances[sender][_msgSender()] - castAmount);
                      _transfer(sender, recipient, castAmount);
                      return true;
                    }
                    /**
                     * @notice Increases the allowance of spender to spend _msgSender() tokens
                     * @param spender The user allowed to spend on behalf of _msgSender()
                     * @param addedValue The amount being added to the allowance
                     * @return `true`
                     */
                    function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool) {
                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
                      return true;
                    }
                    /**
                     * @notice Decreases the allowance of spender to spend _msgSender() tokens
                     * @param spender The user allowed to spend on behalf of _msgSender()
                     * @param subtractedValue The amount being subtracted to the allowance
                     * @return `true`
                     */
                    function decreaseAllowance(address spender, uint256 subtractedValue)
                      external
                      virtual
                      returns (bool)
                    {
                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue);
                      return true;
                    }
                    /**
                     * @notice Transfers tokens between two users and apply incentives if defined.
                     * @param sender The source address
                     * @param recipient The destination address
                     * @param amount The amount getting transferred
                     */
                    function _transfer(
                      address sender,
                      address recipient,
                      uint128 amount
                    ) internal virtual {
                      uint128 oldSenderBalance = _userState[sender].balance;
                      _userState[sender].balance = oldSenderBalance - amount;
                      uint128 oldRecipientBalance = _userState[recipient].balance;
                      _userState[recipient].balance = oldRecipientBalance + amount;
                      IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                      if (address(incentivesControllerLocal) != address(0)) {
                        uint256 currentTotalSupply = _totalSupply;
                        incentivesControllerLocal.handleAction(sender, currentTotalSupply, oldSenderBalance);
                        if (sender != recipient) {
                          incentivesControllerLocal.handleAction(recipient, currentTotalSupply, oldRecipientBalance);
                        }
                      }
                    }
                    /**
                     * @notice Approve `spender` to use `amount` of `owner`s balance
                     * @param owner The address owning the tokens
                     * @param spender The address approved for spending
                     * @param amount The amount of tokens to approve spending of
                     */
                    function _approve(
                      address owner,
                      address spender,
                      uint256 amount
                    ) internal virtual {
                      _allowances[owner][spender] = amount;
                      emit Approval(owner, spender, amount);
                    }
                    /**
                     * @notice Update the name of the token
                     * @param newName The new name for the token
                     */
                    function _setName(string memory newName) internal {
                      _name = newName;
                    }
                    /**
                     * @notice Update the symbol for the token
                     * @param newSymbol The new symbol for the token
                     */
                    function _setSymbol(string memory newSymbol) internal {
                      _symbol = newSymbol;
                    }
                    /**
                     * @notice Update the number of decimals for the token
                     * @param newDecimals The new number of decimals for the token
                     */
                    function _setDecimals(uint8 newDecimals) internal {
                      _decimals = newDecimals;
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.10;
                  import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
                  import {IPool} from '../../../interfaces/IPool.sol';
                  import {IncentivizedERC20} from './IncentivizedERC20.sol';
                  /**
                   * @title MintableIncentivizedERC20
                   * @author Aave
                   * @notice Implements mint and burn functions for IncentivizedERC20
                   */
                  abstract contract MintableIncentivizedERC20 is IncentivizedERC20 {
                    /**
                     * @dev Constructor.
                     * @param pool The reference to the main Pool contract
                     * @param name The name of the token
                     * @param symbol The symbol of the token
                     * @param decimals The number of decimals of the token
                     */
                    constructor(
                      IPool pool,
                      string memory name,
                      string memory symbol,
                      uint8 decimals
                    ) IncentivizedERC20(pool, name, symbol, decimals) {
                      // Intentionally left blank
                    }
                    /**
                     * @notice Mints tokens to an account and apply incentives if defined
                     * @param account The address receiving tokens
                     * @param amount The amount of tokens to mint
                     */
                    function _mint(address account, uint128 amount) internal virtual {
                      uint256 oldTotalSupply = _totalSupply;
                      _totalSupply = oldTotalSupply + amount;
                      uint128 oldAccountBalance = _userState[account].balance;
                      _userState[account].balance = oldAccountBalance + amount;
                      IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                      if (address(incentivesControllerLocal) != address(0)) {
                        incentivesControllerLocal.handleAction(account, oldTotalSupply, oldAccountBalance);
                      }
                    }
                    /**
                     * @notice Burns tokens from an account and apply incentives if defined
                     * @param account The account whose tokens are burnt
                     * @param amount The amount of tokens to burn
                     */
                    function _burn(address account, uint128 amount) internal virtual {
                      uint256 oldTotalSupply = _totalSupply;
                      _totalSupply = oldTotalSupply - amount;
                      uint128 oldAccountBalance = _userState[account].balance;
                      _userState[account].balance = oldAccountBalance - amount;
                      IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                      if (address(incentivesControllerLocal) != address(0)) {
                        incentivesControllerLocal.handleAction(account, oldTotalSupply, oldAccountBalance);
                      }
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.10;
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  import {Errors} from '../../libraries/helpers/Errors.sol';
                  import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                  import {IPool} from '../../../interfaces/IPool.sol';
                  import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
                  import {MintableIncentivizedERC20} from './MintableIncentivizedERC20.sol';
                  /**
                   * @title ScaledBalanceTokenBase
                   * @author Aave
                   * @notice Basic ERC20 implementation of scaled balance token
                   */
                  abstract contract ScaledBalanceTokenBase is MintableIncentivizedERC20, IScaledBalanceToken {
                    using WadRayMath for uint256;
                    using SafeCast for uint256;
                    /**
                     * @dev Constructor.
                     * @param pool The reference to the main Pool contract
                     * @param name The name of the token
                     * @param symbol The symbol of the token
                     * @param decimals The number of decimals of the token
                     */
                    constructor(
                      IPool pool,
                      string memory name,
                      string memory symbol,
                      uint8 decimals
                    ) MintableIncentivizedERC20(pool, name, symbol, decimals) {
                      // Intentionally left blank
                    }
                    /// @inheritdoc IScaledBalanceToken
                    function scaledBalanceOf(address user) external view override returns (uint256) {
                      return super.balanceOf(user);
                    }
                    /// @inheritdoc IScaledBalanceToken
                    function getScaledUserBalanceAndSupply(address user)
                      external
                      view
                      override
                      returns (uint256, uint256)
                    {
                      return (super.balanceOf(user), super.totalSupply());
                    }
                    /// @inheritdoc IScaledBalanceToken
                    function scaledTotalSupply() public view virtual override returns (uint256) {
                      return super.totalSupply();
                    }
                    /// @inheritdoc IScaledBalanceToken
                    function getPreviousIndex(address user) external view virtual override returns (uint256) {
                      return _userState[user].additionalData;
                    }
                    /**
                     * @notice Implements the basic logic to mint a scaled balance token.
                     * @param caller The address performing the mint
                     * @param onBehalfOf The address of the user that will receive the scaled tokens
                     * @param amount The amount of tokens getting minted
                     * @param index The next liquidity index of the reserve
                     * @return `true` if the the previous balance of the user was 0
                     */
                    function _mintScaled(
                      address caller,
                      address onBehalfOf,
                      uint256 amount,
                      uint256 index
                    ) internal returns (bool) {
                      uint256 amountScaled = amount.rayDiv(index);
                      require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT);
                      uint256 scaledBalance = super.balanceOf(onBehalfOf);
                      uint256 balanceIncrease = scaledBalance.rayMul(index) -
                        scaledBalance.rayMul(_userState[onBehalfOf].additionalData);
                      _userState[onBehalfOf].additionalData = index.toUint128();
                      _mint(onBehalfOf, amountScaled.toUint128());
                      uint256 amountToMint = amount + balanceIncrease;
                      emit Transfer(address(0), onBehalfOf, amountToMint);
                      emit Mint(caller, onBehalfOf, amountToMint, balanceIncrease, index);
                      return (scaledBalance == 0);
                    }
                    /**
                     * @notice Implements the basic logic to burn a scaled balance token.
                     * @dev In some instances, a burn transaction will emit a mint event
                     * if the amount to burn is less than the interest that the user accrued
                     * @param user The user which debt is burnt
                     * @param target The address that will receive the underlying, if any
                     * @param amount The amount getting burned
                     * @param index The variable debt index of the reserve
                     */
                    function _burnScaled(
                      address user,
                      address target,
                      uint256 amount,
                      uint256 index
                    ) internal {
                      uint256 amountScaled = amount.rayDiv(index);
                      require(amountScaled != 0, Errors.INVALID_BURN_AMOUNT);
                      uint256 scaledBalance = super.balanceOf(user);
                      uint256 balanceIncrease = scaledBalance.rayMul(index) -
                        scaledBalance.rayMul(_userState[user].additionalData);
                      _userState[user].additionalData = index.toUint128();
                      _burn(user, amountScaled.toUint128());
                      if (balanceIncrease > amount) {
                        uint256 amountToMint = balanceIncrease - amount;
                        emit Transfer(address(0), user, amountToMint);
                        emit Mint(user, user, amountToMint, balanceIncrease, index);
                      } else {
                        uint256 amountToBurn = amount - balanceIncrease;
                        emit Transfer(user, address(0), amountToBurn);
                        emit Burn(user, target, amountToBurn, balanceIncrease, index);
                      }
                    }
                    /**
                     * @notice Implements the basic logic to transfer scaled balance tokens between two users
                     * @dev It emits a mint event with the interest accrued per user
                     * @param sender The source address
                     * @param recipient The destination address
                     * @param amount The amount getting transferred
                     * @param index The next liquidity index of the reserve
                     */
                    function _transfer(
                      address sender,
                      address recipient,
                      uint256 amount,
                      uint256 index
                    ) internal {
                      uint256 senderScaledBalance = super.balanceOf(sender);
                      uint256 senderBalanceIncrease = senderScaledBalance.rayMul(index) -
                        senderScaledBalance.rayMul(_userState[sender].additionalData);
                      uint256 recipientScaledBalance = super.balanceOf(recipient);
                      uint256 recipientBalanceIncrease = recipientScaledBalance.rayMul(index) -
                        recipientScaledBalance.rayMul(_userState[recipient].additionalData);
                      _userState[sender].additionalData = index.toUint128();
                      _userState[recipient].additionalData = index.toUint128();
                      super._transfer(sender, recipient, amount.rayDiv(index).toUint128());
                      if (senderBalanceIncrease > 0) {
                        emit Transfer(address(0), sender, senderBalanceIncrease);
                        emit Mint(_msgSender(), sender, senderBalanceIncrease, senderBalanceIncrease, index);
                      }
                      if (sender != recipient && recipientBalanceIncrease > 0) {
                        emit Transfer(address(0), recipient, recipientBalanceIncrease);
                        emit Mint(_msgSender(), recipient, recipientBalanceIncrease, recipientBalanceIncrease, index);
                      }
                      emit Transfer(sender, recipient, amount);
                    }
                  }
                  

                  File 7 of 10: PoolInstance
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  import {Pool} from '../protocol/pool/Pool.sol';
                  import {IPoolAddressesProvider} from '../interfaces/IPoolAddressesProvider.sol';
                  import {Errors} from '../protocol/libraries/helpers/Errors.sol';
                  contract PoolInstance is Pool {
                    uint256 public constant POOL_REVISION = 7;
                    constructor(IPoolAddressesProvider provider) Pool(provider) {}
                    /**
                     * @notice Initializes the Pool.
                     * @dev Function is invoked by the proxy contract when the Pool contract is added to the
                     * PoolAddressesProvider of the market.
                     * @dev The passed PoolAddressesProvider is validated against the POOL.ADDRESSES_PROVIDER, to ensure the upgrade is done with correct intention.
                     * @param provider The address of the PoolAddressesProvider
                     */
                    function initialize(IPoolAddressesProvider provider) external virtual override initializer {
                      require(provider == ADDRESSES_PROVIDER, Errors.INVALID_ADDRESSES_PROVIDER);
                    }
                    function getRevision() internal pure virtual override returns (uint256) {
                      return POOL_REVISION;
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {VersionedInitializable} from '../../misc/aave-upgradeability/VersionedInitializable.sol';
                  import {Errors} from '../libraries/helpers/Errors.sol';
                  import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
                  import {PoolLogic} from '../libraries/logic/PoolLogic.sol';
                  import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
                  import {EModeLogic} from '../libraries/logic/EModeLogic.sol';
                  import {SupplyLogic} from '../libraries/logic/SupplyLogic.sol';
                  import {FlashLoanLogic} from '../libraries/logic/FlashLoanLogic.sol';
                  import {BorrowLogic} from '../libraries/logic/BorrowLogic.sol';
                  import {LiquidationLogic} from '../libraries/logic/LiquidationLogic.sol';
                  import {DataTypes} from '../libraries/types/DataTypes.sol';
                  import {BridgeLogic} from '../libraries/logic/BridgeLogic.sol';
                  import {IERC20WithPermit} from '../../interfaces/IERC20WithPermit.sol';
                  import {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol';
                  import {IPool} from '../../interfaces/IPool.sol';
                  import {IACLManager} from '../../interfaces/IACLManager.sol';
                  import {PoolStorage} from './PoolStorage.sol';
                  /**
                   * @title Pool contract
                   * @author Aave
                   * @notice Main point of interaction with an Aave protocol's market
                   * - Users can:
                   *   # Supply
                   *   # Withdraw
                   *   # Borrow
                   *   # Repay
                   *   # Enable/disable their supplied assets as collateral
                   *   # Liquidate positions
                   *   # Execute Flash Loans
                   * @dev To be covered by a proxy contract, owned by the PoolAddressesProvider of the specific market
                   * @dev All admin functions are callable by the PoolConfigurator contract defined also in the
                   *   PoolAddressesProvider
                   */
                  abstract contract Pool is VersionedInitializable, PoolStorage, IPool {
                    using ReserveLogic for DataTypes.ReserveData;
                    IPoolAddressesProvider public immutable ADDRESSES_PROVIDER;
                    // @notice The name used to fetch the UMBRELLA contract
                    bytes32 public constant UMBRELLA = 'UMBRELLA';
                    /**
                     * @dev Only pool configurator can call functions marked by this modifier.
                     */
                    modifier onlyPoolConfigurator() {
                      _onlyPoolConfigurator();
                      _;
                    }
                    /**
                     * @dev Only pool admin can call functions marked by this modifier.
                     */
                    modifier onlyPoolAdmin() {
                      _onlyPoolAdmin();
                      _;
                    }
                    /**
                     * @dev Only bridge can call functions marked by this modifier.
                     */
                    modifier onlyBridge() {
                      _onlyBridge();
                      _;
                    }
                    /**
                     * @dev Only the umbrella contract can call functions marked by this modifier.
                     */
                    modifier onlyUmbrella() {
                      require(ADDRESSES_PROVIDER.getAddress(UMBRELLA) == msg.sender, Errors.CALLER_NOT_UMBRELLA);
                      _;
                    }
                    function _onlyPoolConfigurator() internal view virtual {
                      require(
                        ADDRESSES_PROVIDER.getPoolConfigurator() == msg.sender,
                        Errors.CALLER_NOT_POOL_CONFIGURATOR
                      );
                    }
                    function _onlyPoolAdmin() internal view virtual {
                      require(
                        IACLManager(ADDRESSES_PROVIDER.getACLManager()).isPoolAdmin(msg.sender),
                        Errors.CALLER_NOT_POOL_ADMIN
                      );
                    }
                    function _onlyBridge() internal view virtual {
                      require(
                        IACLManager(ADDRESSES_PROVIDER.getACLManager()).isBridge(msg.sender),
                        Errors.CALLER_NOT_BRIDGE
                      );
                    }
                    /**
                     * @dev Constructor.
                     * @param provider The address of the PoolAddressesProvider contract
                     */
                    constructor(IPoolAddressesProvider provider) {
                      ADDRESSES_PROVIDER = provider;
                    }
                    /**
                     * @notice Initializes the Pool.
                     * @dev Function is invoked by the proxy contract when the Pool contract is added to the
                     * PoolAddressesProvider of the market.
                     * @dev Caching the address of the PoolAddressesProvider in order to reduce gas consumption on subsequent operations
                     * @param provider The address of the PoolAddressesProvider
                     */
                    function initialize(IPoolAddressesProvider provider) external virtual;
                    /// @inheritdoc IPool
                    function mintUnbacked(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode
                    ) external virtual override onlyBridge {
                      BridgeLogic.executeMintUnbacked(
                        _reserves,
                        _reservesList,
                        _usersConfig[onBehalfOf],
                        asset,
                        amount,
                        onBehalfOf,
                        referralCode
                      );
                    }
                    /// @inheritdoc IPool
                    function backUnbacked(
                      address asset,
                      uint256 amount,
                      uint256 fee
                    ) external virtual override onlyBridge returns (uint256) {
                      return
                        BridgeLogic.executeBackUnbacked(_reserves[asset], asset, amount, fee, _bridgeProtocolFee);
                    }
                    /// @inheritdoc IPool
                    function supply(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode
                    ) public virtual override {
                      SupplyLogic.executeSupply(
                        _reserves,
                        _reservesList,
                        _usersConfig[onBehalfOf],
                        DataTypes.ExecuteSupplyParams({
                          asset: asset,
                          amount: amount,
                          onBehalfOf: onBehalfOf,
                          referralCode: referralCode
                        })
                      );
                    }
                    /// @inheritdoc IPool
                    function supplyWithPermit(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode,
                      uint256 deadline,
                      uint8 permitV,
                      bytes32 permitR,
                      bytes32 permitS
                    ) public virtual override {
                      try
                        IERC20WithPermit(asset).permit(
                          msg.sender,
                          address(this),
                          amount,
                          deadline,
                          permitV,
                          permitR,
                          permitS
                        )
                      {} catch {}
                      SupplyLogic.executeSupply(
                        _reserves,
                        _reservesList,
                        _usersConfig[onBehalfOf],
                        DataTypes.ExecuteSupplyParams({
                          asset: asset,
                          amount: amount,
                          onBehalfOf: onBehalfOf,
                          referralCode: referralCode
                        })
                      );
                    }
                    /// @inheritdoc IPool
                    function withdraw(
                      address asset,
                      uint256 amount,
                      address to
                    ) public virtual override returns (uint256) {
                      return
                        SupplyLogic.executeWithdraw(
                          _reserves,
                          _reservesList,
                          _eModeCategories,
                          _usersConfig[msg.sender],
                          DataTypes.ExecuteWithdrawParams({
                            asset: asset,
                            amount: amount,
                            to: to,
                            reservesCount: _reservesCount,
                            oracle: ADDRESSES_PROVIDER.getPriceOracle(),
                            userEModeCategory: _usersEModeCategory[msg.sender]
                          })
                        );
                    }
                    /// @inheritdoc IPool
                    function borrow(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      uint16 referralCode,
                      address onBehalfOf
                    ) public virtual override {
                      BorrowLogic.executeBorrow(
                        _reserves,
                        _reservesList,
                        _eModeCategories,
                        _usersConfig[onBehalfOf],
                        DataTypes.ExecuteBorrowParams({
                          asset: asset,
                          user: msg.sender,
                          onBehalfOf: onBehalfOf,
                          amount: amount,
                          interestRateMode: DataTypes.InterestRateMode(interestRateMode),
                          referralCode: referralCode,
                          releaseUnderlying: true,
                          reservesCount: _reservesCount,
                          oracle: ADDRESSES_PROVIDER.getPriceOracle(),
                          userEModeCategory: _usersEModeCategory[onBehalfOf],
                          priceOracleSentinel: ADDRESSES_PROVIDER.getPriceOracleSentinel()
                        })
                      );
                    }
                    /// @inheritdoc IPool
                    function repay(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      address onBehalfOf
                    ) public virtual override returns (uint256) {
                      return
                        BorrowLogic.executeRepay(
                          _reserves,
                          _reservesList,
                          _usersConfig[onBehalfOf],
                          DataTypes.ExecuteRepayParams({
                            asset: asset,
                            amount: amount,
                            interestRateMode: DataTypes.InterestRateMode(interestRateMode),
                            onBehalfOf: onBehalfOf,
                            useATokens: false
                          })
                        );
                    }
                    /// @inheritdoc IPool
                    function repayWithPermit(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      address onBehalfOf,
                      uint256 deadline,
                      uint8 permitV,
                      bytes32 permitR,
                      bytes32 permitS
                    ) public virtual override returns (uint256) {
                      try
                        IERC20WithPermit(asset).permit(
                          msg.sender,
                          address(this),
                          amount,
                          deadline,
                          permitV,
                          permitR,
                          permitS
                        )
                      {} catch {}
                      {
                        DataTypes.ExecuteRepayParams memory params = DataTypes.ExecuteRepayParams({
                          asset: asset,
                          amount: amount,
                          interestRateMode: DataTypes.InterestRateMode(interestRateMode),
                          onBehalfOf: onBehalfOf,
                          useATokens: false
                        });
                        return BorrowLogic.executeRepay(_reserves, _reservesList, _usersConfig[onBehalfOf], params);
                      }
                    }
                    /// @inheritdoc IPool
                    function repayWithATokens(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode
                    ) public virtual override returns (uint256) {
                      return
                        BorrowLogic.executeRepay(
                          _reserves,
                          _reservesList,
                          _usersConfig[msg.sender],
                          DataTypes.ExecuteRepayParams({
                            asset: asset,
                            amount: amount,
                            interestRateMode: DataTypes.InterestRateMode(interestRateMode),
                            onBehalfOf: msg.sender,
                            useATokens: true
                          })
                        );
                    }
                    /// @inheritdoc IPool
                    function setUserUseReserveAsCollateral(
                      address asset,
                      bool useAsCollateral
                    ) public virtual override {
                      SupplyLogic.executeUseReserveAsCollateral(
                        _reserves,
                        _reservesList,
                        _eModeCategories,
                        _usersConfig[msg.sender],
                        asset,
                        useAsCollateral,
                        _reservesCount,
                        ADDRESSES_PROVIDER.getPriceOracle(),
                        _usersEModeCategory[msg.sender]
                      );
                    }
                    /// @inheritdoc IPool
                    function liquidationCall(
                      address collateralAsset,
                      address debtAsset,
                      address user,
                      uint256 debtToCover,
                      bool receiveAToken
                    ) public virtual override {
                      LiquidationLogic.executeLiquidationCall(
                        _reserves,
                        _reservesList,
                        _usersConfig,
                        _eModeCategories,
                        DataTypes.ExecuteLiquidationCallParams({
                          reservesCount: _reservesCount,
                          debtToCover: debtToCover,
                          collateralAsset: collateralAsset,
                          debtAsset: debtAsset,
                          user: user,
                          receiveAToken: receiveAToken,
                          priceOracle: ADDRESSES_PROVIDER.getPriceOracle(),
                          userEModeCategory: _usersEModeCategory[user],
                          priceOracleSentinel: ADDRESSES_PROVIDER.getPriceOracleSentinel()
                        })
                      );
                    }
                    /// @inheritdoc IPool
                    function flashLoan(
                      address receiverAddress,
                      address[] calldata assets,
                      uint256[] calldata amounts,
                      uint256[] calldata interestRateModes,
                      address onBehalfOf,
                      bytes calldata params,
                      uint16 referralCode
                    ) public virtual override {
                      DataTypes.FlashloanParams memory flashParams = DataTypes.FlashloanParams({
                        receiverAddress: receiverAddress,
                        assets: assets,
                        amounts: amounts,
                        interestRateModes: interestRateModes,
                        onBehalfOf: onBehalfOf,
                        params: params,
                        referralCode: referralCode,
                        flashLoanPremiumToProtocol: _flashLoanPremiumToProtocol,
                        flashLoanPremiumTotal: _flashLoanPremiumTotal,
                        reservesCount: _reservesCount,
                        addressesProvider: address(ADDRESSES_PROVIDER),
                        pool: address(this),
                        userEModeCategory: _usersEModeCategory[onBehalfOf],
                        isAuthorizedFlashBorrower: IACLManager(ADDRESSES_PROVIDER.getACLManager()).isFlashBorrower(
                          msg.sender
                        )
                      });
                      FlashLoanLogic.executeFlashLoan(
                        _reserves,
                        _reservesList,
                        _eModeCategories,
                        _usersConfig[onBehalfOf],
                        flashParams
                      );
                    }
                    /// @inheritdoc IPool
                    function flashLoanSimple(
                      address receiverAddress,
                      address asset,
                      uint256 amount,
                      bytes calldata params,
                      uint16 referralCode
                    ) public virtual override {
                      DataTypes.FlashloanSimpleParams memory flashParams = DataTypes.FlashloanSimpleParams({
                        receiverAddress: receiverAddress,
                        asset: asset,
                        amount: amount,
                        params: params,
                        referralCode: referralCode,
                        flashLoanPremiumToProtocol: _flashLoanPremiumToProtocol,
                        flashLoanPremiumTotal: _flashLoanPremiumTotal
                      });
                      FlashLoanLogic.executeFlashLoanSimple(_reserves[asset], flashParams);
                    }
                    /// @inheritdoc IPool
                    function mintToTreasury(address[] calldata assets) external virtual override {
                      PoolLogic.executeMintToTreasury(_reserves, assets);
                    }
                    /// @inheritdoc IPool
                    function getReserveData(
                      address asset
                    ) external view virtual override returns (DataTypes.ReserveDataLegacy memory) {
                      DataTypes.ReserveData storage reserve = _reserves[asset];
                      DataTypes.ReserveDataLegacy memory res;
                      res.configuration = reserve.configuration;
                      res.liquidityIndex = reserve.liquidityIndex;
                      res.currentLiquidityRate = reserve.currentLiquidityRate;
                      res.variableBorrowIndex = reserve.variableBorrowIndex;
                      res.currentVariableBorrowRate = reserve.currentVariableBorrowRate;
                      res.lastUpdateTimestamp = reserve.lastUpdateTimestamp;
                      res.id = reserve.id;
                      res.aTokenAddress = reserve.aTokenAddress;
                      res.variableDebtTokenAddress = reserve.variableDebtTokenAddress;
                      res.interestRateStrategyAddress = reserve.interestRateStrategyAddress;
                      res.accruedToTreasury = reserve.accruedToTreasury;
                      res.unbacked = reserve.unbacked;
                      res.isolationModeTotalDebt = reserve.isolationModeTotalDebt;
                      // This is a temporary workaround for integrations that are broken by Aave 3.2
                      // While the new pool data provider is backward compatible, some integrations hard-code an old implementation
                      // To allow them to not have any infrastructural blocker, a mock must be configured in the Aave Pool Addresses Provider, returning zero on all required view methods, instead of reverting
                      res.stableDebtTokenAddress = ADDRESSES_PROVIDER.getAddress(bytes32('MOCK_STABLE_DEBT'));
                      return res;
                    }
                    /// @inheritdoc IPool
                    function getVirtualUnderlyingBalance(
                      address asset
                    ) external view virtual override returns (uint128) {
                      return _reserves[asset].virtualUnderlyingBalance;
                    }
                    /// @inheritdoc IPool
                    function getUserAccountData(
                      address user
                    )
                      external
                      view
                      virtual
                      override
                      returns (
                        uint256 totalCollateralBase,
                        uint256 totalDebtBase,
                        uint256 availableBorrowsBase,
                        uint256 currentLiquidationThreshold,
                        uint256 ltv,
                        uint256 healthFactor
                      )
                    {
                      return
                        PoolLogic.executeGetUserAccountData(
                          _reserves,
                          _reservesList,
                          _eModeCategories,
                          DataTypes.CalculateUserAccountDataParams({
                            userConfig: _usersConfig[user],
                            reservesCount: _reservesCount,
                            user: user,
                            oracle: ADDRESSES_PROVIDER.getPriceOracle(),
                            userEModeCategory: _usersEModeCategory[user]
                          })
                        );
                    }
                    /// @inheritdoc IPool
                    function getConfiguration(
                      address asset
                    ) external view virtual override returns (DataTypes.ReserveConfigurationMap memory) {
                      return _reserves[asset].configuration;
                    }
                    /// @inheritdoc IPool
                    function getUserConfiguration(
                      address user
                    ) external view virtual override returns (DataTypes.UserConfigurationMap memory) {
                      return _usersConfig[user];
                    }
                    /// @inheritdoc IPool
                    function getReserveNormalizedIncome(
                      address asset
                    ) external view virtual override returns (uint256) {
                      return _reserves[asset].getNormalizedIncome();
                    }
                    /// @inheritdoc IPool
                    function getReserveNormalizedVariableDebt(
                      address asset
                    ) external view virtual override returns (uint256) {
                      return _reserves[asset].getNormalizedDebt();
                    }
                    /// @inheritdoc IPool
                    function getReservesList() external view virtual override returns (address[] memory) {
                      uint256 reservesListCount = _reservesCount;
                      uint256 droppedReservesCount = 0;
                      address[] memory reservesList = new address[](reservesListCount);
                      for (uint256 i = 0; i < reservesListCount; i++) {
                        if (_reservesList[i] != address(0)) {
                          reservesList[i - droppedReservesCount] = _reservesList[i];
                        } else {
                          droppedReservesCount++;
                        }
                      }
                      // Reduces the length of the reserves array by `droppedReservesCount`
                      assembly {
                        mstore(reservesList, sub(reservesListCount, droppedReservesCount))
                      }
                      return reservesList;
                    }
                    /// @inheritdoc IPool
                    function getReservesCount() external view virtual override returns (uint256) {
                      return _reservesCount;
                    }
                    /// @inheritdoc IPool
                    function getReserveAddressById(uint16 id) external view returns (address) {
                      return _reservesList[id];
                    }
                    /// @inheritdoc IPool
                    function BRIDGE_PROTOCOL_FEE() public view virtual override returns (uint256) {
                      return _bridgeProtocolFee;
                    }
                    /// @inheritdoc IPool
                    function FLASHLOAN_PREMIUM_TOTAL() public view virtual override returns (uint128) {
                      return _flashLoanPremiumTotal;
                    }
                    /// @inheritdoc IPool
                    function FLASHLOAN_PREMIUM_TO_PROTOCOL() public view virtual override returns (uint128) {
                      return _flashLoanPremiumToProtocol;
                    }
                    /// @inheritdoc IPool
                    function MAX_NUMBER_RESERVES() public view virtual override returns (uint16) {
                      return ReserveConfiguration.MAX_RESERVES_COUNT;
                    }
                    /// @inheritdoc IPool
                    function finalizeTransfer(
                      address asset,
                      address from,
                      address to,
                      uint256 amount,
                      uint256 balanceFromBefore,
                      uint256 balanceToBefore
                    ) external virtual override {
                      require(msg.sender == _reserves[asset].aTokenAddress, Errors.CALLER_NOT_ATOKEN);
                      SupplyLogic.executeFinalizeTransfer(
                        _reserves,
                        _reservesList,
                        _eModeCategories,
                        _usersConfig,
                        DataTypes.FinalizeTransferParams({
                          asset: asset,
                          from: from,
                          to: to,
                          amount: amount,
                          balanceFromBefore: balanceFromBefore,
                          balanceToBefore: balanceToBefore,
                          reservesCount: _reservesCount,
                          oracle: ADDRESSES_PROVIDER.getPriceOracle(),
                          fromEModeCategory: _usersEModeCategory[from]
                        })
                      );
                    }
                    /// @inheritdoc IPool
                    function initReserve(
                      address asset,
                      address aTokenAddress,
                      address variableDebtAddress,
                      address interestRateStrategyAddress
                    ) external virtual override onlyPoolConfigurator {
                      if (
                        PoolLogic.executeInitReserve(
                          _reserves,
                          _reservesList,
                          DataTypes.InitReserveParams({
                            asset: asset,
                            aTokenAddress: aTokenAddress,
                            variableDebtAddress: variableDebtAddress,
                            interestRateStrategyAddress: interestRateStrategyAddress,
                            reservesCount: _reservesCount,
                            maxNumberReserves: MAX_NUMBER_RESERVES()
                          })
                        )
                      ) {
                        _reservesCount++;
                      }
                    }
                    /// @inheritdoc IPool
                    function dropReserve(address asset) external virtual override onlyPoolConfigurator {
                      PoolLogic.executeDropReserve(_reserves, _reservesList, asset);
                    }
                    /// @inheritdoc IPool
                    function setReserveInterestRateStrategyAddress(
                      address asset,
                      address rateStrategyAddress
                    ) external virtual override onlyPoolConfigurator {
                      require(asset != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                      require(_reserves[asset].id != 0 || _reservesList[0] == asset, Errors.ASSET_NOT_LISTED);
                      _reserves[asset].interestRateStrategyAddress = rateStrategyAddress;
                    }
                    /// @inheritdoc IPool
                    function syncIndexesState(address asset) external virtual override onlyPoolConfigurator {
                      DataTypes.ReserveData storage reserve = _reserves[asset];
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      reserve.updateState(reserveCache);
                    }
                    /// @inheritdoc IPool
                    function syncRatesState(address asset) external virtual override onlyPoolConfigurator {
                      DataTypes.ReserveData storage reserve = _reserves[asset];
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      ReserveLogic.updateInterestRatesAndVirtualBalance(reserve, reserveCache, asset, 0, 0);
                    }
                    /// @inheritdoc IPool
                    function setConfiguration(
                      address asset,
                      DataTypes.ReserveConfigurationMap calldata configuration
                    ) external virtual override onlyPoolConfigurator {
                      require(asset != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                      require(_reserves[asset].id != 0 || _reservesList[0] == asset, Errors.ASSET_NOT_LISTED);
                      _reserves[asset].configuration = configuration;
                    }
                    /// @inheritdoc IPool
                    function updateBridgeProtocolFee(
                      uint256 protocolFee
                    ) external virtual override onlyPoolConfigurator {
                      _bridgeProtocolFee = protocolFee;
                    }
                    /// @inheritdoc IPool
                    function updateFlashloanPremiums(
                      uint128 flashLoanPremiumTotal,
                      uint128 flashLoanPremiumToProtocol
                    ) external virtual override onlyPoolConfigurator {
                      _flashLoanPremiumTotal = flashLoanPremiumTotal;
                      _flashLoanPremiumToProtocol = flashLoanPremiumToProtocol;
                    }
                    /// @inheritdoc IPool
                    function configureEModeCategory(
                      uint8 id,
                      DataTypes.EModeCategoryBaseConfiguration calldata category
                    ) external virtual override onlyPoolConfigurator {
                      // category 0 is reserved for volatile heterogeneous assets and it's always disabled
                      require(id != 0, Errors.EMODE_CATEGORY_RESERVED);
                      _eModeCategories[id].ltv = category.ltv;
                      _eModeCategories[id].liquidationThreshold = category.liquidationThreshold;
                      _eModeCategories[id].liquidationBonus = category.liquidationBonus;
                      _eModeCategories[id].label = category.label;
                    }
                    /// @inheritdoc IPool
                    function configureEModeCategoryCollateralBitmap(
                      uint8 id,
                      uint128 collateralBitmap
                    ) external virtual override onlyPoolConfigurator {
                      // category 0 is reserved for volatile heterogeneous assets and it's always disabled
                      require(id != 0, Errors.EMODE_CATEGORY_RESERVED);
                      _eModeCategories[id].collateralBitmap = collateralBitmap;
                    }
                    /// @inheritdoc IPool
                    function configureEModeCategoryBorrowableBitmap(
                      uint8 id,
                      uint128 borrowableBitmap
                    ) external virtual override onlyPoolConfigurator {
                      // category 0 is reserved for volatile heterogeneous assets and it's always disabled
                      require(id != 0, Errors.EMODE_CATEGORY_RESERVED);
                      _eModeCategories[id].borrowableBitmap = borrowableBitmap;
                    }
                    /// @inheritdoc IPool
                    function getEModeCategoryData(
                      uint8 id
                    ) external view virtual override returns (DataTypes.EModeCategoryLegacy memory) {
                      DataTypes.EModeCategory storage category = _eModeCategories[id];
                      return
                        DataTypes.EModeCategoryLegacy({
                          ltv: category.ltv,
                          liquidationThreshold: category.liquidationThreshold,
                          liquidationBonus: category.liquidationBonus,
                          priceSource: address(0),
                          label: category.label
                        });
                    }
                    /// @inheritdoc IPool
                    function getEModeCategoryCollateralConfig(
                      uint8 id
                    ) external view returns (DataTypes.CollateralConfig memory) {
                      return
                        DataTypes.CollateralConfig({
                          ltv: _eModeCategories[id].ltv,
                          liquidationThreshold: _eModeCategories[id].liquidationThreshold,
                          liquidationBonus: _eModeCategories[id].liquidationBonus
                        });
                    }
                    /// @inheritdoc IPool
                    function getEModeCategoryLabel(uint8 id) external view returns (string memory) {
                      return _eModeCategories[id].label;
                    }
                    /// @inheritdoc IPool
                    function getEModeCategoryCollateralBitmap(uint8 id) external view returns (uint128) {
                      return _eModeCategories[id].collateralBitmap;
                    }
                    /// @inheritdoc IPool
                    function getEModeCategoryBorrowableBitmap(uint8 id) external view returns (uint128) {
                      return _eModeCategories[id].borrowableBitmap;
                    }
                    /// @inheritdoc IPool
                    function setUserEMode(uint8 categoryId) external virtual override {
                      EModeLogic.executeSetUserEMode(
                        _reserves,
                        _reservesList,
                        _eModeCategories,
                        _usersEModeCategory,
                        _usersConfig[msg.sender],
                        DataTypes.ExecuteSetUserEModeParams({
                          reservesCount: _reservesCount,
                          oracle: ADDRESSES_PROVIDER.getPriceOracle(),
                          categoryId: categoryId
                        })
                      );
                    }
                    /// @inheritdoc IPool
                    function getUserEMode(address user) external view virtual override returns (uint256) {
                      return _usersEModeCategory[user];
                    }
                    /// @inheritdoc IPool
                    function resetIsolationModeTotalDebt(
                      address asset
                    ) external virtual override onlyPoolConfigurator {
                      PoolLogic.executeResetIsolationModeTotalDebt(_reserves, asset);
                    }
                    /// @inheritdoc IPool
                    function getLiquidationGracePeriod(
                      address asset
                    ) external view virtual override returns (uint40) {
                      return _reserves[asset].liquidationGracePeriodUntil;
                    }
                    /// @inheritdoc IPool
                    function setLiquidationGracePeriod(
                      address asset,
                      uint40 until
                    ) external virtual override onlyPoolConfigurator {
                      require(_reserves[asset].id != 0 || _reservesList[0] == asset, Errors.ASSET_NOT_LISTED);
                      PoolLogic.executeSetLiquidationGracePeriod(_reserves, asset, until);
                    }
                    /// @inheritdoc IPool
                    function rescueTokens(
                      address token,
                      address to,
                      uint256 amount
                    ) external virtual override onlyPoolAdmin {
                      PoolLogic.executeRescueTokens(token, to, amount);
                    }
                    /// @inheritdoc IPool
                    /// @dev Deprecated: maintained for compatibility purposes
                    function deposit(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode
                    ) external virtual override {
                      SupplyLogic.executeSupply(
                        _reserves,
                        _reservesList,
                        _usersConfig[onBehalfOf],
                        DataTypes.ExecuteSupplyParams({
                          asset: asset,
                          amount: amount,
                          onBehalfOf: onBehalfOf,
                          referralCode: referralCode
                        })
                      );
                    }
                    /// @inheritdoc IPool
                    function eliminateReserveDeficit(address asset, uint256 amount) external override onlyUmbrella {
                      LiquidationLogic.executeEliminateDeficit(
                        _reserves,
                        _usersConfig[msg.sender],
                        DataTypes.ExecuteEliminateDeficitParams({asset: asset, amount: amount})
                      );
                    }
                    /// @inheritdoc IPool
                    function getReserveDeficit(address asset) external view virtual returns (uint256) {
                      return _reserves[asset].deficit;
                    }
                    /// @inheritdoc IPool
                    function getReserveAToken(address asset) external view virtual returns (address) {
                      return _reserves[asset].aTokenAddress;
                    }
                    /// @inheritdoc IPool
                    function getReserveVariableDebtToken(address asset) external view virtual returns (address) {
                      return _reserves[asset].variableDebtTokenAddress;
                    }
                    /// @inheritdoc IPool
                    function getFlashLoanLogic() external pure returns (address) {
                      return address(FlashLoanLogic);
                    }
                    /// @inheritdoc IPool
                    function getBorrowLogic() external pure returns (address) {
                      return address(BorrowLogic);
                    }
                    /// @inheritdoc IPool
                    function getBridgeLogic() external pure returns (address) {
                      return address(BridgeLogic);
                    }
                    /// @inheritdoc IPool
                    function getEModeLogic() external pure returns (address) {
                      return address(EModeLogic);
                    }
                    /// @inheritdoc IPool
                    function getLiquidationLogic() external pure returns (address) {
                      return address(LiquidationLogic);
                    }
                    /// @inheritdoc IPool
                    function getPoolLogic() external pure returns (address) {
                      return address(PoolLogic);
                    }
                    /// @inheritdoc IPool
                    function getSupplyLogic() external pure returns (address) {
                      return address(SupplyLogic);
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title IPoolAddressesProvider
                   * @author Aave
                   * @notice Defines the basic interface for a Pool Addresses Provider.
                   */
                  interface IPoolAddressesProvider {
                    /**
                     * @dev Emitted when the market identifier is updated.
                     * @param oldMarketId The old id of the market
                     * @param newMarketId The new id of the market
                     */
                    event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);
                    /**
                     * @dev Emitted when the pool is updated.
                     * @param oldAddress The old address of the Pool
                     * @param newAddress The new address of the Pool
                     */
                    event PoolUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the pool configurator is updated.
                     * @param oldAddress The old address of the PoolConfigurator
                     * @param newAddress The new address of the PoolConfigurator
                     */
                    event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the price oracle is updated.
                     * @param oldAddress The old address of the PriceOracle
                     * @param newAddress The new address of the PriceOracle
                     */
                    event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the ACL manager is updated.
                     * @param oldAddress The old address of the ACLManager
                     * @param newAddress The new address of the ACLManager
                     */
                    event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the ACL admin is updated.
                     * @param oldAddress The old address of the ACLAdmin
                     * @param newAddress The new address of the ACLAdmin
                     */
                    event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the price oracle sentinel is updated.
                     * @param oldAddress The old address of the PriceOracleSentinel
                     * @param newAddress The new address of the PriceOracleSentinel
                     */
                    event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the pool data provider is updated.
                     * @param oldAddress The old address of the PoolDataProvider
                     * @param newAddress The new address of the PoolDataProvider
                     */
                    event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when a new proxy is created.
                     * @param id The identifier of the proxy
                     * @param proxyAddress The address of the created proxy contract
                     * @param implementationAddress The address of the implementation contract
                     */
                    event ProxyCreated(
                      bytes32 indexed id,
                      address indexed proxyAddress,
                      address indexed implementationAddress
                    );
                    /**
                     * @dev Emitted when a new non-proxied contract address is registered.
                     * @param id The identifier of the contract
                     * @param oldAddress The address of the old contract
                     * @param newAddress The address of the new contract
                     */
                    event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the implementation of the proxy registered with id is updated
                     * @param id The identifier of the contract
                     * @param proxyAddress The address of the proxy contract
                     * @param oldImplementationAddress The address of the old implementation contract
                     * @param newImplementationAddress The address of the new implementation contract
                     */
                    event AddressSetAsProxy(
                      bytes32 indexed id,
                      address indexed proxyAddress,
                      address oldImplementationAddress,
                      address indexed newImplementationAddress
                    );
                    /**
                     * @notice Returns the id of the Aave market to which this contract points to.
                     * @return The market id
                     */
                    function getMarketId() external view returns (string memory);
                    /**
                     * @notice Associates an id with a specific PoolAddressesProvider.
                     * @dev This can be used to create an onchain registry of PoolAddressesProviders to
                     * identify and validate multiple Aave markets.
                     * @param newMarketId The market id
                     */
                    function setMarketId(string calldata newMarketId) external;
                    /**
                     * @notice Returns an address by its identifier.
                     * @dev The returned address might be an EOA or a contract, potentially proxied
                     * @dev It returns ZERO if there is no registered address with the given id
                     * @param id The id
                     * @return The address of the registered for the specified id
                     */
                    function getAddress(bytes32 id) external view returns (address);
                    /**
                     * @notice General function to update the implementation of a proxy registered with
                     * certain `id`. If there is no proxy registered, it will instantiate one and
                     * set as implementation the `newImplementationAddress`.
                     * @dev IMPORTANT Use this function carefully, only for ids that don't have an explicit
                     * setter function, in order to avoid unexpected consequences
                     * @param id The id
                     * @param newImplementationAddress The address of the new implementation
                     */
                    function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;
                    /**
                     * @notice Sets an address for an id replacing the address saved in the addresses map.
                     * @dev IMPORTANT Use this function carefully, as it will do a hard replacement
                     * @param id The id
                     * @param newAddress The address to set
                     */
                    function setAddress(bytes32 id, address newAddress) external;
                    /**
                     * @notice Returns the address of the Pool proxy.
                     * @return The Pool proxy address
                     */
                    function getPool() external view returns (address);
                    /**
                     * @notice Updates the implementation of the Pool, or creates a proxy
                     * setting the new `pool` implementation when the function is called for the first time.
                     * @param newPoolImpl The new Pool implementation
                     */
                    function setPoolImpl(address newPoolImpl) external;
                    /**
                     * @notice Returns the address of the PoolConfigurator proxy.
                     * @return The PoolConfigurator proxy address
                     */
                    function getPoolConfigurator() external view returns (address);
                    /**
                     * @notice Updates the implementation of the PoolConfigurator, or creates a proxy
                     * setting the new `PoolConfigurator` implementation when the function is called for the first time.
                     * @param newPoolConfiguratorImpl The new PoolConfigurator implementation
                     */
                    function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;
                    /**
                     * @notice Returns the address of the price oracle.
                     * @return The address of the PriceOracle
                     */
                    function getPriceOracle() external view returns (address);
                    /**
                     * @notice Updates the address of the price oracle.
                     * @param newPriceOracle The address of the new PriceOracle
                     */
                    function setPriceOracle(address newPriceOracle) external;
                    /**
                     * @notice Returns the address of the ACL manager.
                     * @return The address of the ACLManager
                     */
                    function getACLManager() external view returns (address);
                    /**
                     * @notice Updates the address of the ACL manager.
                     * @param newAclManager The address of the new ACLManager
                     */
                    function setACLManager(address newAclManager) external;
                    /**
                     * @notice Returns the address of the ACL admin.
                     * @return The address of the ACL admin
                     */
                    function getACLAdmin() external view returns (address);
                    /**
                     * @notice Updates the address of the ACL admin.
                     * @param newAclAdmin The address of the new ACL admin
                     */
                    function setACLAdmin(address newAclAdmin) external;
                    /**
                     * @notice Returns the address of the price oracle sentinel.
                     * @return The address of the PriceOracleSentinel
                     */
                    function getPriceOracleSentinel() external view returns (address);
                    /**
                     * @notice Updates the address of the price oracle sentinel.
                     * @param newPriceOracleSentinel The address of the new PriceOracleSentinel
                     */
                    function setPriceOracleSentinel(address newPriceOracleSentinel) external;
                    /**
                     * @notice Returns the address of the data provider.
                     * @return The address of the DataProvider
                     */
                    function getPoolDataProvider() external view returns (address);
                    /**
                     * @notice Updates the address of the data provider.
                     * @param newDataProvider The address of the new DataProvider
                     */
                    function setPoolDataProvider(address newDataProvider) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title Errors library
                   * @author Aave
                   * @notice Defines the error messages emitted by the different contracts of the Aave protocol
                   */
                  library Errors {
                    string public constant CALLER_NOT_POOL_ADMIN = '1'; // 'The caller of the function is not a pool admin'
                    string public constant CALLER_NOT_EMERGENCY_ADMIN = '2'; // 'The caller of the function is not an emergency admin'
                    string public constant CALLER_NOT_POOL_OR_EMERGENCY_ADMIN = '3'; // 'The caller of the function is not a pool or emergency admin'
                    string public constant CALLER_NOT_RISK_OR_POOL_ADMIN = '4'; // 'The caller of the function is not a risk or pool admin'
                    string public constant CALLER_NOT_ASSET_LISTING_OR_POOL_ADMIN = '5'; // 'The caller of the function is not an asset listing or pool admin'
                    string public constant CALLER_NOT_BRIDGE = '6'; // 'The caller of the function is not a bridge'
                    string public constant ADDRESSES_PROVIDER_NOT_REGISTERED = '7'; // 'Pool addresses provider is not registered'
                    string public constant INVALID_ADDRESSES_PROVIDER_ID = '8'; // 'Invalid id for the pool addresses provider'
                    string public constant NOT_CONTRACT = '9'; // 'Address is not a contract'
                    string public constant CALLER_NOT_POOL_CONFIGURATOR = '10'; // 'The caller of the function is not the pool configurator'
                    string public constant CALLER_NOT_ATOKEN = '11'; // 'The caller of the function is not an AToken'
                    string public constant INVALID_ADDRESSES_PROVIDER = '12'; // 'The address of the pool addresses provider is invalid'
                    string public constant INVALID_FLASHLOAN_EXECUTOR_RETURN = '13'; // 'Invalid return value of the flashloan executor function'
                    string public constant RESERVE_ALREADY_ADDED = '14'; // 'Reserve has already been added to reserve list'
                    string public constant NO_MORE_RESERVES_ALLOWED = '15'; // 'Maximum amount of reserves in the pool reached'
                    string public constant EMODE_CATEGORY_RESERVED = '16'; // 'Zero eMode category is reserved for volatile heterogeneous assets'
                    string public constant INVALID_EMODE_CATEGORY_ASSIGNMENT = '17'; // 'Invalid eMode category assignment to asset'
                    string public constant RESERVE_LIQUIDITY_NOT_ZERO = '18'; // 'The liquidity of the reserve needs to be 0'
                    string public constant FLASHLOAN_PREMIUM_INVALID = '19'; // 'Invalid flashloan premium'
                    string public constant INVALID_RESERVE_PARAMS = '20'; // 'Invalid risk parameters for the reserve'
                    string public constant INVALID_EMODE_CATEGORY_PARAMS = '21'; // 'Invalid risk parameters for the eMode category'
                    string public constant BRIDGE_PROTOCOL_FEE_INVALID = '22'; // 'Invalid bridge protocol fee'
                    string public constant CALLER_MUST_BE_POOL = '23'; // 'The caller of this function must be a pool'
                    string public constant INVALID_MINT_AMOUNT = '24'; // 'Invalid amount to mint'
                    string public constant INVALID_BURN_AMOUNT = '25'; // 'Invalid amount to burn'
                    string public constant INVALID_AMOUNT = '26'; // 'Amount must be greater than 0'
                    string public constant RESERVE_INACTIVE = '27'; // 'Action requires an active reserve'
                    string public constant RESERVE_FROZEN = '28'; // 'Action cannot be performed because the reserve is frozen'
                    string public constant RESERVE_PAUSED = '29'; // 'Action cannot be performed because the reserve is paused'
                    string public constant BORROWING_NOT_ENABLED = '30'; // 'Borrowing is not enabled'
                    string public constant NOT_ENOUGH_AVAILABLE_USER_BALANCE = '32'; // 'User cannot withdraw more than the available balance'
                    string public constant INVALID_INTEREST_RATE_MODE_SELECTED = '33'; // 'Invalid interest rate mode selected'
                    string public constant COLLATERAL_BALANCE_IS_ZERO = '34'; // 'The collateral balance is 0'
                    string public constant HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '35'; // 'Health factor is lesser than the liquidation threshold'
                    string public constant COLLATERAL_CANNOT_COVER_NEW_BORROW = '36'; // 'There is not enough collateral to cover a new borrow'
                    string public constant COLLATERAL_SAME_AS_BORROWING_CURRENCY = '37'; // 'Collateral is (mostly) the same currency that is being borrowed'
                    string public constant NO_DEBT_OF_SELECTED_TYPE = '39'; // 'For repayment of a specific type of debt, the user needs to have debt that type'
                    string public constant NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '40'; // 'To repay on behalf of a user an explicit amount to repay is needed'
                    string public constant NO_OUTSTANDING_VARIABLE_DEBT = '42'; // 'User does not have outstanding variable rate debt on this reserve'
                    string public constant UNDERLYING_BALANCE_ZERO = '43'; // 'The underlying balance needs to be greater than 0'
                    string public constant INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '44'; // 'Interest rate rebalance conditions were not met'
                    string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45'; // 'Health factor is not below the threshold'
                    string public constant COLLATERAL_CANNOT_BE_LIQUIDATED = '46'; // 'The collateral chosen cannot be liquidated'
                    string public constant SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47'; // 'User did not borrow the specified currency'
                    string public constant INCONSISTENT_FLASHLOAN_PARAMS = '49'; // 'Inconsistent flashloan parameters'
                    string public constant BORROW_CAP_EXCEEDED = '50'; // 'Borrow cap is exceeded'
                    string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'
                    string public constant UNBACKED_MINT_CAP_EXCEEDED = '52'; // 'Unbacked mint cap is exceeded'
                    string public constant DEBT_CEILING_EXCEEDED = '53'; // 'Debt ceiling is exceeded'
                    string public constant UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54'; // 'Claimable rights over underlying not zero (aToken supply or accruedToTreasury)'
                    string public constant VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56'; // 'Variable debt supply is not zero'
                    string public constant LTV_VALIDATION_FAILED = '57'; // 'Ltv validation failed'
                    string public constant INCONSISTENT_EMODE_CATEGORY = '58'; // 'Inconsistent eMode category'
                    string public constant PRICE_ORACLE_SENTINEL_CHECK_FAILED = '59'; // 'Price oracle sentinel validation failed'
                    string public constant ASSET_NOT_BORROWABLE_IN_ISOLATION = '60'; // 'Asset is not borrowable in isolation mode'
                    string public constant RESERVE_ALREADY_INITIALIZED = '61'; // 'Reserve has already been initialized'
                    string public constant USER_IN_ISOLATION_MODE_OR_LTV_ZERO = '62'; // 'User is in isolation mode or ltv is zero'
                    string public constant INVALID_LTV = '63'; // 'Invalid ltv parameter for the reserve'
                    string public constant INVALID_LIQ_THRESHOLD = '64'; // 'Invalid liquidity threshold parameter for the reserve'
                    string public constant INVALID_LIQ_BONUS = '65'; // 'Invalid liquidity bonus parameter for the reserve'
                    string public constant INVALID_DECIMALS = '66'; // 'Invalid decimals parameter of the underlying asset of the reserve'
                    string public constant INVALID_RESERVE_FACTOR = '67'; // 'Invalid reserve factor parameter for the reserve'
                    string public constant INVALID_BORROW_CAP = '68'; // 'Invalid borrow cap for the reserve'
                    string public constant INVALID_SUPPLY_CAP = '69'; // 'Invalid supply cap for the reserve'
                    string public constant INVALID_LIQUIDATION_PROTOCOL_FEE = '70'; // 'Invalid liquidation protocol fee for the reserve'
                    string public constant INVALID_EMODE_CATEGORY = '71'; // 'Invalid eMode category for the reserve'
                    string public constant INVALID_UNBACKED_MINT_CAP = '72'; // 'Invalid unbacked mint cap for the reserve'
                    string public constant INVALID_DEBT_CEILING = '73'; // 'Invalid debt ceiling for the reserve
                    string public constant INVALID_RESERVE_INDEX = '74'; // 'Invalid reserve index'
                    string public constant ACL_ADMIN_CANNOT_BE_ZERO = '75'; // 'ACL admin cannot be set to the zero address'
                    string public constant INCONSISTENT_PARAMS_LENGTH = '76'; // 'Array parameters that should be equal length are not'
                    string public constant ZERO_ADDRESS_NOT_VALID = '77'; // 'Zero address not valid'
                    string public constant INVALID_EXPIRATION = '78'; // 'Invalid expiration'
                    string public constant INVALID_SIGNATURE = '79'; // 'Invalid signature'
                    string public constant OPERATION_NOT_SUPPORTED = '80'; // 'Operation not supported'
                    string public constant DEBT_CEILING_NOT_ZERO = '81'; // 'Debt ceiling is not zero'
                    string public constant ASSET_NOT_LISTED = '82'; // 'Asset is not listed'
                    string public constant INVALID_OPTIMAL_USAGE_RATIO = '83'; // 'Invalid optimal usage ratio'
                    string public constant UNDERLYING_CANNOT_BE_RESCUED = '85'; // 'The underlying asset cannot be rescued'
                    string public constant ADDRESSES_PROVIDER_ALREADY_ADDED = '86'; // 'Reserve has already been added to reserve list'
                    string public constant POOL_ADDRESSES_DO_NOT_MATCH = '87'; // 'The token implementation pool address and the pool address provided by the initializing pool do not match'
                    string public constant SILOED_BORROWING_VIOLATION = '89'; // 'User is trying to borrow multiple assets including a siloed one'
                    string public constant RESERVE_DEBT_NOT_ZERO = '90'; // the total debt of the reserve needs to be 0
                    string public constant FLASHLOAN_DISABLED = '91'; // FlashLoaning for this asset is disabled
                    string public constant INVALID_MAX_RATE = '92'; // The expect maximum borrow rate is invalid
                    string public constant WITHDRAW_TO_ATOKEN = '93'; // Withdrawing to the aToken is not allowed
                    string public constant SUPPLY_TO_ATOKEN = '94'; // Supplying to the aToken is not allowed
                    string public constant SLOPE_2_MUST_BE_GTE_SLOPE_1 = '95'; // Variable interest rate slope 2 can not be lower than slope 1
                    string public constant CALLER_NOT_RISK_OR_POOL_OR_EMERGENCY_ADMIN = '96'; // 'The caller of the function is not a risk, pool or emergency admin'
                    string public constant LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED = '97'; // 'Liquidation grace sentinel validation failed'
                    string public constant INVALID_GRACE_PERIOD = '98'; // Grace period above a valid range
                    string public constant INVALID_FREEZE_STATE = '99'; // Reserve is already in the passed freeze state
                    string public constant NOT_BORROWABLE_IN_EMODE = '100'; // Asset not borrowable in eMode
                    string public constant CALLER_NOT_UMBRELLA = '101'; // The caller of the function is not the umbrella contract
                    string public constant RESERVE_NOT_IN_DEFICIT = '102'; // The reserve is not in deficit
                    string public constant MUST_NOT_LEAVE_DUST = '103'; // Below a certain threshold liquidators need to take the full position
                    string public constant USER_CANNOT_HAVE_DEBT = '104'; // Thrown when a user tries to interact with a method that requires a position without debt
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.10;
                  /**
                   * @title VersionedInitializable
                   * @author Aave, inspired by the OpenZeppelin Initializable contract
                   * @notice Helper contract to implement initializer functions. To use it, replace
                   * the constructor with a function that has the `initializer` modifier.
                   * @dev WARNING: Unlike constructors, initializer functions must be manually
                   * invoked. This applies both to deploying an Initializable contract, as well
                   * as extending an Initializable contract via inheritance.
                   * WARNING: When used with inheritance, manual care must be taken to not invoke
                   * a parent initializer twice, or ensure that all initializers are idempotent,
                   * because this is not dealt with automatically as with constructors.
                   */
                  abstract contract VersionedInitializable {
                    /**
                     * @dev Indicates that the contract has been initialized.
                     */
                    uint256 private lastInitializedRevision = 0;
                    /**
                     * @dev Indicates that the contract is in the process of being initialized.
                     */
                    bool private initializing;
                    /**
                     * @dev Modifier to use in the initializer function of a contract.
                     */
                    modifier initializer() {
                      uint256 revision = getRevision();
                      require(
                        initializing || isConstructor() || revision > lastInitializedRevision,
                        'Contract instance has already been initialized'
                      );
                      bool isTopLevelCall = !initializing;
                      if (isTopLevelCall) {
                        initializing = true;
                        lastInitializedRevision = revision;
                      }
                      _;
                      if (isTopLevelCall) {
                        initializing = false;
                      }
                    }
                    /**
                     * @notice Returns the revision number of the contract
                     * @dev Needs to be defined in the inherited class as a constant.
                     * @return The revision number
                     */
                    function getRevision() internal pure virtual returns (uint256);
                    /**
                     * @notice Returns true if and only if the function is running in the constructor
                     * @return True if the function is running in the constructor
                     */
                    function isConstructor() private view returns (bool) {
                      // extcodesize checks the size of the code stored in an address, and
                      // address returns the current address. Since the code is still not
                      // deployed when running a constructor, any checks on its code size will
                      // yield zero, making it an effective way to detect if a contract is
                      // under construction or not.
                      uint256 cs;
                      //solium-disable-next-line
                      assembly {
                        cs := extcodesize(address())
                      }
                      return cs == 0;
                    }
                    // Reserved storage space to allow for layout changes in the future.
                    uint256[50] private ______gap;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {Errors} from '../helpers/Errors.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  /**
                   * @title ReserveConfiguration library
                   * @author Aave
                   * @notice Implements the bitmap logic to handle the reserve configuration
                   */
                  library ReserveConfiguration {
                    uint256 internal constant LTV_MASK =                       0x000000000000000000000000000000000000000000000000000000000000FFFF; // prettier-ignore
                    uint256 internal constant LIQUIDATION_THRESHOLD_MASK =     0x00000000000000000000000000000000000000000000000000000000FFFF0000; // prettier-ignore
                    uint256 internal constant LIQUIDATION_BONUS_MASK =         0x0000000000000000000000000000000000000000000000000000FFFF00000000; // prettier-ignore
                    uint256 internal constant DECIMALS_MASK =                  0x00000000000000000000000000000000000000000000000000FF000000000000; // prettier-ignore
                    uint256 internal constant ACTIVE_MASK =                    0x0000000000000000000000000000000000000000000000000100000000000000; // prettier-ignore
                    uint256 internal constant FROZEN_MASK =                    0x0000000000000000000000000000000000000000000000000200000000000000; // prettier-ignore
                    uint256 internal constant BORROWING_MASK =                 0x0000000000000000000000000000000000000000000000000400000000000000; // prettier-ignore
                    // @notice there is an unoccupied hole of 1 bit at position 59 from pre 3.2 stableBorrowRateEnabled
                    uint256 internal constant PAUSED_MASK =                    0x0000000000000000000000000000000000000000000000001000000000000000; // prettier-ignore
                    uint256 internal constant BORROWABLE_IN_ISOLATION_MASK =   0x0000000000000000000000000000000000000000000000002000000000000000; // prettier-ignore
                    uint256 internal constant SILOED_BORROWING_MASK =          0x0000000000000000000000000000000000000000000000004000000000000000; // prettier-ignore
                    uint256 internal constant FLASHLOAN_ENABLED_MASK =         0x0000000000000000000000000000000000000000000000008000000000000000; // prettier-ignore
                    uint256 internal constant RESERVE_FACTOR_MASK =            0x00000000000000000000000000000000000000000000FFFF0000000000000000; // prettier-ignore
                    uint256 internal constant BORROW_CAP_MASK =                0x00000000000000000000000000000000000FFFFFFFFF00000000000000000000; // prettier-ignore
                    uint256 internal constant SUPPLY_CAP_MASK =                0x00000000000000000000000000FFFFFFFFF00000000000000000000000000000; // prettier-ignore
                    uint256 internal constant LIQUIDATION_PROTOCOL_FEE_MASK =  0x0000000000000000000000FFFF00000000000000000000000000000000000000; // prettier-ignore
                    //@notice there is an unoccupied hole of 8 bits from 168 to 176 left from pre 3.2 eModeCategory
                    uint256 internal constant UNBACKED_MINT_CAP_MASK =         0x00000000000FFFFFFFFF00000000000000000000000000000000000000000000; // prettier-ignore
                    uint256 internal constant DEBT_CEILING_MASK =              0x0FFFFFFFFFF00000000000000000000000000000000000000000000000000000; // prettier-ignore
                    uint256 internal constant VIRTUAL_ACC_ACTIVE_MASK =        0x1000000000000000000000000000000000000000000000000000000000000000; // prettier-ignore
                    /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed
                    uint256 internal constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16;
                    uint256 internal constant LIQUIDATION_BONUS_START_BIT_POSITION = 32;
                    uint256 internal constant RESERVE_DECIMALS_START_BIT_POSITION = 48;
                    uint256 internal constant IS_ACTIVE_START_BIT_POSITION = 56;
                    uint256 internal constant IS_FROZEN_START_BIT_POSITION = 57;
                    uint256 internal constant BORROWING_ENABLED_START_BIT_POSITION = 58;
                    uint256 internal constant IS_PAUSED_START_BIT_POSITION = 60;
                    uint256 internal constant BORROWABLE_IN_ISOLATION_START_BIT_POSITION = 61;
                    uint256 internal constant SILOED_BORROWING_START_BIT_POSITION = 62;
                    uint256 internal constant FLASHLOAN_ENABLED_START_BIT_POSITION = 63;
                    uint256 internal constant RESERVE_FACTOR_START_BIT_POSITION = 64;
                    uint256 internal constant BORROW_CAP_START_BIT_POSITION = 80;
                    uint256 internal constant SUPPLY_CAP_START_BIT_POSITION = 116;
                    uint256 internal constant LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION = 152;
                    //@notice there is an unoccupied hole of 8 bits from 168 to 176 left from pre 3.2 eModeCategory
                    uint256 internal constant UNBACKED_MINT_CAP_START_BIT_POSITION = 176;
                    uint256 internal constant DEBT_CEILING_START_BIT_POSITION = 212;
                    uint256 internal constant VIRTUAL_ACC_START_BIT_POSITION = 252;
                    uint256 internal constant MAX_VALID_LTV = 65535;
                    uint256 internal constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535;
                    uint256 internal constant MAX_VALID_LIQUIDATION_BONUS = 65535;
                    uint256 internal constant MAX_VALID_DECIMALS = 255;
                    uint256 internal constant MAX_VALID_RESERVE_FACTOR = 65535;
                    uint256 internal constant MAX_VALID_BORROW_CAP = 68719476735;
                    uint256 internal constant MAX_VALID_SUPPLY_CAP = 68719476735;
                    uint256 internal constant MAX_VALID_LIQUIDATION_PROTOCOL_FEE = 65535;
                    uint256 internal constant MAX_VALID_UNBACKED_MINT_CAP = 68719476735;
                    uint256 internal constant MAX_VALID_DEBT_CEILING = 1099511627775;
                    uint256 public constant DEBT_CEILING_DECIMALS = 2;
                    uint16 public constant MAX_RESERVES_COUNT = 128;
                    /**
                     * @notice Sets the Loan to Value of the reserve
                     * @param self The reserve configuration
                     * @param ltv The new ltv
                     */
                    function setLtv(DataTypes.ReserveConfigurationMap memory self, uint256 ltv) internal pure {
                      require(ltv <= MAX_VALID_LTV, Errors.INVALID_LTV);
                      self.data = (self.data & ~LTV_MASK) | ltv;
                    }
                    /**
                     * @notice Gets the Loan to Value of the reserve
                     * @param self The reserve configuration
                     * @return The loan to value
                     */
                    function getLtv(DataTypes.ReserveConfigurationMap memory self) internal pure returns (uint256) {
                      return self.data & LTV_MASK;
                    }
                    /**
                     * @notice Sets the liquidation threshold of the reserve
                     * @param self The reserve configuration
                     * @param threshold The new liquidation threshold
                     */
                    function setLiquidationThreshold(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 threshold
                    ) internal pure {
                      require(threshold <= MAX_VALID_LIQUIDATION_THRESHOLD, Errors.INVALID_LIQ_THRESHOLD);
                      self.data =
                        (self.data & ~LIQUIDATION_THRESHOLD_MASK) |
                        (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the liquidation threshold of the reserve
                     * @param self The reserve configuration
                     * @return The liquidation threshold
                     */
                    function getLiquidationThreshold(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the liquidation bonus of the reserve
                     * @param self The reserve configuration
                     * @param bonus The new liquidation bonus
                     */
                    function setLiquidationBonus(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 bonus
                    ) internal pure {
                      require(bonus <= MAX_VALID_LIQUIDATION_BONUS, Errors.INVALID_LIQ_BONUS);
                      self.data =
                        (self.data & ~LIQUIDATION_BONUS_MASK) |
                        (bonus << LIQUIDATION_BONUS_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the liquidation bonus of the reserve
                     * @param self The reserve configuration
                     * @return The liquidation bonus
                     */
                    function getLiquidationBonus(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the decimals of the underlying asset of the reserve
                     * @param self The reserve configuration
                     * @param decimals The decimals
                     */
                    function setDecimals(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 decimals
                    ) internal pure {
                      require(decimals <= MAX_VALID_DECIMALS, Errors.INVALID_DECIMALS);
                      self.data = (self.data & ~DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the decimals of the underlying asset of the reserve
                     * @param self The reserve configuration
                     * @return The decimals of the asset
                     */
                    function getDecimals(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the active state of the reserve
                     * @param self The reserve configuration
                     * @param active The active state
                     */
                    function setActive(DataTypes.ReserveConfigurationMap memory self, bool active) internal pure {
                      self.data =
                        (self.data & ~ACTIVE_MASK) |
                        (uint256(active ? 1 : 0) << IS_ACTIVE_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the active state of the reserve
                     * @param self The reserve configuration
                     * @return The active state
                     */
                    function getActive(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                      return (self.data & ACTIVE_MASK) != 0;
                    }
                    /**
                     * @notice Sets the frozen state of the reserve
                     * @param self The reserve configuration
                     * @param frozen The frozen state
                     */
                    function setFrozen(DataTypes.ReserveConfigurationMap memory self, bool frozen) internal pure {
                      self.data =
                        (self.data & ~FROZEN_MASK) |
                        (uint256(frozen ? 1 : 0) << IS_FROZEN_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the frozen state of the reserve
                     * @param self The reserve configuration
                     * @return The frozen state
                     */
                    function getFrozen(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                      return (self.data & FROZEN_MASK) != 0;
                    }
                    /**
                     * @notice Sets the paused state of the reserve
                     * @param self The reserve configuration
                     * @param paused The paused state
                     */
                    function setPaused(DataTypes.ReserveConfigurationMap memory self, bool paused) internal pure {
                      self.data =
                        (self.data & ~PAUSED_MASK) |
                        (uint256(paused ? 1 : 0) << IS_PAUSED_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the paused state of the reserve
                     * @param self The reserve configuration
                     * @return The paused state
                     */
                    function getPaused(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                      return (self.data & PAUSED_MASK) != 0;
                    }
                    /**
                     * @notice Sets the borrowable in isolation flag for the reserve.
                     * @dev When this flag is set to true, the asset will be borrowable against isolated collaterals and the borrowed
                     * amount will be accumulated in the isolated collateral's total debt exposure.
                     * @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep
                     * consistency in the debt ceiling calculations.
                     * @param self The reserve configuration
                     * @param borrowable True if the asset is borrowable
                     */
                    function setBorrowableInIsolation(
                      DataTypes.ReserveConfigurationMap memory self,
                      bool borrowable
                    ) internal pure {
                      self.data =
                        (self.data & ~BORROWABLE_IN_ISOLATION_MASK) |
                        (uint256(borrowable ? 1 : 0) << BORROWABLE_IN_ISOLATION_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the borrowable in isolation flag for the reserve.
                     * @dev If the returned flag is true, the asset is borrowable against isolated collateral. Assets borrowed with
                     * isolated collateral is accounted for in the isolated collateral's total debt exposure.
                     * @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep
                     * consistency in the debt ceiling calculations.
                     * @param self The reserve configuration
                     * @return The borrowable in isolation flag
                     */
                    function getBorrowableInIsolation(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return (self.data & BORROWABLE_IN_ISOLATION_MASK) != 0;
                    }
                    /**
                     * @notice Sets the siloed borrowing flag for the reserve.
                     * @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset.
                     * @param self The reserve configuration
                     * @param siloed True if the asset is siloed
                     */
                    function setSiloedBorrowing(
                      DataTypes.ReserveConfigurationMap memory self,
                      bool siloed
                    ) internal pure {
                      self.data =
                        (self.data & ~SILOED_BORROWING_MASK) |
                        (uint256(siloed ? 1 : 0) << SILOED_BORROWING_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the siloed borrowing flag for the reserve.
                     * @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset.
                     * @param self The reserve configuration
                     * @return The siloed borrowing flag
                     */
                    function getSiloedBorrowing(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return (self.data & SILOED_BORROWING_MASK) != 0;
                    }
                    /**
                     * @notice Enables or disables borrowing on the reserve
                     * @param self The reserve configuration
                     * @param enabled True if the borrowing needs to be enabled, false otherwise
                     */
                    function setBorrowingEnabled(
                      DataTypes.ReserveConfigurationMap memory self,
                      bool enabled
                    ) internal pure {
                      self.data =
                        (self.data & ~BORROWING_MASK) |
                        (uint256(enabled ? 1 : 0) << BORROWING_ENABLED_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the borrowing state of the reserve
                     * @param self The reserve configuration
                     * @return The borrowing state
                     */
                    function getBorrowingEnabled(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return (self.data & BORROWING_MASK) != 0;
                    }
                    /**
                     * @notice Sets the reserve factor of the reserve
                     * @param self The reserve configuration
                     * @param reserveFactor The reserve factor
                     */
                    function setReserveFactor(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 reserveFactor
                    ) internal pure {
                      require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.INVALID_RESERVE_FACTOR);
                      self.data =
                        (self.data & ~RESERVE_FACTOR_MASK) |
                        (reserveFactor << RESERVE_FACTOR_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the reserve factor of the reserve
                     * @param self The reserve configuration
                     * @return The reserve factor
                     */
                    function getReserveFactor(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the borrow cap of the reserve
                     * @param self The reserve configuration
                     * @param borrowCap The borrow cap
                     */
                    function setBorrowCap(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 borrowCap
                    ) internal pure {
                      require(borrowCap <= MAX_VALID_BORROW_CAP, Errors.INVALID_BORROW_CAP);
                      self.data = (self.data & ~BORROW_CAP_MASK) | (borrowCap << BORROW_CAP_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the borrow cap of the reserve
                     * @param self The reserve configuration
                     * @return The borrow cap
                     */
                    function getBorrowCap(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the supply cap of the reserve
                     * @param self The reserve configuration
                     * @param supplyCap The supply cap
                     */
                    function setSupplyCap(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 supplyCap
                    ) internal pure {
                      require(supplyCap <= MAX_VALID_SUPPLY_CAP, Errors.INVALID_SUPPLY_CAP);
                      self.data = (self.data & ~SUPPLY_CAP_MASK) | (supplyCap << SUPPLY_CAP_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the supply cap of the reserve
                     * @param self The reserve configuration
                     * @return The supply cap
                     */
                    function getSupplyCap(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the debt ceiling in isolation mode for the asset
                     * @param self The reserve configuration
                     * @param ceiling The maximum debt ceiling for the asset
                     */
                    function setDebtCeiling(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 ceiling
                    ) internal pure {
                      require(ceiling <= MAX_VALID_DEBT_CEILING, Errors.INVALID_DEBT_CEILING);
                      self.data = (self.data & ~DEBT_CEILING_MASK) | (ceiling << DEBT_CEILING_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the debt ceiling for the asset if the asset is in isolation mode
                     * @param self The reserve configuration
                     * @return The debt ceiling (0 = isolation mode disabled)
                     */
                    function getDebtCeiling(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & DEBT_CEILING_MASK) >> DEBT_CEILING_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the liquidation protocol fee of the reserve
                     * @param self The reserve configuration
                     * @param liquidationProtocolFee The liquidation protocol fee
                     */
                    function setLiquidationProtocolFee(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 liquidationProtocolFee
                    ) internal pure {
                      require(
                        liquidationProtocolFee <= MAX_VALID_LIQUIDATION_PROTOCOL_FEE,
                        Errors.INVALID_LIQUIDATION_PROTOCOL_FEE
                      );
                      self.data =
                        (self.data & ~LIQUIDATION_PROTOCOL_FEE_MASK) |
                        (liquidationProtocolFee << LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION);
                    }
                    /**
                     * @dev Gets the liquidation protocol fee
                     * @param self The reserve configuration
                     * @return The liquidation protocol fee
                     */
                    function getLiquidationProtocolFee(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return
                        (self.data & LIQUIDATION_PROTOCOL_FEE_MASK) >> LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the unbacked mint cap of the reserve
                     * @param self The reserve configuration
                     * @param unbackedMintCap The unbacked mint cap
                     */
                    function setUnbackedMintCap(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 unbackedMintCap
                    ) internal pure {
                      require(unbackedMintCap <= MAX_VALID_UNBACKED_MINT_CAP, Errors.INVALID_UNBACKED_MINT_CAP);
                      self.data =
                        (self.data & ~UNBACKED_MINT_CAP_MASK) |
                        (unbackedMintCap << UNBACKED_MINT_CAP_START_BIT_POSITION);
                    }
                    /**
                     * @dev Gets the unbacked mint cap of the reserve
                     * @param self The reserve configuration
                     * @return The unbacked mint cap
                     */
                    function getUnbackedMintCap(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & UNBACKED_MINT_CAP_MASK) >> UNBACKED_MINT_CAP_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the flashloanable flag for the reserve
                     * @param self The reserve configuration
                     * @param flashLoanEnabled True if the asset is flashloanable, false otherwise
                     */
                    function setFlashLoanEnabled(
                      DataTypes.ReserveConfigurationMap memory self,
                      bool flashLoanEnabled
                    ) internal pure {
                      self.data =
                        (self.data & ~FLASHLOAN_ENABLED_MASK) |
                        (uint256(flashLoanEnabled ? 1 : 0) << FLASHLOAN_ENABLED_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the flashloanable flag for the reserve
                     * @param self The reserve configuration
                     * @return The flashloanable flag
                     */
                    function getFlashLoanEnabled(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return (self.data & FLASHLOAN_ENABLED_MASK) != 0;
                    }
                    /**
                     * @notice Sets the virtual account active/not state of the reserve
                     * @param self The reserve configuration
                     * @param active The active state
                     */
                    function setVirtualAccActive(
                      DataTypes.ReserveConfigurationMap memory self,
                      bool active
                    ) internal pure {
                      self.data =
                        (self.data & ~VIRTUAL_ACC_ACTIVE_MASK) |
                        (uint256(active ? 1 : 0) << VIRTUAL_ACC_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the virtual account active/not state of the reserve
                     * @dev The state should be true for all normal assets and should be false
                     * Virtual accounting being disabled means that the asset:
                     * - is GHO
                     * - can never be supplied
                     * - the interest rate strategy is not influenced by the virtual balance
                     * @param self The reserve configuration
                     * @return The active state
                     */
                    function getIsVirtualAccActive(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return (self.data & VIRTUAL_ACC_ACTIVE_MASK) != 0;
                    }
                    /**
                     * @notice Gets the configuration flags of the reserve
                     * @param self The reserve configuration
                     * @return The state flag representing active
                     * @return The state flag representing frozen
                     * @return The state flag representing borrowing enabled
                     * @return The state flag representing paused
                     */
                    function getFlags(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool, bool, bool, bool) {
                      uint256 dataLocal = self.data;
                      return (
                        (dataLocal & ACTIVE_MASK) != 0,
                        (dataLocal & FROZEN_MASK) != 0,
                        (dataLocal & BORROWING_MASK) != 0,
                        (dataLocal & PAUSED_MASK) != 0
                      );
                    }
                    /**
                     * @notice Gets the configuration parameters of the reserve from storage
                     * @param self The reserve configuration
                     * @return The state param representing ltv
                     * @return The state param representing liquidation threshold
                     * @return The state param representing liquidation bonus
                     * @return The state param representing reserve decimals
                     * @return The state param representing reserve factor
                     */
                    function getParams(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256, uint256, uint256, uint256, uint256) {
                      uint256 dataLocal = self.data;
                      return (
                        dataLocal & LTV_MASK,
                        (dataLocal & LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION,
                        (dataLocal & LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION,
                        (dataLocal & DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION,
                        (dataLocal & RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION
                      );
                    }
                    /**
                     * @notice Gets the caps parameters of the reserve from storage
                     * @param self The reserve configuration
                     * @return The state param representing borrow cap
                     * @return The state param representing supply cap.
                     */
                    function getCaps(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256, uint256) {
                      uint256 dataLocal = self.data;
                      return (
                        (dataLocal & BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION,
                        (dataLocal & SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION
                      );
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {Address} from '../../../dependencies/openzeppelin/contracts/Address.sol';
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {IAToken} from '../../../interfaces/IAToken.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  import {Errors} from '../helpers/Errors.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  import {ValidationLogic} from './ValidationLogic.sol';
                  import {GenericLogic} from './GenericLogic.sol';
                  /**
                   * @title PoolLogic library
                   * @author Aave
                   * @notice Implements the logic for Pool specific functions
                   */
                  library PoolLogic {
                    using GPv2SafeERC20 for IERC20;
                    using WadRayMath for uint256;
                    using ReserveLogic for DataTypes.ReserveData;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    // See `IPool` for descriptions
                    event MintedToTreasury(address indexed reserve, uint256 amountMinted);
                    event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                    /**
                     * @notice Initialize an asset reserve and add the reserve to the list of reserves
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param params Additional parameters needed for initiation
                     * @return true if appended, false if inserted at existing empty spot
                     */
                    function executeInitReserve(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.InitReserveParams memory params
                    ) external returns (bool) {
                      require(Address.isContract(params.asset), Errors.NOT_CONTRACT);
                      reservesData[params.asset].init(
                        params.aTokenAddress,
                        params.variableDebtAddress,
                        params.interestRateStrategyAddress
                      );
                      bool reserveAlreadyAdded = reservesData[params.asset].id != 0 ||
                        reservesList[0] == params.asset;
                      require(!reserveAlreadyAdded, Errors.RESERVE_ALREADY_ADDED);
                      for (uint16 i = 0; i < params.reservesCount; i++) {
                        if (reservesList[i] == address(0)) {
                          reservesData[params.asset].id = i;
                          reservesList[i] = params.asset;
                          return false;
                        }
                      }
                      require(params.reservesCount < params.maxNumberReserves, Errors.NO_MORE_RESERVES_ALLOWED);
                      reservesData[params.asset].id = params.reservesCount;
                      reservesList[params.reservesCount] = params.asset;
                      return true;
                    }
                    /**
                     * @notice Rescue and transfer tokens locked in this contract
                     * @param token The address of the token
                     * @param to The address of the recipient
                     * @param amount The amount of token to transfer
                     */
                    function executeRescueTokens(address token, address to, uint256 amount) external {
                      IERC20(token).safeTransfer(to, amount);
                    }
                    /**
                     * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens
                     * @param reservesData The state of all the reserves
                     * @param assets The list of reserves for which the minting needs to be executed
                     */
                    function executeMintToTreasury(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      address[] calldata assets
                    ) external {
                      for (uint256 i = 0; i < assets.length; i++) {
                        address assetAddress = assets[i];
                        DataTypes.ReserveData storage reserve = reservesData[assetAddress];
                        // this cover both inactive reserves and invalid reserves since the flag will be 0 for both
                        if (!reserve.configuration.getActive()) {
                          continue;
                        }
                        uint256 accruedToTreasury = reserve.accruedToTreasury;
                        if (accruedToTreasury != 0) {
                          reserve.accruedToTreasury = 0;
                          uint256 normalizedIncome = reserve.getNormalizedIncome();
                          uint256 amountToMint = accruedToTreasury.rayMul(normalizedIncome);
                          IAToken(reserve.aTokenAddress).mintToTreasury(amountToMint, normalizedIncome);
                          emit MintedToTreasury(assetAddress, amountToMint);
                        }
                      }
                    }
                    /**
                     * @notice Resets the isolation mode total debt of the given asset to zero
                     * @dev It requires the given asset has zero debt ceiling
                     * @param reservesData The state of all the reserves
                     * @param asset The address of the underlying asset to reset the isolationModeTotalDebt
                     */
                    function executeResetIsolationModeTotalDebt(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      address asset
                    ) external {
                      require(reservesData[asset].configuration.getDebtCeiling() == 0, Errors.DEBT_CEILING_NOT_ZERO);
                      reservesData[asset].isolationModeTotalDebt = 0;
                      emit IsolationModeTotalDebtUpdated(asset, 0);
                    }
                    /**
                     * @notice Sets the liquidation grace period of the asset
                     * @param reservesData The state of all the reserves
                     * @param asset The address of the underlying asset to set the liquidationGracePeriod
                     * @param until Timestamp when the liquidation grace period will end
                     */
                    function executeSetLiquidationGracePeriod(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      address asset,
                      uint40 until
                    ) external {
                      reservesData[asset].liquidationGracePeriodUntil = until;
                    }
                    /**
                     * @notice Drop a reserve
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param asset The address of the underlying asset of the reserve
                     */
                    function executeDropReserve(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      address asset
                    ) external {
                      DataTypes.ReserveData storage reserve = reservesData[asset];
                      ValidationLogic.validateDropReserve(reservesList, reserve, asset);
                      reservesList[reservesData[asset].id] = address(0);
                      delete reservesData[asset];
                    }
                    /**
                     * @notice Returns the user account data across all the reserves
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param params Additional params needed for the calculation
                     * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
                     * @return totalDebtBase The total debt of the user in the base currency used by the price feed
                     * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
                     * @return currentLiquidationThreshold The liquidation threshold of the user
                     * @return ltv The loan to value of The user
                     * @return healthFactor The current health factor of the user
                     */
                    function executeGetUserAccountData(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.CalculateUserAccountDataParams memory params
                    )
                      external
                      view
                      returns (
                        uint256 totalCollateralBase,
                        uint256 totalDebtBase,
                        uint256 availableBorrowsBase,
                        uint256 currentLiquidationThreshold,
                        uint256 ltv,
                        uint256 healthFactor
                      )
                    {
                      (
                        totalCollateralBase,
                        totalDebtBase,
                        ltv,
                        currentLiquidationThreshold,
                        healthFactor,
                      ) = GenericLogic.calculateUserAccountData(reservesData, reservesList, eModeCategories, params);
                      availableBorrowsBase = GenericLogic.calculateAvailableBorrows(
                        totalCollateralBase,
                        totalDebtBase,
                        ltv
                      );
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
                  import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  import {MathUtils} from '../math/MathUtils.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {Errors} from '../helpers/Errors.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  /**
                   * @title ReserveLogic library
                   * @author Aave
                   * @notice Implements the logic to update the reserves state
                   */
                  library ReserveLogic {
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    using SafeCast for uint256;
                    using GPv2SafeERC20 for IERC20;
                    using ReserveLogic for DataTypes.ReserveData;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    // See `IPool` for descriptions
                    event ReserveDataUpdated(
                      address indexed reserve,
                      uint256 liquidityRate,
                      uint256 stableBorrowRate,
                      uint256 variableBorrowRate,
                      uint256 liquidityIndex,
                      uint256 variableBorrowIndex
                    );
                    /**
                     * @notice Returns the ongoing normalized income for the reserve.
                     * @dev A value of 1e27 means there is no income. As time passes, the income is accrued
                     * @dev A value of 2*1e27 means for each unit of asset one unit of income has been accrued
                     * @param reserve The reserve object
                     * @return The normalized income, expressed in ray
                     */
                    function getNormalizedIncome(
                      DataTypes.ReserveData storage reserve
                    ) internal view returns (uint256) {
                      uint40 timestamp = reserve.lastUpdateTimestamp;
                      //solium-disable-next-line
                      if (timestamp == block.timestamp) {
                        //if the index was updated in the same block, no need to perform any calculation
                        return reserve.liquidityIndex;
                      } else {
                        return
                          MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul(
                            reserve.liquidityIndex
                          );
                      }
                    }
                    /**
                     * @notice Returns the ongoing normalized variable debt for the reserve.
                     * @dev A value of 1e27 means there is no debt. As time passes, the debt is accrued
                     * @dev A value of 2*1e27 means that for each unit of debt, one unit worth of interest has been accumulated
                     * @param reserve The reserve object
                     * @return The normalized variable debt, expressed in ray
                     */
                    function getNormalizedDebt(
                      DataTypes.ReserveData storage reserve
                    ) internal view returns (uint256) {
                      uint40 timestamp = reserve.lastUpdateTimestamp;
                      //solium-disable-next-line
                      if (timestamp == block.timestamp) {
                        //if the index was updated in the same block, no need to perform any calculation
                        return reserve.variableBorrowIndex;
                      } else {
                        return
                          MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp).rayMul(
                            reserve.variableBorrowIndex
                          );
                      }
                    }
                    /**
                     * @notice Updates the liquidity cumulative index and the variable borrow index.
                     * @param reserve The reserve object
                     * @param reserveCache The caching layer for the reserve data
                     */
                    function updateState(
                      DataTypes.ReserveData storage reserve,
                      DataTypes.ReserveCache memory reserveCache
                    ) internal {
                      // If time didn't pass since last stored timestamp, skip state update
                      //solium-disable-next-line
                      if (reserveCache.reserveLastUpdateTimestamp == uint40(block.timestamp)) {
                        return;
                      }
                      _updateIndexes(reserve, reserveCache);
                      _accrueToTreasury(reserve, reserveCache);
                      //solium-disable-next-line
                      reserve.lastUpdateTimestamp = uint40(block.timestamp);
                      reserveCache.reserveLastUpdateTimestamp = uint40(block.timestamp);
                    }
                    /**
                     * @notice Accumulates a predefined amount of asset to the reserve as a fixed, instantaneous income. Used for example
                     * to accumulate the flashloan fee to the reserve, and spread it between all the suppliers.
                     * @param reserve The reserve object
                     * @param totalLiquidity The total liquidity available in the reserve
                     * @param amount The amount to accumulate
                     * @return The next liquidity index of the reserve
                     */
                    function cumulateToLiquidityIndex(
                      DataTypes.ReserveData storage reserve,
                      uint256 totalLiquidity,
                      uint256 amount
                    ) internal returns (uint256) {
                      //next liquidity index is calculated this way: `((amount / totalLiquidity) + 1) * liquidityIndex`
                      //division `amount / totalLiquidity` done in ray for precision
                      uint256 result = (amount.wadToRay().rayDiv(totalLiquidity.wadToRay()) + WadRayMath.RAY).rayMul(
                        reserve.liquidityIndex
                      );
                      reserve.liquidityIndex = result.toUint128();
                      return result;
                    }
                    /**
                     * @notice Initializes a reserve.
                     * @param reserve The reserve object
                     * @param aTokenAddress The address of the overlying atoken contract
                     * @param variableDebtTokenAddress The address of the overlying variable debt token contract
                     * @param interestRateStrategyAddress The address of the interest rate strategy contract
                     */
                    function init(
                      DataTypes.ReserveData storage reserve,
                      address aTokenAddress,
                      address variableDebtTokenAddress,
                      address interestRateStrategyAddress
                    ) internal {
                      require(reserve.aTokenAddress == address(0), Errors.RESERVE_ALREADY_INITIALIZED);
                      reserve.liquidityIndex = uint128(WadRayMath.RAY);
                      reserve.variableBorrowIndex = uint128(WadRayMath.RAY);
                      reserve.aTokenAddress = aTokenAddress;
                      reserve.variableDebtTokenAddress = variableDebtTokenAddress;
                      reserve.interestRateStrategyAddress = interestRateStrategyAddress;
                    }
                    /**
                     * @notice Updates the reserve current variable borrow rate and the current liquidity rate.
                     * @param reserve The reserve reserve to be updated
                     * @param reserveCache The caching layer for the reserve data
                     * @param reserveAddress The address of the reserve to be updated
                     * @param liquidityAdded The amount of liquidity added to the protocol (supply or repay) in the previous action
                     * @param liquidityTaken The amount of liquidity taken from the protocol (redeem or borrow)
                     */
                    function updateInterestRatesAndVirtualBalance(
                      DataTypes.ReserveData storage reserve,
                      DataTypes.ReserveCache memory reserveCache,
                      address reserveAddress,
                      uint256 liquidityAdded,
                      uint256 liquidityTaken
                    ) internal {
                      uint256 totalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul(
                        reserveCache.nextVariableBorrowIndex
                      );
                      (uint256 nextLiquidityRate, uint256 nextVariableRate) = IReserveInterestRateStrategy(
                        reserve.interestRateStrategyAddress
                      ).calculateInterestRates(
                          DataTypes.CalculateInterestRatesParams({
                            unbacked: reserve.unbacked + reserve.deficit,
                            liquidityAdded: liquidityAdded,
                            liquidityTaken: liquidityTaken,
                            totalDebt: totalVariableDebt,
                            reserveFactor: reserveCache.reserveFactor,
                            reserve: reserveAddress,
                            usingVirtualBalance: reserveCache.reserveConfiguration.getIsVirtualAccActive(),
                            virtualUnderlyingBalance: reserve.virtualUnderlyingBalance
                          })
                        );
                      reserve.currentLiquidityRate = nextLiquidityRate.toUint128();
                      reserve.currentVariableBorrowRate = nextVariableRate.toUint128();
                      // Only affect virtual balance if the reserve uses it
                      if (reserveCache.reserveConfiguration.getIsVirtualAccActive()) {
                        if (liquidityAdded > 0) {
                          reserve.virtualUnderlyingBalance += liquidityAdded.toUint128();
                        }
                        if (liquidityTaken > 0) {
                          reserve.virtualUnderlyingBalance -= liquidityTaken.toUint128();
                        }
                      }
                      emit ReserveDataUpdated(
                        reserveAddress,
                        nextLiquidityRate,
                        0,
                        nextVariableRate,
                        reserveCache.nextLiquidityIndex,
                        reserveCache.nextVariableBorrowIndex
                      );
                    }
                    /**
                     * @notice Mints part of the repaid interest to the reserve treasury as a function of the reserve factor for the
                     * specific asset.
                     * @param reserve The reserve to be updated
                     * @param reserveCache The caching layer for the reserve data
                     */
                    function _accrueToTreasury(
                      DataTypes.ReserveData storage reserve,
                      DataTypes.ReserveCache memory reserveCache
                    ) internal {
                      if (reserveCache.reserveFactor == 0) {
                        return;
                      }
                      //calculate the total variable debt at moment of the last interaction
                      uint256 prevTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                        reserveCache.currVariableBorrowIndex
                      );
                      //calculate the new total variable debt after accumulation of the interest on the index
                      uint256 currTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                        reserveCache.nextVariableBorrowIndex
                      );
                      //debt accrued is the sum of the current debt minus the sum of the debt at the last update
                      uint256 totalDebtAccrued = currTotalVariableDebt - prevTotalVariableDebt;
                      uint256 amountToMint = totalDebtAccrued.percentMul(reserveCache.reserveFactor);
                      if (amountToMint != 0) {
                        reserve.accruedToTreasury += amountToMint.rayDiv(reserveCache.nextLiquidityIndex).toUint128();
                      }
                    }
                    /**
                     * @notice Updates the reserve indexes and the timestamp of the update.
                     * @param reserve The reserve reserve to be updated
                     * @param reserveCache The cache layer holding the cached protocol data
                     */
                    function _updateIndexes(
                      DataTypes.ReserveData storage reserve,
                      DataTypes.ReserveCache memory reserveCache
                    ) internal {
                      // Only cumulating on the supply side if there is any income being produced
                      // The case of Reserve Factor 100% is not a problem (currentLiquidityRate == 0),
                      // as liquidity index should not be updated
                      if (reserveCache.currLiquidityRate != 0) {
                        uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(
                          reserveCache.currLiquidityRate,
                          reserveCache.reserveLastUpdateTimestamp
                        );
                        reserveCache.nextLiquidityIndex = cumulatedLiquidityInterest.rayMul(
                          reserveCache.currLiquidityIndex
                        );
                        reserve.liquidityIndex = reserveCache.nextLiquidityIndex.toUint128();
                      }
                      // Variable borrow index only gets updated if there is any variable debt.
                      // reserveCache.currVariableBorrowRate != 0 is not a correct validation,
                      // because a positive base variable rate can be stored on
                      // reserveCache.currVariableBorrowRate, but the index should not increase
                      if (reserveCache.currScaledVariableDebt != 0) {
                        uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest(
                          reserveCache.currVariableBorrowRate,
                          reserveCache.reserveLastUpdateTimestamp
                        );
                        reserveCache.nextVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(
                          reserveCache.currVariableBorrowIndex
                        );
                        reserve.variableBorrowIndex = reserveCache.nextVariableBorrowIndex.toUint128();
                      }
                    }
                    /**
                     * @notice Creates a cache object to avoid repeated storage reads and external contract calls when updating state and
                     * interest rates.
                     * @param reserve The reserve object for which the cache will be filled
                     * @return The cache object
                     */
                    function cache(
                      DataTypes.ReserveData storage reserve
                    ) internal view returns (DataTypes.ReserveCache memory) {
                      DataTypes.ReserveCache memory reserveCache;
                      reserveCache.reserveConfiguration = reserve.configuration;
                      reserveCache.reserveFactor = reserveCache.reserveConfiguration.getReserveFactor();
                      reserveCache.currLiquidityIndex = reserveCache.nextLiquidityIndex = reserve.liquidityIndex;
                      reserveCache.currVariableBorrowIndex = reserveCache.nextVariableBorrowIndex = reserve
                        .variableBorrowIndex;
                      reserveCache.currLiquidityRate = reserve.currentLiquidityRate;
                      reserveCache.currVariableBorrowRate = reserve.currentVariableBorrowRate;
                      reserveCache.aTokenAddress = reserve.aTokenAddress;
                      reserveCache.variableDebtTokenAddress = reserve.variableDebtTokenAddress;
                      reserveCache.reserveLastUpdateTimestamp = reserve.lastUpdateTimestamp;
                      reserveCache.currScaledVariableDebt = reserveCache.nextScaledVariableDebt = IVariableDebtToken(
                        reserveCache.variableDebtTokenAddress
                      ).scaledTotalSupply();
                      return reserveCache;
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ValidationLogic} from './ValidationLogic.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  /**
                   * @title EModeLogic library
                   * @author Aave
                   * @notice Implements the base logic for all the actions related to the eMode
                   */
                  library EModeLogic {
                    using ReserveLogic for DataTypes.ReserveCache;
                    using ReserveLogic for DataTypes.ReserveData;
                    using GPv2SafeERC20 for IERC20;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    // See `IPool` for descriptions
                    event UserEModeSet(address indexed user, uint8 categoryId);
                    /**
                     * @notice Updates the user efficiency mode category
                     * @dev Will revert if user is borrowing non-compatible asset or change will drop HF < HEALTH_FACTOR_LIQUIDATION_THRESHOLD
                     * @dev Emits the `UserEModeSet` event
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param usersEModeCategory The state of all users efficiency mode category
                     * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the setUserEMode function
                     */
                    function executeSetUserEMode(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      mapping(address => uint8) storage usersEModeCategory,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ExecuteSetUserEModeParams memory params
                    ) external {
                      if (usersEModeCategory[msg.sender] == params.categoryId) return;
                      ValidationLogic.validateSetUserEMode(
                        eModeCategories,
                        userConfig,
                        params.reservesCount,
                        params.categoryId
                      );
                      usersEModeCategory[msg.sender] = params.categoryId;
                      ValidationLogic.validateHealthFactor(
                        reservesData,
                        reservesList,
                        eModeCategories,
                        userConfig,
                        msg.sender,
                        params.categoryId,
                        params.reservesCount,
                        params.oracle
                      );
                      emit UserEModeSet(msg.sender, params.categoryId);
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {IAToken} from '../../../interfaces/IAToken.sol';
                  import {Errors} from '../helpers/Errors.sol';
                  import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {ValidationLogic} from './ValidationLogic.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  /**
                   * @title SupplyLogic library
                   * @author Aave
                   * @notice Implements the base logic for supply/withdraw
                   */
                  library SupplyLogic {
                    using ReserveLogic for DataTypes.ReserveCache;
                    using ReserveLogic for DataTypes.ReserveData;
                    using GPv2SafeERC20 for IERC20;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    // See `IPool` for descriptions
                    event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                    event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                    event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
                    event Supply(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      uint16 indexed referralCode
                    );
                    /**
                     * @notice Implements the supply feature. Through `supply()`, users supply assets to the Aave protocol.
                     * @dev Emits the `Supply()` event.
                     * @dev In the first supply action, `ReserveUsedAsCollateralEnabled()` is emitted, if the asset can be enabled as
                     * collateral.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the supply function
                     */
                    function executeSupply(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ExecuteSupplyParams memory params
                    ) external {
                      DataTypes.ReserveData storage reserve = reservesData[params.asset];
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      reserve.updateState(reserveCache);
                      ValidationLogic.validateSupply(reserveCache, reserve, params.amount, params.onBehalfOf);
                      reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, params.amount, 0);
                      IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, params.amount);
                      bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
                        msg.sender,
                        params.onBehalfOf,
                        params.amount,
                        reserveCache.nextLiquidityIndex
                      );
                      if (isFirstSupply) {
                        if (
                          ValidationLogic.validateAutomaticUseAsCollateral(
                            reservesData,
                            reservesList,
                            userConfig,
                            reserveCache.reserveConfiguration,
                            reserveCache.aTokenAddress
                          )
                        ) {
                          userConfig.setUsingAsCollateral(reserve.id, true);
                          emit ReserveUsedAsCollateralEnabled(params.asset, params.onBehalfOf);
                        }
                      }
                      emit Supply(params.asset, msg.sender, params.onBehalfOf, params.amount, params.referralCode);
                    }
                    /**
                     * @notice Implements the withdraw feature. Through `withdraw()`, users redeem their aTokens for the underlying asset
                     * previously supplied in the Aave protocol.
                     * @dev Emits the `Withdraw()` event.
                     * @dev If the user withdraws everything, `ReserveUsedAsCollateralDisabled()` is emitted.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the withdraw function
                     * @return The actual amount withdrawn
                     */
                    function executeWithdraw(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ExecuteWithdrawParams memory params
                    ) external returns (uint256) {
                      DataTypes.ReserveData storage reserve = reservesData[params.asset];
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      require(params.to != reserveCache.aTokenAddress, Errors.WITHDRAW_TO_ATOKEN);
                      reserve.updateState(reserveCache);
                      uint256 userBalance = IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul(
                        reserveCache.nextLiquidityIndex
                      );
                      uint256 amountToWithdraw = params.amount;
                      if (params.amount == type(uint256).max) {
                        amountToWithdraw = userBalance;
                      }
                      ValidationLogic.validateWithdraw(reserveCache, amountToWithdraw, userBalance);
                      reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, 0, amountToWithdraw);
                      bool isCollateral = userConfig.isUsingAsCollateral(reserve.id);
                      if (isCollateral && amountToWithdraw == userBalance) {
                        userConfig.setUsingAsCollateral(reserve.id, false);
                        emit ReserveUsedAsCollateralDisabled(params.asset, msg.sender);
                      }
                      IAToken(reserveCache.aTokenAddress).burn(
                        msg.sender,
                        params.to,
                        amountToWithdraw,
                        reserveCache.nextLiquidityIndex
                      );
                      if (isCollateral && userConfig.isBorrowingAny()) {
                        ValidationLogic.validateHFAndLtv(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          userConfig,
                          params.asset,
                          msg.sender,
                          params.reservesCount,
                          params.oracle,
                          params.userEModeCategory
                        );
                      }
                      emit Withdraw(params.asset, msg.sender, params.to, amountToWithdraw);
                      return amountToWithdraw;
                    }
                    /**
                     * @notice Validates a transfer of aTokens. The sender is subjected to health factor validation to avoid
                     * collateralization constraints violation.
                     * @dev Emits the `ReserveUsedAsCollateralEnabled()` event for the `to` account, if the asset is being activated as
                     * collateral.
                     * @dev In case the `from` user transfers everything, `ReserveUsedAsCollateralDisabled()` is emitted for `from`.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param usersConfig The users configuration mapping that track the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the finalizeTransfer function
                     */
                    function executeFinalizeTransfer(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      mapping(address => DataTypes.UserConfigurationMap) storage usersConfig,
                      DataTypes.FinalizeTransferParams memory params
                    ) external {
                      DataTypes.ReserveData storage reserve = reservesData[params.asset];
                      ValidationLogic.validateTransfer(reserve);
                      uint256 reserveId = reserve.id;
                      uint256 scaledAmount = params.amount.rayDiv(reserve.getNormalizedIncome());
                      if (params.from != params.to && scaledAmount != 0) {
                        DataTypes.UserConfigurationMap storage fromConfig = usersConfig[params.from];
                        if (fromConfig.isUsingAsCollateral(reserveId)) {
                          if (fromConfig.isBorrowingAny()) {
                            ValidationLogic.validateHFAndLtv(
                              reservesData,
                              reservesList,
                              eModeCategories,
                              usersConfig[params.from],
                              params.asset,
                              params.from,
                              params.reservesCount,
                              params.oracle,
                              params.fromEModeCategory
                            );
                          }
                          if (params.balanceFromBefore == params.amount) {
                            fromConfig.setUsingAsCollateral(reserveId, false);
                            emit ReserveUsedAsCollateralDisabled(params.asset, params.from);
                          }
                        }
                        if (params.balanceToBefore == 0) {
                          DataTypes.UserConfigurationMap storage toConfig = usersConfig[params.to];
                          if (
                            ValidationLogic.validateAutomaticUseAsCollateral(
                              reservesData,
                              reservesList,
                              toConfig,
                              reserve.configuration,
                              reserve.aTokenAddress
                            )
                          ) {
                            toConfig.setUsingAsCollateral(reserveId, true);
                            emit ReserveUsedAsCollateralEnabled(params.asset, params.to);
                          }
                        }
                      }
                    }
                    /**
                     * @notice Executes the 'set as collateral' feature. A user can choose to activate or deactivate an asset as
                     * collateral at any point in time. Deactivating an asset as collateral is subjected to the usual health factor
                     * checks to ensure collateralization.
                     * @dev Emits the `ReserveUsedAsCollateralEnabled()` event if the asset can be activated as collateral.
                     * @dev In case the asset is being deactivated as collateral, `ReserveUsedAsCollateralDisabled()` is emitted.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param userConfig The users configuration mapping that track the supplied/borrowed assets
                     * @param asset The address of the asset being configured as collateral
                     * @param useAsCollateral True if the user wants to set the asset as collateral, false otherwise
                     * @param reservesCount The number of initialized reserves
                     * @param priceOracle The address of the price oracle
                     * @param userEModeCategory The eMode category chosen by the user
                     */
                    function executeUseReserveAsCollateral(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap storage userConfig,
                      address asset,
                      bool useAsCollateral,
                      uint256 reservesCount,
                      address priceOracle,
                      uint8 userEModeCategory
                    ) external {
                      DataTypes.ReserveData storage reserve = reservesData[asset];
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      uint256 userBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender);
                      ValidationLogic.validateSetUseReserveAsCollateral(reserveCache, userBalance);
                      if (useAsCollateral == userConfig.isUsingAsCollateral(reserve.id)) return;
                      if (useAsCollateral) {
                        require(
                          ValidationLogic.validateUseAsCollateral(
                            reservesData,
                            reservesList,
                            userConfig,
                            reserveCache.reserveConfiguration
                          ),
                          Errors.USER_IN_ISOLATION_MODE_OR_LTV_ZERO
                        );
                        userConfig.setUsingAsCollateral(reserve.id, true);
                        emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
                      } else {
                        userConfig.setUsingAsCollateral(reserve.id, false);
                        ValidationLogic.validateHFAndLtv(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          userConfig,
                          asset,
                          msg.sender,
                          reservesCount,
                          priceOracle,
                          userEModeCategory
                        );
                        emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
                      }
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {IAToken} from '../../../interfaces/IAToken.sol';
                  import {IPool} from '../../../interfaces/IPool.sol';
                  import {IFlashLoanReceiver} from '../../../misc/flashloan/interfaces/IFlashLoanReceiver.sol';
                  import {IFlashLoanSimpleReceiver} from '../../../misc/flashloan/interfaces/IFlashLoanSimpleReceiver.sol';
                  import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  import {Errors} from '../helpers/Errors.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ValidationLogic} from './ValidationLogic.sol';
                  import {BorrowLogic} from './BorrowLogic.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  /**
                   * @title FlashLoanLogic library
                   * @author Aave
                   * @notice Implements the logic for the flash loans
                   */
                  library FlashLoanLogic {
                    using ReserveLogic for DataTypes.ReserveCache;
                    using ReserveLogic for DataTypes.ReserveData;
                    using GPv2SafeERC20 for IERC20;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    using SafeCast for uint256;
                    // See `IPool` for descriptions
                    event FlashLoan(
                      address indexed target,
                      address initiator,
                      address indexed asset,
                      uint256 amount,
                      DataTypes.InterestRateMode interestRateMode,
                      uint256 premium,
                      uint16 indexed referralCode
                    );
                    // Helper struct for internal variables used in the `executeFlashLoan` function
                    struct FlashLoanLocalVars {
                      IFlashLoanReceiver receiver;
                      address currentAsset;
                      uint256 currentAmount;
                      uint256[] totalPremiums;
                      uint256 flashloanPremiumTotal;
                      uint256 flashloanPremiumToProtocol;
                    }
                    /**
                     * @notice Implements the flashloan feature that allow users to access liquidity of the pool for one transaction
                     * as long as the amount taken plus fee is returned or debt is opened.
                     * @dev For authorized flashborrowers the fee is waived
                     * @dev At the end of the transaction the pool will pull amount borrowed + fee from the receiver,
                     * if the receiver have not approved the pool the transaction will revert.
                     * @dev Emits the `FlashLoan()` event
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the flashloan function
                     */
                    function executeFlashLoan(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.FlashloanParams memory params
                    ) external {
                      // The usual action flow (cache -> updateState -> validation -> changeState -> updateRates)
                      // is altered to (validation -> user payload -> cache -> updateState -> changeState -> updateRates) for flashloans.
                      // This is done to protect against reentrance and rate manipulation within the user specified payload.
                      ValidationLogic.validateFlashloan(reservesData, params.assets, params.amounts);
                      FlashLoanLocalVars memory vars;
                      vars.totalPremiums = new uint256[](params.assets.length);
                      vars.receiver = IFlashLoanReceiver(params.receiverAddress);
                      (vars.flashloanPremiumTotal, vars.flashloanPremiumToProtocol) = params.isAuthorizedFlashBorrower
                        ? (0, 0)
                        : (params.flashLoanPremiumTotal, params.flashLoanPremiumToProtocol);
                      for (uint256 i = 0; i < params.assets.length; i++) {
                        vars.currentAmount = params.amounts[i];
                        vars.totalPremiums[i] = DataTypes.InterestRateMode(params.interestRateModes[i]) ==
                          DataTypes.InterestRateMode.NONE
                          ? vars.currentAmount.percentMul(vars.flashloanPremiumTotal)
                          : 0;
                        if (reservesData[params.assets[i]].configuration.getIsVirtualAccActive()) {
                          reservesData[params.assets[i]].virtualUnderlyingBalance -= vars.currentAmount.toUint128();
                        }
                        IAToken(reservesData[params.assets[i]].aTokenAddress).transferUnderlyingTo(
                          params.receiverAddress,
                          vars.currentAmount
                        );
                      }
                      require(
                        vars.receiver.executeOperation(
                          params.assets,
                          params.amounts,
                          vars.totalPremiums,
                          msg.sender,
                          params.params
                        ),
                        Errors.INVALID_FLASHLOAN_EXECUTOR_RETURN
                      );
                      for (uint256 i = 0; i < params.assets.length; i++) {
                        vars.currentAsset = params.assets[i];
                        vars.currentAmount = params.amounts[i];
                        if (
                          DataTypes.InterestRateMode(params.interestRateModes[i]) == DataTypes.InterestRateMode.NONE
                        ) {
                          _handleFlashLoanRepayment(
                            reservesData[vars.currentAsset],
                            DataTypes.FlashLoanRepaymentParams({
                              asset: vars.currentAsset,
                              receiverAddress: params.receiverAddress,
                              amount: vars.currentAmount,
                              totalPremium: vars.totalPremiums[i],
                              flashLoanPremiumToProtocol: vars.flashloanPremiumToProtocol,
                              referralCode: params.referralCode
                            })
                          );
                        } else {
                          // If the user chose to not return the funds, the system checks if there is enough collateral and
                          // eventually opens a debt position
                          BorrowLogic.executeBorrow(
                            reservesData,
                            reservesList,
                            eModeCategories,
                            userConfig,
                            DataTypes.ExecuteBorrowParams({
                              asset: vars.currentAsset,
                              user: msg.sender,
                              onBehalfOf: params.onBehalfOf,
                              amount: vars.currentAmount,
                              interestRateMode: DataTypes.InterestRateMode(params.interestRateModes[i]),
                              referralCode: params.referralCode,
                              releaseUnderlying: false,
                              reservesCount: IPool(params.pool).getReservesCount(),
                              oracle: IPoolAddressesProvider(params.addressesProvider).getPriceOracle(),
                              userEModeCategory: IPool(params.pool).getUserEMode(params.onBehalfOf).toUint8(),
                              priceOracleSentinel: IPoolAddressesProvider(params.addressesProvider)
                                .getPriceOracleSentinel()
                            })
                          );
                          // no premium is paid when taking on the flashloan as debt
                          emit FlashLoan(
                            params.receiverAddress,
                            msg.sender,
                            vars.currentAsset,
                            vars.currentAmount,
                            DataTypes.InterestRateMode(params.interestRateModes[i]),
                            0,
                            params.referralCode
                          );
                        }
                      }
                    }
                    /**
                     * @notice Implements the simple flashloan feature that allow users to access liquidity of ONE reserve for one
                     * transaction as long as the amount taken plus fee is returned.
                     * @dev Does not waive fee for approved flashborrowers nor allow taking on debt instead of repaying to save gas
                     * @dev At the end of the transaction the pool will pull amount borrowed + fee from the receiver,
                     * if the receiver have not approved the pool the transaction will revert.
                     * @dev Emits the `FlashLoan()` event
                     * @param reserve The state of the flashloaned reserve
                     * @param params The additional parameters needed to execute the simple flashloan function
                     */
                    function executeFlashLoanSimple(
                      DataTypes.ReserveData storage reserve,
                      DataTypes.FlashloanSimpleParams memory params
                    ) external {
                      // The usual action flow (cache -> updateState -> validation -> changeState -> updateRates)
                      // is altered to (validation -> user payload -> cache -> updateState -> changeState -> updateRates) for flashloans.
                      // This is done to protect against reentrance and rate manipulation within the user specified payload.
                      ValidationLogic.validateFlashloanSimple(reserve, params.amount);
                      IFlashLoanSimpleReceiver receiver = IFlashLoanSimpleReceiver(params.receiverAddress);
                      uint256 totalPremium = params.amount.percentMul(params.flashLoanPremiumTotal);
                      if (reserve.configuration.getIsVirtualAccActive()) {
                        reserve.virtualUnderlyingBalance -= params.amount.toUint128();
                      }
                      IAToken(reserve.aTokenAddress).transferUnderlyingTo(params.receiverAddress, params.amount);
                      require(
                        receiver.executeOperation(
                          params.asset,
                          params.amount,
                          totalPremium,
                          msg.sender,
                          params.params
                        ),
                        Errors.INVALID_FLASHLOAN_EXECUTOR_RETURN
                      );
                      _handleFlashLoanRepayment(
                        reserve,
                        DataTypes.FlashLoanRepaymentParams({
                          asset: params.asset,
                          receiverAddress: params.receiverAddress,
                          amount: params.amount,
                          totalPremium: totalPremium,
                          flashLoanPremiumToProtocol: params.flashLoanPremiumToProtocol,
                          referralCode: params.referralCode
                        })
                      );
                    }
                    /**
                     * @notice Handles repayment of flashloaned assets + premium
                     * @dev Will pull the amount + premium from the receiver, so must have approved pool
                     * @param reserve The state of the flashloaned reserve
                     * @param params The additional parameters needed to execute the repayment function
                     */
                    function _handleFlashLoanRepayment(
                      DataTypes.ReserveData storage reserve,
                      DataTypes.FlashLoanRepaymentParams memory params
                    ) internal {
                      uint256 premiumToProtocol = params.totalPremium.percentMul(params.flashLoanPremiumToProtocol);
                      uint256 premiumToLP = params.totalPremium - premiumToProtocol;
                      uint256 amountPlusPremium = params.amount + params.totalPremium;
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      reserve.updateState(reserveCache);
                      reserveCache.nextLiquidityIndex = reserve.cumulateToLiquidityIndex(
                        IERC20(reserveCache.aTokenAddress).totalSupply() +
                          uint256(reserve.accruedToTreasury).rayMul(reserveCache.nextLiquidityIndex),
                        premiumToLP
                      );
                      reserve.accruedToTreasury += premiumToProtocol
                        .rayDiv(reserveCache.nextLiquidityIndex)
                        .toUint128();
                      reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, amountPlusPremium, 0);
                      IERC20(params.asset).safeTransferFrom(
                        params.receiverAddress,
                        reserveCache.aTokenAddress,
                        amountPlusPremium
                      );
                      IAToken(reserveCache.aTokenAddress).handleRepayment(
                        params.receiverAddress,
                        params.receiverAddress,
                        amountPlusPremium
                      );
                      emit FlashLoan(
                        params.receiverAddress,
                        msg.sender,
                        params.asset,
                        params.amount,
                        DataTypes.InterestRateMode.NONE,
                        params.totalPremium,
                        params.referralCode
                      );
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
                  import {IAToken} from '../../../interfaces/IAToken.sol';
                  import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ValidationLogic} from './ValidationLogic.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  import {IsolationModeLogic} from './IsolationModeLogic.sol';
                  /**
                   * @title BorrowLogic library
                   * @author Aave
                   * @notice Implements the base logic for all the actions related to borrowing
                   */
                  library BorrowLogic {
                    using ReserveLogic for DataTypes.ReserveCache;
                    using ReserveLogic for DataTypes.ReserveData;
                    using GPv2SafeERC20 for IERC20;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using SafeCast for uint256;
                    // See `IPool` for descriptions
                    event Borrow(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      DataTypes.InterestRateMode interestRateMode,
                      uint256 borrowRate,
                      uint16 indexed referralCode
                    );
                    event Repay(
                      address indexed reserve,
                      address indexed user,
                      address indexed repayer,
                      uint256 amount,
                      bool useATokens
                    );
                    event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                    event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                    /**
                     * @notice Implements the borrow feature. Borrowing allows users that provided collateral to draw liquidity from the
                     * Aave protocol proportionally to their collateralization power. For isolated positions, it also increases the
                     * isolated debt.
                     * @dev  Emits the `Borrow()` event
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the borrow function
                     */
                    function executeBorrow(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ExecuteBorrowParams memory params
                    ) external {
                      DataTypes.ReserveData storage reserve = reservesData[params.asset];
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      reserve.updateState(reserveCache);
                      (
                        bool isolationModeActive,
                        address isolationModeCollateralAddress,
                        uint256 isolationModeDebtCeiling
                      ) = userConfig.getIsolationModeState(reservesData, reservesList);
                      ValidationLogic.validateBorrow(
                        reservesData,
                        reservesList,
                        eModeCategories,
                        DataTypes.ValidateBorrowParams({
                          reserveCache: reserveCache,
                          userConfig: userConfig,
                          asset: params.asset,
                          userAddress: params.onBehalfOf,
                          amount: params.amount,
                          interestRateMode: params.interestRateMode,
                          reservesCount: params.reservesCount,
                          oracle: params.oracle,
                          userEModeCategory: params.userEModeCategory,
                          priceOracleSentinel: params.priceOracleSentinel,
                          isolationModeActive: isolationModeActive,
                          isolationModeCollateralAddress: isolationModeCollateralAddress,
                          isolationModeDebtCeiling: isolationModeDebtCeiling
                        })
                      );
                      bool isFirstBorrowing = false;
                      (isFirstBorrowing, reserveCache.nextScaledVariableDebt) = IVariableDebtToken(
                        reserveCache.variableDebtTokenAddress
                      ).mint(params.user, params.onBehalfOf, params.amount, reserveCache.nextVariableBorrowIndex);
                      if (isFirstBorrowing) {
                        userConfig.setBorrowing(reserve.id, true);
                      }
                      if (isolationModeActive) {
                        uint256 nextIsolationModeTotalDebt = reservesData[isolationModeCollateralAddress]
                          .isolationModeTotalDebt += (params.amount /
                          10 **
                            (reserveCache.reserveConfiguration.getDecimals() -
                              ReserveConfiguration.DEBT_CEILING_DECIMALS)).toUint128();
                        emit IsolationModeTotalDebtUpdated(
                          isolationModeCollateralAddress,
                          nextIsolationModeTotalDebt
                        );
                      }
                      reserve.updateInterestRatesAndVirtualBalance(
                        reserveCache,
                        params.asset,
                        0,
                        params.releaseUnderlying ? params.amount : 0
                      );
                      if (params.releaseUnderlying) {
                        IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(params.user, params.amount);
                      }
                      emit Borrow(
                        params.asset,
                        params.user,
                        params.onBehalfOf,
                        params.amount,
                        DataTypes.InterestRateMode.VARIABLE,
                        reserve.currentVariableBorrowRate,
                        params.referralCode
                      );
                    }
                    /**
                     * @notice Implements the repay feature. Repaying transfers the underlying back to the aToken and clears the
                     * equivalent amount of debt for the user by burning the corresponding debt token. For isolated positions, it also
                     * reduces the isolated debt.
                     * @dev  Emits the `Repay()` event
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the repay function
                     * @return The actual amount being repaid
                     */
                    function executeRepay(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ExecuteRepayParams memory params
                    ) external returns (uint256) {
                      DataTypes.ReserveData storage reserve = reservesData[params.asset];
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      reserve.updateState(reserveCache);
                      uint256 variableDebt = IERC20(reserveCache.variableDebtTokenAddress).balanceOf(
                        params.onBehalfOf
                      );
                      ValidationLogic.validateRepay(
                        reserveCache,
                        params.amount,
                        params.interestRateMode,
                        params.onBehalfOf,
                        variableDebt
                      );
                      uint256 paybackAmount = variableDebt;
                      // Allows a user to repay with aTokens without leaving dust from interest.
                      if (params.useATokens && params.amount == type(uint256).max) {
                        params.amount = IAToken(reserveCache.aTokenAddress).balanceOf(msg.sender);
                      }
                      if (params.amount < paybackAmount) {
                        paybackAmount = params.amount;
                      }
                      reserveCache.nextScaledVariableDebt = IVariableDebtToken(reserveCache.variableDebtTokenAddress)
                        .burn(params.onBehalfOf, paybackAmount, reserveCache.nextVariableBorrowIndex);
                      reserve.updateInterestRatesAndVirtualBalance(
                        reserveCache,
                        params.asset,
                        params.useATokens ? 0 : paybackAmount,
                        0
                      );
                      if (variableDebt - paybackAmount == 0) {
                        userConfig.setBorrowing(reserve.id, false);
                      }
                      IsolationModeLogic.updateIsolatedDebtIfIsolated(
                        reservesData,
                        reservesList,
                        userConfig,
                        reserveCache,
                        paybackAmount
                      );
                      // in case of aToken repayment the msg.sender must always repay on behalf of itself
                      if (params.useATokens) {
                        IAToken(reserveCache.aTokenAddress).burn(
                          msg.sender,
                          reserveCache.aTokenAddress,
                          paybackAmount,
                          reserveCache.nextLiquidityIndex
                        );
                        bool isCollateral = userConfig.isUsingAsCollateral(reserve.id);
                        if (isCollateral && IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender) == 0) {
                          userConfig.setUsingAsCollateral(reserve.id, false);
                          emit ReserveUsedAsCollateralDisabled(params.asset, msg.sender);
                        }
                      } else {
                        IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, paybackAmount);
                        IAToken(reserveCache.aTokenAddress).handleRepayment(
                          msg.sender,
                          params.onBehalfOf,
                          paybackAmount
                        );
                      }
                      emit Repay(params.asset, params.onBehalfOf, msg.sender, paybackAmount, params.useATokens);
                      return paybackAmount;
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts//IERC20.sol';
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {PercentageMath} from '../../libraries/math/PercentageMath.sol';
                  import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                  import {DataTypes} from '../../libraries/types/DataTypes.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  import {ValidationLogic} from './ValidationLogic.sol';
                  import {GenericLogic} from './GenericLogic.sol';
                  import {IsolationModeLogic} from './IsolationModeLogic.sol';
                  import {UserConfiguration} from '../../libraries/configuration/UserConfiguration.sol';
                  import {ReserveConfiguration} from '../../libraries/configuration/ReserveConfiguration.sol';
                  import {EModeConfiguration} from '../../libraries/configuration/EModeConfiguration.sol';
                  import {IAToken} from '../../../interfaces/IAToken.sol';
                  import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
                  import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  import {Errors} from '../helpers/Errors.sol';
                  interface IGhoVariableDebtToken {
                    function getBalanceFromInterest(address user) external view returns (uint256);
                  }
                  /**
                   * @title LiquidationLogic library
                   * @author Aave
                   * @notice Implements actions involving management of collateral in the protocol, the main one being the liquidations
                   */
                  library LiquidationLogic {
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    using ReserveLogic for DataTypes.ReserveCache;
                    using ReserveLogic for DataTypes.ReserveData;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using GPv2SafeERC20 for IERC20;
                    using SafeCast for uint256;
                    // See `IPool` for descriptions
                    event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                    event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                    event DeficitCreated(address indexed user, address indexed debtAsset, uint256 amountCreated);
                    event DeficitCovered(address indexed reserve, address caller, uint256 amountCovered);
                    event LiquidationCall(
                      address indexed collateralAsset,
                      address indexed debtAsset,
                      address indexed user,
                      uint256 debtToCover,
                      uint256 liquidatedCollateralAmount,
                      address liquidator,
                      bool receiveAToken
                    );
                    /**
                     * @dev Default percentage of borrower's debt to be repaid in a liquidation.
                     * @dev Percentage applied when the users health factor is above `CLOSE_FACTOR_HF_THRESHOLD`
                     * Expressed in bps, a value of 0.5e4 results in 50.00%
                     */
                    uint256 internal constant DEFAULT_LIQUIDATION_CLOSE_FACTOR = 0.5e4;
                    /**
                     * @dev This constant represents the upper bound on the health factor, below(inclusive) which the full amount of debt becomes liquidatable.
                     * A value of 0.95e18 results in 0.95
                     */
                    uint256 public constant CLOSE_FACTOR_HF_THRESHOLD = 0.95e18;
                    /**
                     * @dev This constant represents a base value threshold.
                     * If the total collateral or debt on a position is below this threshold, the close factor is raised to 100%.
                     * @notice The default value assumes that the basePrice is usd denominated by 8 decimals and needs to be adjusted in a non USD-denominated pool.
                     */
                    uint256 public constant MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD = 2000e8;
                    /**
                     * @dev This constant represents the minimum amount of assets in base currency that need to be leftover after a liquidation, if not clearing a position completely.
                     * This parameter is inferred from MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD as the logic is dependent.
                     * Assuming a MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD of `n` a liquidation of `n+1` might result in `n/2` leftover which is assumed to be still economically liquidatable.
                     * This mechanic was introduced to ensure liquidators don't optimize gas by leaving some wei on the liquidation.
                     */
                    uint256 public constant MIN_LEFTOVER_BASE = MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD / 2;
                    /**
                     * @notice Reduces a portion or all of the deficit of a specified reserve by burning:
                     * - the equivalent aToken `amount` for assets with virtual accounting enabled
                     * - the equivalent `amount` of underlying for assets with virtual accounting disabled (e.g. GHO)
                     * The caller of this method MUST always be the Umbrella contract and the Umbrella contract is assumed to never have debt.
                     * @dev Emits the `DeficitCovered() event`.
                     * @dev If the coverage admin covers its entire balance, `ReserveUsedAsCollateralDisabled()` is emitted.
                     * @param reservesData The state of all the reserves
                     * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the eliminateDeficit function
                     */
                    function executeEliminateDeficit(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ExecuteEliminateDeficitParams memory params
                    ) external {
                      require(params.amount != 0, Errors.INVALID_AMOUNT);
                      DataTypes.ReserveData storage reserve = reservesData[params.asset];
                      uint256 currentDeficit = reserve.deficit;
                      require(currentDeficit != 0, Errors.RESERVE_NOT_IN_DEFICIT);
                      require(!userConfig.isBorrowingAny(), Errors.USER_CANNOT_HAVE_DEBT);
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      reserve.updateState(reserveCache);
                      bool isActive = reserveCache.reserveConfiguration.getActive();
                      require(isActive, Errors.RESERVE_INACTIVE);
                      uint256 balanceWriteOff = params.amount;
                      if (params.amount > currentDeficit) {
                        balanceWriteOff = currentDeficit;
                      }
                      uint256 userBalance = reserveCache.reserveConfiguration.getIsVirtualAccActive()
                        ? IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul(
                          reserveCache.nextLiquidityIndex
                        )
                        : IERC20(params.asset).balanceOf(msg.sender);
                      require(balanceWriteOff <= userBalance, Errors.NOT_ENOUGH_AVAILABLE_USER_BALANCE);
                      if (reserveCache.reserveConfiguration.getIsVirtualAccActive()) {
                        // assets without virtual accounting can never be a collateral
                        bool isCollateral = userConfig.isUsingAsCollateral(reserve.id);
                        if (isCollateral && balanceWriteOff == userBalance) {
                          userConfig.setUsingAsCollateral(reserve.id, false);
                          emit ReserveUsedAsCollateralDisabled(params.asset, msg.sender);
                        }
                        IAToken(reserveCache.aTokenAddress).burn(
                          msg.sender,
                          reserveCache.aTokenAddress,
                          balanceWriteOff,
                          reserveCache.nextLiquidityIndex
                        );
                      } else {
                        // This is a special case to allow mintable assets (ex. GHO), which by definition cannot be supplied
                        // and thus do not use virtual underlying balances.
                        // In that case, the procedure is 1) sending the underlying asset to the aToken and
                        // 2) trigger the handleRepayment() for the aToken to dispose of those assets
                        IERC20(params.asset).safeTransferFrom(
                          msg.sender,
                          reserveCache.aTokenAddress,
                          balanceWriteOff
                        );
                        // it is assumed that handleRepayment does not touch the variable debt balance
                        IAToken(reserveCache.aTokenAddress).handleRepayment(
                          msg.sender,
                          // In the context of GHO it's only relevant that the address has no debt.
                          // Passing the pool is fitting as it's handling the repayment on behalf of the protocol.
                          address(this),
                          balanceWriteOff
                        );
                      }
                      reserve.deficit -= balanceWriteOff.toUint128();
                      reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, 0, 0);
                      emit DeficitCovered(params.asset, msg.sender, balanceWriteOff);
                    }
                    struct LiquidationCallLocalVars {
                      uint256 userCollateralBalance;
                      uint256 userReserveDebt;
                      uint256 actualDebtToLiquidate;
                      uint256 actualCollateralToLiquidate;
                      uint256 liquidationBonus;
                      uint256 healthFactor;
                      uint256 liquidationProtocolFeeAmount;
                      uint256 totalCollateralInBaseCurrency;
                      uint256 totalDebtInBaseCurrency;
                      uint256 collateralToLiquidateInBaseCurrency;
                      uint256 userReserveDebtInBaseCurrency;
                      uint256 userReserveCollateralInBaseCurrency;
                      uint256 collateralAssetPrice;
                      uint256 debtAssetPrice;
                      uint256 collateralAssetUnit;
                      uint256 debtAssetUnit;
                      IAToken collateralAToken;
                      DataTypes.ReserveCache debtReserveCache;
                    }
                    /**
                     * @notice Function to liquidate a position if its Health Factor drops below 1. The caller (liquidator)
                     * covers `debtToCover` amount of debt of the user getting liquidated, and receives
                     * a proportional amount of the `collateralAsset` plus a bonus to cover market risk
                     * @dev Emits the `LiquidationCall()` event, and the `DeficitCreated()` event if the liquidation results in bad debt
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param usersConfig The users configuration mapping that track the supplied/borrowed assets
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param params The additional parameters needed to execute the liquidation function
                     */
                    function executeLiquidationCall(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(address => DataTypes.UserConfigurationMap) storage usersConfig,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.ExecuteLiquidationCallParams memory params
                    ) external {
                      LiquidationCallLocalVars memory vars;
                      DataTypes.ReserveData storage collateralReserve = reservesData[params.collateralAsset];
                      DataTypes.ReserveData storage debtReserve = reservesData[params.debtAsset];
                      DataTypes.UserConfigurationMap storage userConfig = usersConfig[params.user];
                      vars.debtReserveCache = debtReserve.cache();
                      debtReserve.updateState(vars.debtReserveCache);
                      (
                        vars.totalCollateralInBaseCurrency,
                        vars.totalDebtInBaseCurrency,
                        ,
                        ,
                        vars.healthFactor,
                      ) = GenericLogic.calculateUserAccountData(
                        reservesData,
                        reservesList,
                        eModeCategories,
                        DataTypes.CalculateUserAccountDataParams({
                          userConfig: userConfig,
                          reservesCount: params.reservesCount,
                          user: params.user,
                          oracle: params.priceOracle,
                          userEModeCategory: params.userEModeCategory
                        })
                      );
                      vars.collateralAToken = IAToken(collateralReserve.aTokenAddress);
                      vars.userCollateralBalance = vars.collateralAToken.balanceOf(params.user);
                      vars.userReserveDebt = IERC20(vars.debtReserveCache.variableDebtTokenAddress).balanceOf(
                        params.user
                      );
                      ValidationLogic.validateLiquidationCall(
                        userConfig,
                        collateralReserve,
                        debtReserve,
                        DataTypes.ValidateLiquidationCallParams({
                          debtReserveCache: vars.debtReserveCache,
                          totalDebt: vars.userReserveDebt,
                          healthFactor: vars.healthFactor,
                          priceOracleSentinel: params.priceOracleSentinel
                        })
                      );
                      if (
                        params.userEModeCategory != 0 &&
                        EModeConfiguration.isReserveEnabledOnBitmap(
                          eModeCategories[params.userEModeCategory].collateralBitmap,
                          collateralReserve.id
                        )
                      ) {
                        vars.liquidationBonus = eModeCategories[params.userEModeCategory].liquidationBonus;
                      } else {
                        vars.liquidationBonus = collateralReserve.configuration.getLiquidationBonus();
                      }
                      vars.collateralAssetPrice = IPriceOracleGetter(params.priceOracle).getAssetPrice(
                        params.collateralAsset
                      );
                      vars.debtAssetPrice = IPriceOracleGetter(params.priceOracle).getAssetPrice(params.debtAsset);
                      vars.collateralAssetUnit = 10 ** collateralReserve.configuration.getDecimals();
                      vars.debtAssetUnit = 10 ** vars.debtReserveCache.reserveConfiguration.getDecimals();
                      vars.userReserveDebtInBaseCurrency =
                        (vars.userReserveDebt * vars.debtAssetPrice) /
                        vars.debtAssetUnit;
                      vars.userReserveCollateralInBaseCurrency =
                        (vars.userCollateralBalance * vars.collateralAssetPrice) /
                        vars.collateralAssetUnit;
                      // by default whole debt in the reserve could be liquidated
                      uint256 maxLiquidatableDebt = vars.userReserveDebt;
                      // but if debt and collateral is above or equal MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD
                      // and health factor is above CLOSE_FACTOR_HF_THRESHOLD this amount may be adjusted
                      if (
                        vars.userReserveCollateralInBaseCurrency >= MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD &&
                        vars.userReserveDebtInBaseCurrency >= MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD &&
                        vars.healthFactor > CLOSE_FACTOR_HF_THRESHOLD
                      ) {
                        uint256 totalDefaultLiquidatableDebtInBaseCurrency = vars.totalDebtInBaseCurrency.percentMul(
                          DEFAULT_LIQUIDATION_CLOSE_FACTOR
                        );
                        // if the debt is more then DEFAULT_LIQUIDATION_CLOSE_FACTOR % of the whole,
                        // then we CAN liquidate only up to DEFAULT_LIQUIDATION_CLOSE_FACTOR %
                        if (vars.userReserveDebtInBaseCurrency > totalDefaultLiquidatableDebtInBaseCurrency) {
                          maxLiquidatableDebt =
                            (totalDefaultLiquidatableDebtInBaseCurrency * vars.debtAssetUnit) /
                            vars.debtAssetPrice;
                        }
                      }
                      vars.actualDebtToLiquidate = params.debtToCover > maxLiquidatableDebt
                        ? maxLiquidatableDebt
                        : params.debtToCover;
                      (
                        vars.actualCollateralToLiquidate,
                        vars.actualDebtToLiquidate,
                        vars.liquidationProtocolFeeAmount,
                        vars.collateralToLiquidateInBaseCurrency
                      ) = _calculateAvailableCollateralToLiquidate(
                        collateralReserve.configuration,
                        vars.collateralAssetPrice,
                        vars.collateralAssetUnit,
                        vars.debtAssetPrice,
                        vars.debtAssetUnit,
                        vars.actualDebtToLiquidate,
                        vars.userCollateralBalance,
                        vars.liquidationBonus
                      );
                      // to prevent accumulation of dust on the protocol, it is enforced that you either
                      // 1. liquidate all debt
                      // 2. liquidate all collateral
                      // 3. leave more than MIN_LEFTOVER_BASE of collateral & debt
                      if (
                        vars.actualDebtToLiquidate < vars.userReserveDebt &&
                        vars.actualCollateralToLiquidate + vars.liquidationProtocolFeeAmount <
                        vars.userCollateralBalance
                      ) {
                        bool isDebtMoreThanLeftoverThreshold = ((vars.userReserveDebt - vars.actualDebtToLiquidate) *
                          vars.debtAssetPrice) /
                          vars.debtAssetUnit >=
                          MIN_LEFTOVER_BASE;
                        bool isCollateralMoreThanLeftoverThreshold = ((vars.userCollateralBalance -
                          vars.actualCollateralToLiquidate -
                          vars.liquidationProtocolFeeAmount) * vars.collateralAssetPrice) /
                          vars.collateralAssetUnit >=
                          MIN_LEFTOVER_BASE;
                        require(
                          isDebtMoreThanLeftoverThreshold && isCollateralMoreThanLeftoverThreshold,
                          Errors.MUST_NOT_LEAVE_DUST
                        );
                      }
                      // If the collateral being liquidated is equal to the user balance,
                      // we set the currency as not being used as collateral anymore
                      if (
                        vars.actualCollateralToLiquidate + vars.liquidationProtocolFeeAmount ==
                        vars.userCollateralBalance
                      ) {
                        userConfig.setUsingAsCollateral(collateralReserve.id, false);
                        emit ReserveUsedAsCollateralDisabled(params.collateralAsset, params.user);
                      }
                      bool hasNoCollateralLeft = vars.totalCollateralInBaseCurrency ==
                        vars.collateralToLiquidateInBaseCurrency;
                      _burnDebtTokens(
                        vars.debtReserveCache,
                        debtReserve,
                        userConfig,
                        params.user,
                        params.debtAsset,
                        vars.userReserveDebt,
                        vars.actualDebtToLiquidate,
                        hasNoCollateralLeft
                      );
                      // An asset can only be ceiled if it has no supply or if it was not a collateral previously.
                      // Therefore we can be sure that no inconsistent state can be reached in which a user has multiple collaterals, with one being ceiled.
                      // This allows for the implicit assumption that: if the asset was a collateral & the asset was ceiled, the user must have been in isolation.
                      if (collateralReserve.configuration.getDebtCeiling() != 0) {
                        // IsolationModeTotalDebt only discounts `actualDebtToLiquidate`, not the fully burned amount in case of deficit creation.
                        // This is by design as otherwise the debt ceiling would render ineffective if a collateral asset faces bad debt events.
                        // The governance can decide the raise the ceiling to discount manifested deficit.
                        IsolationModeLogic.updateIsolatedDebt(
                          reservesData,
                          vars.debtReserveCache,
                          vars.actualDebtToLiquidate,
                          params.collateralAsset
                        );
                      }
                      if (params.receiveAToken) {
                        _liquidateATokens(reservesData, reservesList, usersConfig, collateralReserve, params, vars);
                      } else {
                        _burnCollateralATokens(collateralReserve, params, vars);
                      }
                      // Transfer fee to treasury if it is non-zero
                      if (vars.liquidationProtocolFeeAmount != 0) {
                        uint256 liquidityIndex = collateralReserve.getNormalizedIncome();
                        uint256 scaledDownLiquidationProtocolFee = vars.liquidationProtocolFeeAmount.rayDiv(
                          liquidityIndex
                        );
                        uint256 scaledDownUserBalance = vars.collateralAToken.scaledBalanceOf(params.user);
                        // To avoid trying to send more aTokens than available on balance, due to 1 wei imprecision
                        if (scaledDownLiquidationProtocolFee > scaledDownUserBalance) {
                          vars.liquidationProtocolFeeAmount = scaledDownUserBalance.rayMul(liquidityIndex);
                        }
                        vars.collateralAToken.transferOnLiquidation(
                          params.user,
                          vars.collateralAToken.RESERVE_TREASURY_ADDRESS(),
                          vars.liquidationProtocolFeeAmount
                        );
                      }
                      // burn bad debt if necessary
                      // Each additional debt asset already adds around ~75k gas to the liquidation.
                      // To keep the liquidation gas under control, 0 usd collateral positions are not touched, as there is no immediate benefit in burning or transferring to treasury.
                      if (hasNoCollateralLeft && userConfig.isBorrowingAny()) {
                        _burnBadDebt(reservesData, reservesList, userConfig, params.reservesCount, params.user);
                      }
                      // Transfers the debt asset being repaid to the aToken, where the liquidity is kept
                      IERC20(params.debtAsset).safeTransferFrom(
                        msg.sender,
                        vars.debtReserveCache.aTokenAddress,
                        vars.actualDebtToLiquidate
                      );
                      IAToken(vars.debtReserveCache.aTokenAddress).handleRepayment(
                        msg.sender,
                        params.user,
                        vars.actualDebtToLiquidate
                      );
                      emit LiquidationCall(
                        params.collateralAsset,
                        params.debtAsset,
                        params.user,
                        vars.actualDebtToLiquidate,
                        vars.actualCollateralToLiquidate,
                        msg.sender,
                        params.receiveAToken
                      );
                    }
                    /**
                     * @notice Burns the collateral aTokens and transfers the underlying to the liquidator.
                     * @dev   The function also updates the state and the interest rate of the collateral reserve.
                     * @param collateralReserve The data of the collateral reserve
                     * @param params The additional parameters needed to execute the liquidation function
                     * @param vars The executeLiquidationCall() function local vars
                     */
                    function _burnCollateralATokens(
                      DataTypes.ReserveData storage collateralReserve,
                      DataTypes.ExecuteLiquidationCallParams memory params,
                      LiquidationCallLocalVars memory vars
                    ) internal {
                      DataTypes.ReserveCache memory collateralReserveCache = collateralReserve.cache();
                      collateralReserve.updateState(collateralReserveCache);
                      collateralReserve.updateInterestRatesAndVirtualBalance(
                        collateralReserveCache,
                        params.collateralAsset,
                        0,
                        vars.actualCollateralToLiquidate
                      );
                      // Burn the equivalent amount of aToken, sending the underlying to the liquidator
                      vars.collateralAToken.burn(
                        params.user,
                        msg.sender,
                        vars.actualCollateralToLiquidate,
                        collateralReserveCache.nextLiquidityIndex
                      );
                    }
                    /**
                     * @notice Liquidates the user aTokens by transferring them to the liquidator.
                     * @dev   The function also checks the state of the liquidator and activates the aToken as collateral
                     *        as in standard transfers if the isolation mode constraints are respected.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param usersConfig The users configuration mapping that track the supplied/borrowed assets
                     * @param collateralReserve The data of the collateral reserve
                     * @param params The additional parameters needed to execute the liquidation function
                     * @param vars The executeLiquidationCall() function local vars
                     */
                    function _liquidateATokens(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(address => DataTypes.UserConfigurationMap) storage usersConfig,
                      DataTypes.ReserveData storage collateralReserve,
                      DataTypes.ExecuteLiquidationCallParams memory params,
                      LiquidationCallLocalVars memory vars
                    ) internal {
                      uint256 liquidatorPreviousATokenBalance = IERC20(vars.collateralAToken).balanceOf(msg.sender);
                      vars.collateralAToken.transferOnLiquidation(
                        params.user,
                        msg.sender,
                        vars.actualCollateralToLiquidate
                      );
                      if (
                        liquidatorPreviousATokenBalance == 0 ||
                        // For the special case of msg.sender == params.user (self-liquidation) the liquidatorPreviousATokenBalance
                        // will not yet be 0, but the liquidation will result in collateral being fully liquidated and then resupplied.
                        (msg.sender == params.user &&
                          vars.actualCollateralToLiquidate + vars.liquidationProtocolFeeAmount ==
                          vars.userCollateralBalance)
                      ) {
                        DataTypes.UserConfigurationMap storage liquidatorConfig = usersConfig[msg.sender];
                        if (
                          ValidationLogic.validateAutomaticUseAsCollateral(
                            reservesData,
                            reservesList,
                            liquidatorConfig,
                            collateralReserve.configuration,
                            collateralReserve.aTokenAddress
                          )
                        ) {
                          liquidatorConfig.setUsingAsCollateral(collateralReserve.id, true);
                          emit ReserveUsedAsCollateralEnabled(params.collateralAsset, msg.sender);
                        }
                      }
                    }
                    /**
                     * @notice Burns the debt tokens of the user up to the amount being repaid by the liquidator
                     * or the entire debt if the user is in a bad debt scenario.
                     * @dev The function alters the `debtReserveCache` state in `vars` to update the debt related data.
                     * @param debtReserveCache The cached debt reserve parameters
                     * @param debtReserve The storage pointer of the debt reserve parameters
                     * @param userConfig The pointer of the user configuration
                     * @param user The user address
                     * @param debtAsset The debt asset address
                     * @param actualDebtToLiquidate The actual debt to liquidate
                     * @param hasNoCollateralLeft The flag representing, will user will have no collateral left after liquidation
                     */
                    function _burnDebtTokens(
                      DataTypes.ReserveCache memory debtReserveCache,
                      DataTypes.ReserveData storage debtReserve,
                      DataTypes.UserConfigurationMap storage userConfig,
                      address user,
                      address debtAsset,
                      uint256 userReserveDebt,
                      uint256 actualDebtToLiquidate,
                      bool hasNoCollateralLeft
                    ) internal {
                      // Prior v3.1, there were cases where, after liquidation, the `isBorrowing` flag was left on
                      // even after the user debt was fully repaid, so to avoid this function reverting in the `_burnScaled`
                      // (see ScaledBalanceTokenBase contract), we check for any debt remaining.
                      if (userReserveDebt != 0) {
                        debtReserveCache.nextScaledVariableDebt = IVariableDebtToken(
                          debtReserveCache.variableDebtTokenAddress
                        ).burn(
                            user,
                            hasNoCollateralLeft ? userReserveDebt : actualDebtToLiquidate,
                            debtReserveCache.nextVariableBorrowIndex
                          );
                      }
                      uint256 outstandingDebt = userReserveDebt - actualDebtToLiquidate;
                      if (hasNoCollateralLeft && outstandingDebt != 0) {
                        /**
                         * Special handling of GHO. Implicitly assuming that virtual acc !active == GHO, which is true.
                         * Scenario 1: The amount of GHO debt being liquidated is greater or equal to the GHO accrued interest.
                         *             In this case, the outer handleRepayment will clear the storage and all additional operations can be skipped.
                         * Scenario 2: The amount of debt being liquidated is lower than the GHO accrued interest.
                         *             In this case handleRepayment will be called with the difference required to clear the storage.
                         *             If we assume a liquidation of n debt, and m accrued interest, the difference is k = m-n.
                         *             Therefore we call handleRepayment(k).
                         *             Additionally, as the dao (GHO issuer) accepts the loss on interest on the bad debt,
                         *             we need to discount k from the deficit (via reducing outstandingDebt).
                         * Note: If a non GHO asset is liquidated and GHO bad debt is created in the process, Scenario 2 applies with n = 0.
                         */
                        if (!debtReserveCache.reserveConfiguration.getIsVirtualAccActive()) {
                          uint256 accruedInterest = IGhoVariableDebtToken(debtReserveCache.variableDebtTokenAddress)
                            .getBalanceFromInterest(user);
                          // handleRepayment() will first discount the protocol fee from an internal `accumulatedDebtInterest` variable
                          // and then burn the excess GHO
                          if (accruedInterest != 0 && accruedInterest > actualDebtToLiquidate) {
                            // in order to clean the `accumulatedDebtInterest` storage the function will need to be called with the accruedInterest
                            // discounted by the actualDebtToLiquidate, as in the main flow `handleRepayment` will be called with actualDebtToLiquidate already
                            uint256 amountToBurn = accruedInterest - actualDebtToLiquidate;
                            // In the case of GHO, all obligations are to the protocol
                            // therefore the protocol assumes the losses on interest and only tracks the pure deficit by discounting the not-collected & burned debt
                            outstandingDebt -= amountToBurn;
                            // IMPORTANT: address(0) is used here to indicate that the accrued fee is discounted and not actually repayed.
                            // The value passed has no relevance as it is unused on the aGHO.handleRepayment, therefore the value is purely esthetical.
                            IAToken(debtReserveCache.aTokenAddress).handleRepayment(address(0), user, amountToBurn);
                          }
                        }
                        debtReserve.deficit += outstandingDebt.toUint128();
                        emit DeficitCreated(user, debtAsset, outstandingDebt);
                        outstandingDebt = 0;
                      }
                      if (outstandingDebt == 0) {
                        userConfig.setBorrowing(debtReserve.id, false);
                      }
                      debtReserve.updateInterestRatesAndVirtualBalance(
                        debtReserveCache,
                        debtAsset,
                        actualDebtToLiquidate,
                        0
                      );
                    }
                    struct AvailableCollateralToLiquidateLocalVars {
                      uint256 maxCollateralToLiquidate;
                      uint256 baseCollateral;
                      uint256 bonusCollateral;
                      uint256 collateralAmount;
                      uint256 debtAmountNeeded;
                      uint256 liquidationProtocolFeePercentage;
                      uint256 liquidationProtocolFee;
                      uint256 collateralToLiquidateInBaseCurrency;
                      uint256 collateralAssetPrice;
                    }
                    /**
                     * @notice Calculates how much of a specific collateral can be liquidated, given
                     * a certain amount of debt asset.
                     * @dev This function needs to be called after all the checks to validate the liquidation have been performed,
                     *   otherwise it might fail.
                     * @param collateralReserveConfiguration The data of the collateral reserve
                     * @param collateralAssetPrice The price of the underlying asset used as collateral
                     * @param collateralAssetUnit The asset units of the collateral
                     * @param debtAssetPrice The price of the underlying borrowed asset to be repaid with the liquidation
                     * @param debtAssetUnit The asset units of the debt
                     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                     * @param userCollateralBalance The collateral balance for the specific `collateralAsset` of the user being liquidated
                     * @param liquidationBonus The collateral bonus percentage to receive as result of the liquidation
                     * @return The maximum amount that is possible to liquidate given all the liquidation constraints (user balance, close factor)
                     * @return The amount to repay with the liquidation
                     * @return The fee taken from the liquidation bonus amount to be paid to the protocol
                     * @return The collateral amount to liquidate in the base currency used by the price feed
                     */
                    function _calculateAvailableCollateralToLiquidate(
                      DataTypes.ReserveConfigurationMap memory collateralReserveConfiguration,
                      uint256 collateralAssetPrice,
                      uint256 collateralAssetUnit,
                      uint256 debtAssetPrice,
                      uint256 debtAssetUnit,
                      uint256 debtToCover,
                      uint256 userCollateralBalance,
                      uint256 liquidationBonus
                    ) internal pure returns (uint256, uint256, uint256, uint256) {
                      AvailableCollateralToLiquidateLocalVars memory vars;
                      vars.collateralAssetPrice = collateralAssetPrice;
                      vars.liquidationProtocolFeePercentage = collateralReserveConfiguration
                        .getLiquidationProtocolFee();
                      // This is the base collateral to liquidate based on the given debt to cover
                      vars.baseCollateral =
                        ((debtAssetPrice * debtToCover * collateralAssetUnit)) /
                        (vars.collateralAssetPrice * debtAssetUnit);
                      vars.maxCollateralToLiquidate = vars.baseCollateral.percentMul(liquidationBonus);
                      if (vars.maxCollateralToLiquidate > userCollateralBalance) {
                        vars.collateralAmount = userCollateralBalance;
                        vars.debtAmountNeeded = ((vars.collateralAssetPrice * vars.collateralAmount * debtAssetUnit) /
                          (debtAssetPrice * collateralAssetUnit)).percentDiv(liquidationBonus);
                      } else {
                        vars.collateralAmount = vars.maxCollateralToLiquidate;
                        vars.debtAmountNeeded = debtToCover;
                      }
                      vars.collateralToLiquidateInBaseCurrency =
                        (vars.collateralAmount * vars.collateralAssetPrice) /
                        collateralAssetUnit;
                      if (vars.liquidationProtocolFeePercentage != 0) {
                        vars.bonusCollateral =
                          vars.collateralAmount -
                          vars.collateralAmount.percentDiv(liquidationBonus);
                        vars.liquidationProtocolFee = vars.bonusCollateral.percentMul(
                          vars.liquidationProtocolFeePercentage
                        );
                        vars.collateralAmount -= vars.liquidationProtocolFee;
                      }
                      return (
                        vars.collateralAmount,
                        vars.debtAmountNeeded,
                        vars.liquidationProtocolFee,
                        vars.collateralToLiquidateInBaseCurrency
                      );
                    }
                    /**
                     * @notice Remove a user's bad debt by burning debt tokens.
                     * @dev This function iterates through all active reserves where the user has a debt position,
                     * updates their state, and performs the necessary burn.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param userConfig The user configuration
                     * @param reservesCount The total number of valid reserves
                     * @param user The user from which the debt will be burned.
                     */
                    function _burnBadDebt(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.UserConfigurationMap storage userConfig,
                      uint256 reservesCount,
                      address user
                    ) internal {
                      for (uint256 i; i < reservesCount; i++) {
                        if (!userConfig.isBorrowing(i)) {
                          continue;
                        }
                        address reserveAddress = reservesList[i];
                        if (reserveAddress == address(0)) {
                          continue;
                        }
                        DataTypes.ReserveData storage currentReserve = reservesData[reserveAddress];
                        DataTypes.ReserveCache memory reserveCache = currentReserve.cache();
                        if (!reserveCache.reserveConfiguration.getActive()) continue;
                        currentReserve.updateState(reserveCache);
                        _burnDebtTokens(
                          reserveCache,
                          currentReserve,
                          userConfig,
                          user,
                          reserveAddress,
                          IERC20(reserveCache.variableDebtTokenAddress).balanceOf(user),
                          0,
                          true
                        );
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  library DataTypes {
                    /**
                     * This exists specifically to maintain the `getReserveData()` interface, since the new, internal
                     * `ReserveData` struct includes the reserve's `virtualUnderlyingBalance`.
                     */
                    struct ReserveDataLegacy {
                      //stores the reserve configuration
                      ReserveConfigurationMap configuration;
                      //the liquidity index. Expressed in ray
                      uint128 liquidityIndex;
                      //the current supply rate. Expressed in ray
                      uint128 currentLiquidityRate;
                      //variable borrow index. Expressed in ray
                      uint128 variableBorrowIndex;
                      //the current variable borrow rate. Expressed in ray
                      uint128 currentVariableBorrowRate;
                      // DEPRECATED on v3.2.0
                      uint128 currentStableBorrowRate;
                      //timestamp of last update
                      uint40 lastUpdateTimestamp;
                      //the id of the reserve. Represents the position in the list of the active reserves
                      uint16 id;
                      //aToken address
                      address aTokenAddress;
                      // DEPRECATED on v3.2.0
                      address stableDebtTokenAddress;
                      //variableDebtToken address
                      address variableDebtTokenAddress;
                      //address of the interest rate strategy
                      address interestRateStrategyAddress;
                      //the current treasury balance, scaled
                      uint128 accruedToTreasury;
                      //the outstanding unbacked aTokens minted through the bridging feature
                      uint128 unbacked;
                      //the outstanding debt borrowed against this asset in isolation mode
                      uint128 isolationModeTotalDebt;
                    }
                    struct ReserveData {
                      //stores the reserve configuration
                      ReserveConfigurationMap configuration;
                      //the liquidity index. Expressed in ray
                      uint128 liquidityIndex;
                      //the current supply rate. Expressed in ray
                      uint128 currentLiquidityRate;
                      //variable borrow index. Expressed in ray
                      uint128 variableBorrowIndex;
                      //the current variable borrow rate. Expressed in ray
                      uint128 currentVariableBorrowRate;
                      /// @notice reused `__deprecatedStableBorrowRate` storage from pre 3.2
                      // the current accumulate deficit in underlying tokens
                      uint128 deficit;
                      //timestamp of last update
                      uint40 lastUpdateTimestamp;
                      //the id of the reserve. Represents the position in the list of the active reserves
                      uint16 id;
                      //timestamp until when liquidations are not allowed on the reserve, if set to past liquidations will be allowed
                      uint40 liquidationGracePeriodUntil;
                      //aToken address
                      address aTokenAddress;
                      // DEPRECATED on v3.2.0
                      address __deprecatedStableDebtTokenAddress;
                      //variableDebtToken address
                      address variableDebtTokenAddress;
                      //address of the interest rate strategy
                      address interestRateStrategyAddress;
                      //the current treasury balance, scaled
                      uint128 accruedToTreasury;
                      //the outstanding unbacked aTokens minted through the bridging feature
                      uint128 unbacked;
                      //the outstanding debt borrowed against this asset in isolation mode
                      uint128 isolationModeTotalDebt;
                      //the amount of underlying accounted for by the protocol
                      uint128 virtualUnderlyingBalance;
                    }
                    struct ReserveConfigurationMap {
                      //bit 0-15: LTV
                      //bit 16-31: Liq. threshold
                      //bit 32-47: Liq. bonus
                      //bit 48-55: Decimals
                      //bit 56: reserve is active
                      //bit 57: reserve is frozen
                      //bit 58: borrowing is enabled
                      //bit 59: DEPRECATED: stable rate borrowing enabled
                      //bit 60: asset is paused
                      //bit 61: borrowing in isolation mode is enabled
                      //bit 62: siloed borrowing enabled
                      //bit 63: flashloaning enabled
                      //bit 64-79: reserve factor
                      //bit 80-115: borrow cap in whole tokens, borrowCap == 0 => no cap
                      //bit 116-151: supply cap in whole tokens, supplyCap == 0 => no cap
                      //bit 152-167: liquidation protocol fee
                      //bit 168-175: DEPRECATED: eMode category
                      //bit 176-211: unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
                      //bit 212-251: debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
                      //bit 252: virtual accounting is enabled for the reserve
                      //bit 253-255 unused
                      uint256 data;
                    }
                    struct UserConfigurationMap {
                      /**
                       * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.
                       * The first bit indicates if an asset is used as collateral by the user, the second whether an
                       * asset is borrowed by the user.
                       */
                      uint256 data;
                    }
                    // DEPRECATED: kept for backwards compatibility, might be removed in a future version
                    struct EModeCategoryLegacy {
                      // each eMode category has a custom ltv and liquidation threshold
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                      // DEPRECATED
                      address priceSource;
                      string label;
                    }
                    struct CollateralConfig {
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                    }
                    struct EModeCategoryBaseConfiguration {
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                      string label;
                    }
                    struct EModeCategory {
                      // each eMode category has a custom ltv and liquidation threshold
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                      uint128 collateralBitmap;
                      string label;
                      uint128 borrowableBitmap;
                    }
                    enum InterestRateMode {
                      NONE,
                      __DEPRECATED,
                      VARIABLE
                    }
                    struct ReserveCache {
                      uint256 currScaledVariableDebt;
                      uint256 nextScaledVariableDebt;
                      uint256 currLiquidityIndex;
                      uint256 nextLiquidityIndex;
                      uint256 currVariableBorrowIndex;
                      uint256 nextVariableBorrowIndex;
                      uint256 currLiquidityRate;
                      uint256 currVariableBorrowRate;
                      uint256 reserveFactor;
                      ReserveConfigurationMap reserveConfiguration;
                      address aTokenAddress;
                      address variableDebtTokenAddress;
                      uint40 reserveLastUpdateTimestamp;
                    }
                    struct ExecuteLiquidationCallParams {
                      uint256 reservesCount;
                      uint256 debtToCover;
                      address collateralAsset;
                      address debtAsset;
                      address user;
                      bool receiveAToken;
                      address priceOracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                    }
                    struct ExecuteSupplyParams {
                      address asset;
                      uint256 amount;
                      address onBehalfOf;
                      uint16 referralCode;
                    }
                    struct ExecuteBorrowParams {
                      address asset;
                      address user;
                      address onBehalfOf;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      uint16 referralCode;
                      bool releaseUnderlying;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                    }
                    struct ExecuteRepayParams {
                      address asset;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      address onBehalfOf;
                      bool useATokens;
                    }
                    struct ExecuteWithdrawParams {
                      address asset;
                      uint256 amount;
                      address to;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                    }
                    struct ExecuteEliminateDeficitParams {
                      address asset;
                      uint256 amount;
                    }
                    struct ExecuteSetUserEModeParams {
                      uint256 reservesCount;
                      address oracle;
                      uint8 categoryId;
                    }
                    struct FinalizeTransferParams {
                      address asset;
                      address from;
                      address to;
                      uint256 amount;
                      uint256 balanceFromBefore;
                      uint256 balanceToBefore;
                      uint256 reservesCount;
                      address oracle;
                      uint8 fromEModeCategory;
                    }
                    struct FlashloanParams {
                      address receiverAddress;
                      address[] assets;
                      uint256[] amounts;
                      uint256[] interestRateModes;
                      address onBehalfOf;
                      bytes params;
                      uint16 referralCode;
                      uint256 flashLoanPremiumToProtocol;
                      uint256 flashLoanPremiumTotal;
                      uint256 reservesCount;
                      address addressesProvider;
                      address pool;
                      uint8 userEModeCategory;
                      bool isAuthorizedFlashBorrower;
                    }
                    struct FlashloanSimpleParams {
                      address receiverAddress;
                      address asset;
                      uint256 amount;
                      bytes params;
                      uint16 referralCode;
                      uint256 flashLoanPremiumToProtocol;
                      uint256 flashLoanPremiumTotal;
                    }
                    struct FlashLoanRepaymentParams {
                      uint256 amount;
                      uint256 totalPremium;
                      uint256 flashLoanPremiumToProtocol;
                      address asset;
                      address receiverAddress;
                      uint16 referralCode;
                    }
                    struct CalculateUserAccountDataParams {
                      UserConfigurationMap userConfig;
                      uint256 reservesCount;
                      address user;
                      address oracle;
                      uint8 userEModeCategory;
                    }
                    struct ValidateBorrowParams {
                      ReserveCache reserveCache;
                      UserConfigurationMap userConfig;
                      address asset;
                      address userAddress;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                      bool isolationModeActive;
                      address isolationModeCollateralAddress;
                      uint256 isolationModeDebtCeiling;
                    }
                    struct ValidateLiquidationCallParams {
                      ReserveCache debtReserveCache;
                      uint256 totalDebt;
                      uint256 healthFactor;
                      address priceOracleSentinel;
                    }
                    struct CalculateInterestRatesParams {
                      uint256 unbacked;
                      uint256 liquidityAdded;
                      uint256 liquidityTaken;
                      uint256 totalDebt;
                      uint256 reserveFactor;
                      address reserve;
                      bool usingVirtualBalance;
                      uint256 virtualUnderlyingBalance;
                    }
                    struct InitReserveParams {
                      address asset;
                      address aTokenAddress;
                      address variableDebtAddress;
                      address interestRateStrategyAddress;
                      uint16 reservesCount;
                      uint16 maxNumberReserves;
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  import {IAToken} from '../../../interfaces/IAToken.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {Errors} from '../helpers/Errors.sol';
                  import {ValidationLogic} from './ValidationLogic.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  /**
                   * @title BridgeLogic library
                   * @author Aave
                   * @notice Implements functions to mint unbacked aTokens and back the unbacked tokens.
                   */
                  library BridgeLogic {
                    using ReserveLogic for DataTypes.ReserveCache;
                    using ReserveLogic for DataTypes.ReserveData;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    using SafeCast for uint256;
                    using GPv2SafeERC20 for IERC20;
                    // See `IPool` for descriptions
                    event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                    event MintUnbacked(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      uint16 indexed referralCode
                    );
                    event BackUnbacked(address indexed reserve, address indexed backer, uint256 amount, uint256 fee);
                    /**
                     * @notice Mint unbacked aTokens to a user and updates the unbacked for the reserve.
                     * @dev Essentially a supply without transferring the underlying.
                     * @dev Emits the `MintUnbacked` event
                     * @dev Emits the `ReserveUsedAsCollateralEnabled` if asset is set as collateral
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                     * @param asset The address of the underlying asset to mint aTokens of
                     * @param amount The amount to mint
                     * @param onBehalfOf The address that will receive the aTokens
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function executeMintUnbacked(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.UserConfigurationMap storage userConfig,
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode
                    ) external {
                      DataTypes.ReserveData storage reserve = reservesData[asset];
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      reserve.updateState(reserveCache);
                      ValidationLogic.validateSupply(reserveCache, reserve, amount, onBehalfOf);
                      uint256 unbackedMintCap = reserveCache.reserveConfiguration.getUnbackedMintCap();
                      uint256 reserveDecimals = reserveCache.reserveConfiguration.getDecimals();
                      uint256 unbacked = reserve.unbacked += amount.toUint128();
                      require(
                        unbacked <= unbackedMintCap * (10 ** reserveDecimals),
                        Errors.UNBACKED_MINT_CAP_EXCEEDED
                      );
                      reserve.updateInterestRatesAndVirtualBalance(reserveCache, asset, 0, 0);
                      bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
                        msg.sender,
                        onBehalfOf,
                        amount,
                        reserveCache.nextLiquidityIndex
                      );
                      if (isFirstSupply) {
                        if (
                          ValidationLogic.validateAutomaticUseAsCollateral(
                            reservesData,
                            reservesList,
                            userConfig,
                            reserveCache.reserveConfiguration,
                            reserveCache.aTokenAddress
                          )
                        ) {
                          userConfig.setUsingAsCollateral(reserve.id, true);
                          emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf);
                        }
                      }
                      emit MintUnbacked(asset, msg.sender, onBehalfOf, amount, referralCode);
                    }
                    /**
                     * @notice Back the current unbacked with `amount` and pay `fee`.
                     * @dev It is not possible to back more than the existing unbacked amount of the reserve
                     * @dev Emits the `BackUnbacked` event
                     * @param reserve The reserve to back unbacked for
                     * @param asset The address of the underlying asset to repay
                     * @param amount The amount to back
                     * @param fee The amount paid in fees
                     * @param protocolFeeBps The fraction of fees in basis points paid to the protocol
                     * @return The backed amount
                     */
                    function executeBackUnbacked(
                      DataTypes.ReserveData storage reserve,
                      address asset,
                      uint256 amount,
                      uint256 fee,
                      uint256 protocolFeeBps
                    ) external returns (uint256) {
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      reserve.updateState(reserveCache);
                      uint256 backingAmount = (amount < reserve.unbacked) ? amount : reserve.unbacked;
                      uint256 feeToProtocol = fee.percentMul(protocolFeeBps);
                      uint256 feeToLP = fee - feeToProtocol;
                      uint256 added = backingAmount + fee;
                      reserveCache.nextLiquidityIndex = reserve.cumulateToLiquidityIndex(
                        IERC20(reserveCache.aTokenAddress).totalSupply() +
                          uint256(reserve.accruedToTreasury).rayMul(reserveCache.nextLiquidityIndex),
                        feeToLP
                      );
                      reserve.accruedToTreasury += feeToProtocol.rayDiv(reserveCache.nextLiquidityIndex).toUint128();
                      reserve.unbacked -= backingAmount.toUint128();
                      reserve.updateInterestRatesAndVirtualBalance(reserveCache, asset, added, 0);
                      IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, added);
                      emit BackUnbacked(asset, msg.sender, backingAmount, fee);
                      return backingAmount;
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
                  /**
                   * @title IERC20WithPermit
                   * @author Aave
                   * @notice Interface for the permit function (EIP-2612)
                   */
                  interface IERC20WithPermit is IERC20 {
                    /**
                     * @notice Allow passing a signed message to approve spending
                     * @dev implements the permit function as for
                     * https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
                     * @param owner The owner of the funds
                     * @param spender The spender
                     * @param value The amount
                     * @param deadline The deadline timestamp, type(uint256).max for max deadline
                     * @param v Signature param
                     * @param s Signature param
                     * @param r Signature param
                     */
                    function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                    ) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                  import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                  /**
                   * @title IPool
                   * @author Aave
                   * @notice Defines the basic interface for an Aave Pool.
                   */
                  interface IPool {
                    /**
                     * @dev Emitted on mintUnbacked()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address initiating the supply
                     * @param onBehalfOf The beneficiary of the supplied assets, receiving the aTokens
                     * @param amount The amount of supplied assets
                     * @param referralCode The referral code used
                     */
                    event MintUnbacked(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted on backUnbacked()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param backer The address paying for the backing
                     * @param amount The amount added as backing
                     * @param fee The amount paid in fees
                     */
                    event BackUnbacked(address indexed reserve, address indexed backer, uint256 amount, uint256 fee);
                    /**
                     * @dev Emitted on supply()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address initiating the supply
                     * @param onBehalfOf The beneficiary of the supply, receiving the aTokens
                     * @param amount The amount supplied
                     * @param referralCode The referral code used
                     */
                    event Supply(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted on withdraw()
                     * @param reserve The address of the underlying asset being withdrawn
                     * @param user The address initiating the withdrawal, owner of aTokens
                     * @param to The address that will receive the underlying
                     * @param amount The amount to be withdrawn
                     */
                    event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
                    /**
                     * @dev Emitted on borrow() and flashLoan() when debt needs to be opened
                     * @param reserve The address of the underlying asset being borrowed
                     * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just
                     * initiator of the transaction on flashLoan()
                     * @param onBehalfOf The address that will be getting the debt
                     * @param amount The amount borrowed out
                     * @param interestRateMode The rate mode: 2 for Variable, 1 is deprecated (changed on v3.2.0)
                     * @param borrowRate The numeric rate at which the user has borrowed, expressed in ray
                     * @param referralCode The referral code used
                     */
                    event Borrow(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      DataTypes.InterestRateMode interestRateMode,
                      uint256 borrowRate,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted on repay()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The beneficiary of the repayment, getting his debt reduced
                     * @param repayer The address of the user initiating the repay(), providing the funds
                     * @param amount The amount repaid
                     * @param useATokens True if the repayment is done using aTokens, `false` if done with underlying asset directly
                     */
                    event Repay(
                      address indexed reserve,
                      address indexed user,
                      address indexed repayer,
                      uint256 amount,
                      bool useATokens
                    );
                    /**
                     * @dev Emitted on borrow(), repay() and liquidationCall() when using isolated assets
                     * @param asset The address of the underlying asset of the reserve
                     * @param totalDebt The total isolation mode debt for the reserve
                     */
                    event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                    /**
                     * @dev Emitted when the user selects a certain asset category for eMode
                     * @param user The address of the user
                     * @param categoryId The category id
                     */
                    event UserEModeSet(address indexed user, uint8 categoryId);
                    /**
                     * @dev Emitted on setUserUseReserveAsCollateral()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address of the user enabling the usage as collateral
                     */
                    event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                    /**
                     * @dev Emitted on setUserUseReserveAsCollateral()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address of the user enabling the usage as collateral
                     */
                    event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                    /**
                     * @dev Emitted on flashLoan()
                     * @param target The address of the flash loan receiver contract
                     * @param initiator The address initiating the flash loan
                     * @param asset The address of the asset being flash borrowed
                     * @param amount The amount flash borrowed
                     * @param interestRateMode The flashloan mode: 0 for regular flashloan,
                     *        1 for Stable (Deprecated on v3.2.0), 2 for Variable
                     * @param premium The fee flash borrowed
                     * @param referralCode The referral code used
                     */
                    event FlashLoan(
                      address indexed target,
                      address initiator,
                      address indexed asset,
                      uint256 amount,
                      DataTypes.InterestRateMode interestRateMode,
                      uint256 premium,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted when a borrower is liquidated.
                     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                     * @param user The address of the borrower getting liquidated
                     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                     * @param liquidatedCollateralAmount The amount of collateral received by the liquidator
                     * @param liquidator The address of the liquidator
                     * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants
                     * to receive the underlying collateral asset directly
                     */
                    event LiquidationCall(
                      address indexed collateralAsset,
                      address indexed debtAsset,
                      address indexed user,
                      uint256 debtToCover,
                      uint256 liquidatedCollateralAmount,
                      address liquidator,
                      bool receiveAToken
                    );
                    /**
                     * @dev Emitted when the state of a reserve is updated.
                     * @param reserve The address of the underlying asset of the reserve
                     * @param liquidityRate The next liquidity rate
                     * @param stableBorrowRate The next stable borrow rate @note deprecated on v3.2.0
                     * @param variableBorrowRate The next variable borrow rate
                     * @param liquidityIndex The next liquidity index
                     * @param variableBorrowIndex The next variable borrow index
                     */
                    event ReserveDataUpdated(
                      address indexed reserve,
                      uint256 liquidityRate,
                      uint256 stableBorrowRate,
                      uint256 variableBorrowRate,
                      uint256 liquidityIndex,
                      uint256 variableBorrowIndex
                    );
                    /**
                     * @dev Emitted when the deficit of a reserve is covered.
                     * @param reserve The address of the underlying asset of the reserve
                     * @param caller The caller that triggered the DeficitCovered event
                     * @param amountCovered The amount of deficit covered
                     */
                    event DeficitCovered(address indexed reserve, address caller, uint256 amountCovered);
                    /**
                     * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest.
                     * @param reserve The address of the reserve
                     * @param amountMinted The amount minted to the treasury
                     */
                    event MintedToTreasury(address indexed reserve, uint256 amountMinted);
                    /**
                     * @dev Emitted when deficit is realized on a liquidation.
                     * @param user The user address where the bad debt will be burned
                     * @param debtAsset The address of the underlying borrowed asset to be burned
                     * @param amountCreated The amount of deficit created
                     */
                    event DeficitCreated(address indexed user, address indexed debtAsset, uint256 amountCreated);
                    /**
                     * @notice Mints an `amount` of aTokens to the `onBehalfOf`
                     * @param asset The address of the underlying asset to mint
                     * @param amount The amount to mint
                     * @param onBehalfOf The address that will receive the aTokens
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function mintUnbacked(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode
                    ) external;
                    /**
                     * @notice Back the current unbacked underlying with `amount` and pay `fee`.
                     * @param asset The address of the underlying asset to back
                     * @param amount The amount to back
                     * @param fee The amount paid in fees
                     * @return The backed amount
                     */
                    function backUnbacked(address asset, uint256 amount, uint256 fee) external returns (uint256);
                    /**
                     * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                     * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                     * @param asset The address of the underlying asset to supply
                     * @param amount The amount to be supplied
                     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                     *   is a different wallet
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
                    /**
                     * @notice Supply with transfer approval of asset to be supplied done via permit function
                     * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                     * @param asset The address of the underlying asset to supply
                     * @param amount The amount to be supplied
                     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                     *   is a different wallet
                     * @param deadline The deadline timestamp that the permit is valid
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     * @param permitV The V parameter of ERC712 permit sig
                     * @param permitR The R parameter of ERC712 permit sig
                     * @param permitS The S parameter of ERC712 permit sig
                     */
                    function supplyWithPermit(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode,
                      uint256 deadline,
                      uint8 permitV,
                      bytes32 permitR,
                      bytes32 permitS
                    ) external;
                    /**
                     * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
                     * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
                     * @param asset The address of the underlying asset to withdraw
                     * @param amount The underlying amount to be withdrawn
                     *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
                     * @param to The address that will receive the underlying, same as msg.sender if the user
                     *   wants to receive it on his own wallet, or a different address if the beneficiary is a
                     *   different wallet
                     * @return The final amount withdrawn
                     */
                    function withdraw(address asset, uint256 amount, address to) external returns (uint256);
                    /**
                     * @notice Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
                     * already supplied enough collateral, or he was given enough allowance by a credit delegator on the VariableDebtToken
                     * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
                     *   and 100 variable debt tokens
                     * @param asset The address of the underlying asset to borrow
                     * @param amount The amount to be borrowed
                     * @param interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     * @param onBehalfOf The address of the user who will receive the debt. Should be the address of the borrower itself
                     * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
                     * if he has been given credit delegation allowance
                     */
                    function borrow(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      uint16 referralCode,
                      address onBehalfOf
                    ) external;
                    /**
                     * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
                     * - E.g. User repays 100 USDC, burning 100 variable debt tokens of the `onBehalfOf` address
                     * @param asset The address of the borrowed underlying asset previously borrowed
                     * @param amount The amount to repay
                     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                     * @param interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                     * @param onBehalfOf The address of the user who will get his debt reduced/removed. Should be the address of the
                     * user calling the function if he wants to reduce/remove his own debt, or the address of any other
                     * other borrower whose debt should be removed
                     * @return The final amount repaid
                     */
                    function repay(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      address onBehalfOf
                    ) external returns (uint256);
                    /**
                     * @notice Repay with transfer approval of asset to be repaid done via permit function
                     * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                     * @param asset The address of the borrowed underlying asset previously borrowed
                     * @param amount The amount to repay
                     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                     * @param interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                     * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
                     * user calling the function if he wants to reduce/remove his own debt, or the address of any other
                     * other borrower whose debt should be removed
                     * @param deadline The deadline timestamp that the permit is valid
                     * @param permitV The V parameter of ERC712 permit sig
                     * @param permitR The R parameter of ERC712 permit sig
                     * @param permitS The S parameter of ERC712 permit sig
                     * @return The final amount repaid
                     */
                    function repayWithPermit(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      address onBehalfOf,
                      uint256 deadline,
                      uint8 permitV,
                      bytes32 permitR,
                      bytes32 permitS
                    ) external returns (uint256);
                    /**
                     * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the
                     * equivalent debt tokens
                     * - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable debt tokens
                     * @dev  Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken
                     * balance is not enough to cover the whole debt
                     * @param asset The address of the borrowed underlying asset previously borrowed
                     * @param amount The amount to repay
                     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                     * @param interestRateMode DEPRECATED in v3.2.0
                     * @return The final amount repaid
                     */
                    function repayWithATokens(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode
                    ) external returns (uint256);
                    /**
                     * @notice Allows suppliers to enable/disable a specific supplied asset as collateral
                     * @param asset The address of the underlying asset supplied
                     * @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise
                     */
                    function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
                    /**
                     * @notice Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
                     * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
                     *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
                     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                     * @param user The address of the borrower getting liquidated
                     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                     * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants
                     * to receive the underlying collateral asset directly
                     */
                    function liquidationCall(
                      address collateralAsset,
                      address debtAsset,
                      address user,
                      uint256 debtToCover,
                      bool receiveAToken
                    ) external;
                    /**
                     * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                     * as long as the amount taken plus a fee is returned.
                     * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
                     * into consideration. For further details please visit https://docs.aave.com/developers/
                     * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanReceiver interface
                     * @param assets The addresses of the assets being flash-borrowed
                     * @param amounts The amounts of the assets being flash-borrowed
                     * @param interestRateModes Types of the debt to open if the flash loan is not returned:
                     *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
                     *   1 -> Deprecated on v3.2.0
                     *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                     * @param onBehalfOf The address  that will receive the debt in the case of using 2 on `modes`
                     * @param params Variadic packed params to pass to the receiver as extra information
                     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function flashLoan(
                      address receiverAddress,
                      address[] calldata assets,
                      uint256[] calldata amounts,
                      uint256[] calldata interestRateModes,
                      address onBehalfOf,
                      bytes calldata params,
                      uint16 referralCode
                    ) external;
                    /**
                     * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                     * as long as the amount taken plus a fee is returned.
                     * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
                     * into consideration. For further details please visit https://docs.aave.com/developers/
                     * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanSimpleReceiver interface
                     * @param asset The address of the asset being flash-borrowed
                     * @param amount The amount of the asset being flash-borrowed
                     * @param params Variadic packed params to pass to the receiver as extra information
                     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function flashLoanSimple(
                      address receiverAddress,
                      address asset,
                      uint256 amount,
                      bytes calldata params,
                      uint16 referralCode
                    ) external;
                    /**
                     * @notice Returns the user account data across all the reserves
                     * @param user The address of the user
                     * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
                     * @return totalDebtBase The total debt of the user in the base currency used by the price feed
                     * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
                     * @return currentLiquidationThreshold The liquidation threshold of the user
                     * @return ltv The loan to value of The user
                     * @return healthFactor The current health factor of the user
                     */
                    function getUserAccountData(
                      address user
                    )
                      external
                      view
                      returns (
                        uint256 totalCollateralBase,
                        uint256 totalDebtBase,
                        uint256 availableBorrowsBase,
                        uint256 currentLiquidationThreshold,
                        uint256 ltv,
                        uint256 healthFactor
                      );
                    /**
                     * @notice Initializes a reserve, activating it, assigning an aToken and debt tokens and an
                     * interest rate strategy
                     * @dev Only callable by the PoolConfigurator contract
                     * @param asset The address of the underlying asset of the reserve
                     * @param aTokenAddress The address of the aToken that will be assigned to the reserve
                     * @param variableDebtAddress The address of the VariableDebtToken that will be assigned to the reserve
                     * @param interestRateStrategyAddress The address of the interest rate strategy contract
                     */
                    function initReserve(
                      address asset,
                      address aTokenAddress,
                      address variableDebtAddress,
                      address interestRateStrategyAddress
                    ) external;
                    /**
                     * @notice Drop a reserve
                     * @dev Only callable by the PoolConfigurator contract
                     * @dev Does not reset eMode flags, which must be considered when reusing the same reserve id for a different reserve.
                     * @param asset The address of the underlying asset of the reserve
                     */
                    function dropReserve(address asset) external;
                    /**
                     * @notice Updates the address of the interest rate strategy contract
                     * @dev Only callable by the PoolConfigurator contract
                     * @param asset The address of the underlying asset of the reserve
                     * @param rateStrategyAddress The address of the interest rate strategy contract
                     */
                    function setReserveInterestRateStrategyAddress(
                      address asset,
                      address rateStrategyAddress
                    ) external;
                    /**
                     * @notice Accumulates interest to all indexes of the reserve
                     * @dev Only callable by the PoolConfigurator contract
                     * @dev To be used when required by the configurator, for example when updating interest rates strategy data
                     * @param asset The address of the underlying asset of the reserve
                     */
                    function syncIndexesState(address asset) external;
                    /**
                     * @notice Updates interest rates on the reserve data
                     * @dev Only callable by the PoolConfigurator contract
                     * @dev To be used when required by the configurator, for example when updating interest rates strategy data
                     * @param asset The address of the underlying asset of the reserve
                     */
                    function syncRatesState(address asset) external;
                    /**
                     * @notice Sets the configuration bitmap of the reserve as a whole
                     * @dev Only callable by the PoolConfigurator contract
                     * @param asset The address of the underlying asset of the reserve
                     * @param configuration The new configuration bitmap
                     */
                    function setConfiguration(
                      address asset,
                      DataTypes.ReserveConfigurationMap calldata configuration
                    ) external;
                    /**
                     * @notice Returns the configuration of the reserve
                     * @param asset The address of the underlying asset of the reserve
                     * @return The configuration of the reserve
                     */
                    function getConfiguration(
                      address asset
                    ) external view returns (DataTypes.ReserveConfigurationMap memory);
                    /**
                     * @notice Returns the configuration of the user across all the reserves
                     * @param user The user address
                     * @return The configuration of the user
                     */
                    function getUserConfiguration(
                      address user
                    ) external view returns (DataTypes.UserConfigurationMap memory);
                    /**
                     * @notice Returns the normalized income of the reserve
                     * @param asset The address of the underlying asset of the reserve
                     * @return The reserve's normalized income
                     */
                    function getReserveNormalizedIncome(address asset) external view returns (uint256);
                    /**
                     * @notice Returns the normalized variable debt per unit of asset
                     * @dev WARNING: This function is intended to be used primarily by the protocol itself to get a
                     * "dynamic" variable index based on time, current stored index and virtual rate at the current
                     * moment (approx. a borrower would get if opening a position). This means that is always used in
                     * combination with variable debt supply/balances.
                     * If using this function externally, consider that is possible to have an increasing normalized
                     * variable debt that is not equivalent to how the variable debt index would be updated in storage
                     * (e.g. only updates with non-zero variable debt supply)
                     * @param asset The address of the underlying asset of the reserve
                     * @return The reserve normalized variable debt
                     */
                    function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);
                    /**
                     * @notice Returns the state and configuration of the reserve
                     * @param asset The address of the underlying asset of the reserve
                     * @return The state and configuration data of the reserve
                     */
                    function getReserveData(address asset) external view returns (DataTypes.ReserveDataLegacy memory);
                    /**
                     * @notice Returns the virtual underlying balance of the reserve
                     * @param asset The address of the underlying asset of the reserve
                     * @return The reserve virtual underlying balance
                     */
                    function getVirtualUnderlyingBalance(address asset) external view returns (uint128);
                    /**
                     * @notice Validates and finalizes an aToken transfer
                     * @dev Only callable by the overlying aToken of the `asset`
                     * @param asset The address of the underlying asset of the aToken
                     * @param from The user from which the aTokens are transferred
                     * @param to The user receiving the aTokens
                     * @param amount The amount being transferred/withdrawn
                     * @param balanceFromBefore The aToken balance of the `from` user before the transfer
                     * @param balanceToBefore The aToken balance of the `to` user before the transfer
                     */
                    function finalizeTransfer(
                      address asset,
                      address from,
                      address to,
                      uint256 amount,
                      uint256 balanceFromBefore,
                      uint256 balanceToBefore
                    ) external;
                    /**
                     * @notice Returns the list of the underlying assets of all the initialized reserves
                     * @dev It does not include dropped reserves
                     * @return The addresses of the underlying assets of the initialized reserves
                     */
                    function getReservesList() external view returns (address[] memory);
                    /**
                     * @notice Returns the number of initialized reserves
                     * @dev It includes dropped reserves
                     * @return The count
                     */
                    function getReservesCount() external view returns (uint256);
                    /**
                     * @notice Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypes.ReserveData struct
                     * @param id The id of the reserve as stored in the DataTypes.ReserveData struct
                     * @return The address of the reserve associated with id
                     */
                    function getReserveAddressById(uint16 id) external view returns (address);
                    /**
                     * @notice Returns the PoolAddressesProvider connected to this contract
                     * @return The address of the PoolAddressesProvider
                     */
                    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                    /**
                     * @notice Updates the protocol fee on the bridging
                     * @param bridgeProtocolFee The part of the premium sent to the protocol treasury
                     */
                    function updateBridgeProtocolFee(uint256 bridgeProtocolFee) external;
                    /**
                     * @notice Updates flash loan premiums. Flash loan premium consists of two parts:
                     * - A part is sent to aToken holders as extra, one time accumulated interest
                     * - A part is collected by the protocol treasury
                     * @dev The total premium is calculated on the total borrowed amount
                     * @dev The premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal`
                     * @dev Only callable by the PoolConfigurator contract
                     * @param flashLoanPremiumTotal The total premium, expressed in bps
                     * @param flashLoanPremiumToProtocol The part of the premium sent to the protocol treasury, expressed in bps
                     */
                    function updateFlashloanPremiums(
                      uint128 flashLoanPremiumTotal,
                      uint128 flashLoanPremiumToProtocol
                    ) external;
                    /**
                     * @notice Configures a new or alters an existing collateral configuration of an eMode.
                     * @dev In eMode, the protocol allows very high borrowing power to borrow assets of the same category.
                     * The category 0 is reserved as it's the default for volatile assets
                     * @param id The id of the category
                     * @param config The configuration of the category
                     */
                    function configureEModeCategory(
                      uint8 id,
                      DataTypes.EModeCategoryBaseConfiguration memory config
                    ) external;
                    /**
                     * @notice Replaces the current eMode collateralBitmap.
                     * @param id The id of the category
                     * @param collateralBitmap The collateralBitmap of the category
                     */
                    function configureEModeCategoryCollateralBitmap(uint8 id, uint128 collateralBitmap) external;
                    /**
                     * @notice Replaces the current eMode borrowableBitmap.
                     * @param id The id of the category
                     * @param borrowableBitmap The borrowableBitmap of the category
                     */
                    function configureEModeCategoryBorrowableBitmap(uint8 id, uint128 borrowableBitmap) external;
                    /**
                     * @notice Returns the data of an eMode category
                     * @dev DEPRECATED use independent getters instead
                     * @param id The id of the category
                     * @return The configuration data of the category
                     */
                    function getEModeCategoryData(
                      uint8 id
                    ) external view returns (DataTypes.EModeCategoryLegacy memory);
                    /**
                     * @notice Returns the label of an eMode category
                     * @param id The id of the category
                     * @return The label of the category
                     */
                    function getEModeCategoryLabel(uint8 id) external view returns (string memory);
                    /**
                     * @notice Returns the collateral config of an eMode category
                     * @param id The id of the category
                     * @return The ltv,lt,lb of the category
                     */
                    function getEModeCategoryCollateralConfig(
                      uint8 id
                    ) external view returns (DataTypes.CollateralConfig memory);
                    /**
                     * @notice Returns the collateralBitmap of an eMode category
                     * @param id The id of the category
                     * @return The collateralBitmap of the category
                     */
                    function getEModeCategoryCollateralBitmap(uint8 id) external view returns (uint128);
                    /**
                     * @notice Returns the borrowableBitmap of an eMode category
                     * @param id The id of the category
                     * @return The borrowableBitmap of the category
                     */
                    function getEModeCategoryBorrowableBitmap(uint8 id) external view returns (uint128);
                    /**
                     * @notice Allows a user to use the protocol in eMode
                     * @param categoryId The id of the category
                     */
                    function setUserEMode(uint8 categoryId) external;
                    /**
                     * @notice Returns the eMode the user is using
                     * @param user The address of the user
                     * @return The eMode id
                     */
                    function getUserEMode(address user) external view returns (uint256);
                    /**
                     * @notice Resets the isolation mode total debt of the given asset to zero
                     * @dev It requires the given asset has zero debt ceiling
                     * @param asset The address of the underlying asset to reset the isolationModeTotalDebt
                     */
                    function resetIsolationModeTotalDebt(address asset) external;
                    /**
                     * @notice Sets the liquidation grace period of the given asset
                     * @dev To enable a liquidation grace period, a timestamp in the future should be set,
                     *      To disable a liquidation grace period, any timestamp in the past works, like 0
                     * @param asset The address of the underlying asset to set the liquidationGracePeriod
                     * @param until Timestamp when the liquidation grace period will end
                     **/
                    function setLiquidationGracePeriod(address asset, uint40 until) external;
                    /**
                     * @notice Returns the liquidation grace period of the given asset
                     * @param asset The address of the underlying asset
                     * @return Timestamp when the liquidation grace period will end
                     **/
                    function getLiquidationGracePeriod(address asset) external view returns (uint40);
                    /**
                     * @notice Returns the total fee on flash loans
                     * @return The total fee on flashloans
                     */
                    function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint128);
                    /**
                     * @notice Returns the part of the bridge fees sent to protocol
                     * @return The bridge fee sent to the protocol treasury
                     */
                    function BRIDGE_PROTOCOL_FEE() external view returns (uint256);
                    /**
                     * @notice Returns the part of the flashloan fees sent to protocol
                     * @return The flashloan fee sent to the protocol treasury
                     */
                    function FLASHLOAN_PREMIUM_TO_PROTOCOL() external view returns (uint128);
                    /**
                     * @notice Returns the maximum number of reserves supported to be listed in this Pool
                     * @return The maximum number of reserves supported
                     */
                    function MAX_NUMBER_RESERVES() external view returns (uint16);
                    /**
                     * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens
                     * @param assets The list of reserves for which the minting needs to be executed
                     */
                    function mintToTreasury(address[] calldata assets) external;
                    /**
                     * @notice Rescue and transfer tokens locked in this contract
                     * @param token The address of the token
                     * @param to The address of the recipient
                     * @param amount The amount of token to transfer
                     */
                    function rescueTokens(address token, address to, uint256 amount) external;
                    /**
                     * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                     * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                     * @dev Deprecated: Use the `supply` function instead
                     * @param asset The address of the underlying asset to supply
                     * @param amount The amount to be supplied
                     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                     *   is a different wallet
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function deposit(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
                    /**
                     * @notice It covers the deficit of a specified reserve by burning:
                     * - the equivalent aToken `amount` for assets with virtual accounting enabled
                     * - the equivalent `amount` of underlying for assets with virtual accounting disabled (e.g. GHO)
                     * @dev The deficit of a reserve can occur due to situations where borrowed assets are not repaid, leading to bad debt.
                     * @param asset The address of the underlying asset to cover the deficit.
                     * @param amount The amount to be covered, in aToken or underlying on non-virtual accounted assets
                     */
                    function eliminateReserveDeficit(address asset, uint256 amount) external;
                    /**
                     * @notice Returns the current deficit of a reserve.
                     * @param asset The address of the underlying asset of the reserve
                     * @return The current deficit of the reserve
                     */
                    function getReserveDeficit(address asset) external view returns (uint256);
                    /**
                     * @notice Returns the aToken address of a reserve.
                     * @param asset The address of the underlying asset of the reserve
                     * @return The address of the aToken
                     */
                    function getReserveAToken(address asset) external view returns (address);
                    /**
                     * @notice Returns the variableDebtToken address of a reserve.
                     * @param asset The address of the underlying asset of the reserve
                     * @return The address of the variableDebtToken
                     */
                    function getReserveVariableDebtToken(address asset) external view returns (address);
                    /**
                     * @notice Gets the address of the external FlashLoanLogic
                     */
                    function getFlashLoanLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external BorrowLogic
                     */
                    function getBorrowLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external BridgeLogic
                     */
                    function getBridgeLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external EModeLogic
                     */
                    function getEModeLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external LiquidationLogic
                     */
                    function getLiquidationLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external PoolLogic
                     */
                    function getPoolLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external SupplyLogic
                     */
                    function getSupplyLogic() external view returns (address);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                  /**
                   * @title IACLManager
                   * @author Aave
                   * @notice Defines the basic interface for the ACL Manager
                   */
                  interface IACLManager {
                    /**
                     * @notice Returns the contract address of the PoolAddressesProvider
                     * @return The address of the PoolAddressesProvider
                     */
                    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                    /**
                     * @notice Returns the identifier of the PoolAdmin role
                     * @return The id of the PoolAdmin role
                     */
                    function POOL_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the EmergencyAdmin role
                     * @return The id of the EmergencyAdmin role
                     */
                    function EMERGENCY_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the RiskAdmin role
                     * @return The id of the RiskAdmin role
                     */
                    function RISK_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the FlashBorrower role
                     * @return The id of the FlashBorrower role
                     */
                    function FLASH_BORROWER_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the Bridge role
                     * @return The id of the Bridge role
                     */
                    function BRIDGE_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the AssetListingAdmin role
                     * @return The id of the AssetListingAdmin role
                     */
                    function ASSET_LISTING_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Set the role as admin of a specific role.
                     * @dev By default the admin role for all roles is `DEFAULT_ADMIN_ROLE`.
                     * @param role The role to be managed by the admin role
                     * @param adminRole The admin role
                     */
                    function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
                    /**
                     * @notice Adds a new admin as PoolAdmin
                     * @param admin The address of the new admin
                     */
                    function addPoolAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as PoolAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removePoolAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is PoolAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is PoolAdmin, false otherwise
                     */
                    function isPoolAdmin(address admin) external view returns (bool);
                    /**
                     * @notice Adds a new admin as EmergencyAdmin
                     * @param admin The address of the new admin
                     */
                    function addEmergencyAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as EmergencyAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removeEmergencyAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is EmergencyAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is EmergencyAdmin, false otherwise
                     */
                    function isEmergencyAdmin(address admin) external view returns (bool);
                    /**
                     * @notice Adds a new admin as RiskAdmin
                     * @param admin The address of the new admin
                     */
                    function addRiskAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as RiskAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removeRiskAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is RiskAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is RiskAdmin, false otherwise
                     */
                    function isRiskAdmin(address admin) external view returns (bool);
                    /**
                     * @notice Adds a new address as FlashBorrower
                     * @param borrower The address of the new FlashBorrower
                     */
                    function addFlashBorrower(address borrower) external;
                    /**
                     * @notice Removes an address as FlashBorrower
                     * @param borrower The address of the FlashBorrower to remove
                     */
                    function removeFlashBorrower(address borrower) external;
                    /**
                     * @notice Returns true if the address is FlashBorrower, false otherwise
                     * @param borrower The address to check
                     * @return True if the given address is FlashBorrower, false otherwise
                     */
                    function isFlashBorrower(address borrower) external view returns (bool);
                    /**
                     * @notice Adds a new address as Bridge
                     * @param bridge The address of the new Bridge
                     */
                    function addBridge(address bridge) external;
                    /**
                     * @notice Removes an address as Bridge
                     * @param bridge The address of the bridge to remove
                     */
                    function removeBridge(address bridge) external;
                    /**
                     * @notice Returns true if the address is Bridge, false otherwise
                     * @param bridge The address to check
                     * @return True if the given address is Bridge, false otherwise
                     */
                    function isBridge(address bridge) external view returns (bool);
                    /**
                     * @notice Adds a new admin as AssetListingAdmin
                     * @param admin The address of the new admin
                     */
                    function addAssetListingAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as AssetListingAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removeAssetListingAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is AssetListingAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is AssetListingAdmin, false otherwise
                     */
                    function isAssetListingAdmin(address admin) external view returns (bool);
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol';
                  import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
                  import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
                  import {DataTypes} from '../libraries/types/DataTypes.sol';
                  /**
                   * @title PoolStorage
                   * @author Aave
                   * @notice Contract used as storage of the Pool contract.
                   * @dev It defines the storage layout of the Pool contract.
                   */
                  contract PoolStorage {
                    using ReserveLogic for DataTypes.ReserveData;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    // Map of reserves and their data (underlyingAssetOfReserve => reserveData)
                    mapping(address => DataTypes.ReserveData) internal _reserves;
                    // Map of users address and their configuration data (userAddress => userConfiguration)
                    mapping(address => DataTypes.UserConfigurationMap) internal _usersConfig;
                    // List of reserves as a map (reserveId => reserve).
                    // It is structured as a mapping for gas savings reasons, using the reserve id as index
                    mapping(uint256 => address) internal _reservesList;
                    // List of eMode categories as a map (eModeCategoryId => eModeCategory).
                    // It is structured as a mapping for gas savings reasons, using the eModeCategoryId as index
                    mapping(uint8 => DataTypes.EModeCategory) internal _eModeCategories;
                    // Map of users address and their eMode category (userAddress => eModeCategoryId)
                    mapping(address => uint8) internal _usersEModeCategory;
                    // Fee of the protocol bridge, expressed in bps
                    uint256 internal _bridgeProtocolFee;
                    // Total FlashLoan Premium, expressed in bps
                    uint128 internal _flashLoanPremiumTotal;
                    // FlashLoan premium paid to protocol treasury, expressed in bps
                    uint128 internal _flashLoanPremiumToProtocol;
                    // DEPRECATED on v3.2.0
                    uint64 internal __DEPRECATED_maxStableRateBorrowSizePercent;
                    // Maximum number of active reserves there have been in the protocol. It is the upper bound of the reserves list
                    uint16 internal _reservesCount;
                  }
                  // SPDX-License-Identifier: LGPL-3.0-or-later
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../openzeppelin/contracts/IERC20.sol';
                  /// @title Gnosis Protocol v2 Safe ERC20 Transfer Library
                  /// @author Gnosis Developers
                  /// @dev Gas-efficient version of Openzeppelin's SafeERC20 contract.
                  library GPv2SafeERC20 {
                    /// @dev Wrapper around a call to the ERC20 function `transfer` that reverts
                    /// also when the token returns `false`.
                    function safeTransfer(IERC20 token, address to, uint256 value) internal {
                      bytes4 selector_ = token.transfer.selector;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        let freeMemoryPointer := mload(0x40)
                        mstore(freeMemoryPointer, selector_)
                        mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                        mstore(add(freeMemoryPointer, 36), value)
                        if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)) {
                          returndatacopy(0, 0, returndatasize())
                          revert(0, returndatasize())
                        }
                      }
                      require(getLastTransferResult(token), 'GPv2: failed transfer');
                    }
                    /// @dev Wrapper around a call to the ERC20 function `transferFrom` that
                    /// reverts also when the token returns `false`.
                    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                      bytes4 selector_ = token.transferFrom.selector;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        let freeMemoryPointer := mload(0x40)
                        mstore(freeMemoryPointer, selector_)
                        mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
                        mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                        mstore(add(freeMemoryPointer, 68), value)
                        if iszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)) {
                          returndatacopy(0, 0, returndatasize())
                          revert(0, returndatasize())
                        }
                      }
                      require(getLastTransferResult(token), 'GPv2: failed transferFrom');
                    }
                    /// @dev Verifies that the last return was a successful `transfer*` call.
                    /// This is done by checking that the return data is either empty, or
                    /// is a valid ABI encoded boolean.
                    function getLastTransferResult(IERC20 token) private view returns (bool success) {
                      // NOTE: Inspecting previous return data requires assembly. Note that
                      // we write the return data to memory 0 in the case where the return
                      // data size is 32, this is OK since the first 64 bytes of memory are
                      // reserved by Solidy as a scratch space that can be used within
                      // assembly blocks.
                      // <https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html>
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        /// @dev Revert with an ABI encoded Solidity error with a message
                        /// that fits into 32-bytes.
                        ///
                        /// An ABI encoded Solidity error has the following memory layout:
                        ///
                        /// ------------+----------------------------------
                        ///  byte range | value
                        /// ------------+----------------------------------
                        ///  0x00..0x04 |        selector("Error(string)")
                        ///  0x04..0x24 |      string offset (always 0x20)
                        ///  0x24..0x44 |                    string length
                        ///  0x44..0x64 | string value, padded to 32-bytes
                        function revertWithMessage(length, message) {
                          mstore(0x00, '\\x08\\xc3\\x79\\xa0')
                          mstore(0x04, 0x20)
                          mstore(0x24, length)
                          mstore(0x44, message)
                          revert(0x00, 0x64)
                        }
                        switch returndatasize()
                        // Non-standard ERC20 transfer without return.
                        case 0 {
                          // NOTE: When the return data size is 0, verify that there
                          // is code at the address. This is done in order to maintain
                          // compatibility with Solidity calling conventions.
                          // <https://docs.soliditylang.org/en/v0.7.6/control-structures.html#external-function-calls>
                          if iszero(extcodesize(token)) {
                            revertWithMessage(20, 'GPv2: not a contract')
                          }
                          success := 1
                        }
                        // Standard ERC20 transfer returning boolean success value.
                        case 32 {
                          returndatacopy(0, 0, returndatasize())
                          // NOTE: For ABI encoding v1, any non-zero value is accepted
                          // as `true` for a boolean. In order to stay compatible with
                          // OpenZeppelin's `SafeERC20` library which is known to work
                          // with the existing ERC20 implementation we care about,
                          // make sure we return success for any non-zero return value
                          // from the `transfer*` call.
                          success := iszero(iszero(mload(0)))
                        }
                        default {
                          revertWithMessage(31, 'GPv2: malformed transfer result')
                        }
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize, which returns 0 for contracts in
                      // construction, since the code is only stored at the end of the
                      // constructor execution.
                      uint256 size;
                      assembly {
                        size := extcodesize(account)
                      }
                      return size > 0;
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, 'Address: insufficient balance');
                      (bool success, ) = recipient.call{value: amount}('');
                      require(success, 'Address: unable to send value, recipient may have reverted');
                    }
                    /**
                     * @dev Performs a Solidity function call using a low level `call`. A
                     * plain `call` is an unsafe replacement for a function call: use this
                     * function instead.
                     *
                     * If `target` reverts with a revert reason, it is bubbled up by this
                     * function (like regular Solidity function calls).
                     *
                     * Returns the raw returned data. To convert to the expected return value,
                     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                     *
                     * Requirements:
                     *
                     * - `target` must be a contract.
                     * - calling `target` with `data` must not revert.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCall(target, data, 'Address: low-level call failed');
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                     * `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                    ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but also transferring `value` wei to `target`.
                     *
                     * Requirements:
                     *
                     * - the calling contract must have an ETH balance of at least `value`.
                     * - the called Solidity function must be `payable`.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value
                    ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, 'Address: low-level call with value failed');
                    }
                    /**
                     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                     * with `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value,
                      string memory errorMessage
                    ) internal returns (bytes memory) {
                      require(address(this).balance >= value, 'Address: insufficient balance for call');
                      require(isContract(target), 'Address: call to non-contract');
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(
                      address target,
                      bytes memory data
                    ) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, 'Address: low-level static call failed');
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                    ) internal view returns (bytes memory) {
                      require(isContract(target), 'Address: static call to non-contract');
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, 'Address: low-level delegate call failed');
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                    ) internal returns (bytes memory) {
                      require(isContract(target), 'Address: delegate call to non-contract');
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                     * revert reason using the provided one.
                     *
                     * _Available since v4.3._
                     */
                    function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                    ) internal pure returns (bytes memory) {
                      if (success) {
                        return returndata;
                      } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                          }
                        } else {
                          revert(errorMessage);
                        }
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @dev Interface of the ERC20 standard as defined in the EIP.
                   */
                  interface IERC20 {
                    /**
                     * @dev Returns the amount of tokens in existence.
                     */
                    function totalSupply() external view returns (uint256);
                    /**
                     * @dev Returns the amount of tokens owned by `account`.
                     */
                    function balanceOf(address account) external view returns (uint256);
                    /**
                     * @dev Moves `amount` tokens from the caller's account to `recipient`.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transfer(address recipient, uint256 amount) external returns (bool);
                    /**
                     * @dev Returns the remaining number of tokens that `spender` will be
                     * allowed to spend on behalf of `owner` through {transferFrom}. This is
                     * zero by default.
                     *
                     * This value changes when {approve} or {transferFrom} are called.
                     */
                    function allowance(address owner, address spender) external view returns (uint256);
                    /**
                     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * IMPORTANT: Beware that changing an allowance with this method brings the risk
                     * that someone may use both the old and the new allowance by unfortunate
                     * transaction ordering. One possible solution to mitigate this race
                     * condition is to first reduce the spender's allowance to 0 and set the
                     * desired value afterwards:
                     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                     *
                     * Emits an {Approval} event.
                     */
                    function approve(address spender, uint256 amount) external returns (bool);
                    /**
                     * @dev Moves `amount` tokens from `sender` to `recipient` using the
                     * allowance mechanism. `amount` is then deducted from the caller's
                     * allowance.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                    /**
                     * @dev Emitted when `value` tokens are moved from one account (`from`) to
                     * another (`to`).
                     *
                     * Note that `value` may be zero.
                     */
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    /**
                     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                     * a call to {approve}. `value` is the new allowance.
                     */
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                  import {IInitializableAToken} from './IInitializableAToken.sol';
                  /**
                   * @title IAToken
                   * @author Aave
                   * @notice Defines the basic interface for an AToken.
                   */
                  interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken {
                    /**
                     * @dev Emitted during the transfer action
                     * @param from The user whose tokens are being transferred
                     * @param to The recipient
                     * @param value The scaled amount being transferred
                     * @param index The next liquidity index of the reserve
                     */
                    event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index);
                    /**
                     * @notice Mints `amount` aTokens to `user`
                     * @param caller The address performing the mint
                     * @param onBehalfOf The address of the user that will receive the minted aTokens
                     * @param amount The amount of tokens getting minted
                     * @param index The next liquidity index of the reserve
                     * @return `true` if the the previous balance of the user was 0
                     */
                    function mint(
                      address caller,
                      address onBehalfOf,
                      uint256 amount,
                      uint256 index
                    ) external returns (bool);
                    /**
                     * @notice Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
                     * @dev In some instances, the mint event could be emitted from a burn transaction
                     * if the amount to burn is less than the interest that the user accrued
                     * @param from The address from which the aTokens will be burned
                     * @param receiverOfUnderlying The address that will receive the underlying
                     * @param amount The amount being burned
                     * @param index The next liquidity index of the reserve
                     */
                    function burn(address from, address receiverOfUnderlying, uint256 amount, uint256 index) external;
                    /**
                     * @notice Mints aTokens to the reserve treasury
                     * @param amount The amount of tokens getting minted
                     * @param index The next liquidity index of the reserve
                     */
                    function mintToTreasury(uint256 amount, uint256 index) external;
                    /**
                     * @notice Transfers aTokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken
                     * @param from The address getting liquidated, current owner of the aTokens
                     * @param to The recipient
                     * @param value The amount of tokens getting transferred
                     */
                    function transferOnLiquidation(address from, address to, uint256 value) external;
                    /**
                     * @notice Transfers the underlying asset to `target`.
                     * @dev Used by the Pool to transfer assets in borrow(), withdraw() and flashLoan()
                     * @param target The recipient of the underlying
                     * @param amount The amount getting transferred
                     */
                    function transferUnderlyingTo(address target, uint256 amount) external;
                    /**
                     * @notice Handles the underlying received by the aToken after the transfer has been completed.
                     * @dev The default implementation is empty as with standard ERC20 tokens, nothing needs to be done after the
                     * transfer is concluded. However in the future there may be aTokens that allow for example to stake the underlying
                     * to receive LM rewards. In that case, `handleRepayment()` would perform the staking of the underlying asset.
                     * @param user The user executing the repayment
                     * @param onBehalfOf The address of the user who will get his debt reduced/removed
                     * @param amount The amount getting repaid
                     */
                    function handleRepayment(address user, address onBehalfOf, uint256 amount) external;
                    /**
                     * @notice Allow passing a signed message to approve spending
                     * @dev implements the permit function as for
                     * https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
                     * @param owner The owner of the funds
                     * @param spender The spender
                     * @param value The amount
                     * @param deadline The deadline timestamp, type(uint256).max for max deadline
                     * @param v Signature param
                     * @param s Signature param
                     * @param r Signature param
                     */
                    function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                    ) external;
                    /**
                     * @notice Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH)
                     * @return The address of the underlying asset
                     */
                    function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                    /**
                     * @notice Returns the address of the Aave treasury, receiving the fees on this aToken.
                     * @return Address of the Aave treasury
                     */
                    function RESERVE_TREASURY_ADDRESS() external view returns (address);
                    /**
                     * @notice Get the domain separator for the token
                     * @dev Return cached value if chainId matches cache, otherwise recomputes separator
                     * @return The domain separator of the token at current chain
                     */
                    function DOMAIN_SEPARATOR() external view returns (bytes32);
                    /**
                     * @notice Returns the nonce for owner.
                     * @param owner The address of the owner
                     * @return The nonce of the owner
                     */
                    function nonces(address owner) external view returns (uint256);
                    /**
                     * @notice Rescue and transfer tokens locked in this contract
                     * @param token The address of the token
                     * @param to The address of the recipient
                     * @param amount The amount of token to transfer
                     */
                    function rescueTokens(address token, address to, uint256 amount) external;
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  /**
                   * @title WadRayMath library
                   * @author Aave
                   * @notice Provides functions to perform calculations with Wad and Ray units
                   * @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers
                   * with 27 digits of precision)
                   * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                   */
                  library WadRayMath {
                    // HALF_WAD and HALF_RAY expressed with extended notation as constant with operations are not supported in Yul assembly
                    uint256 internal constant WAD = 1e18;
                    uint256 internal constant HALF_WAD = 0.5e18;
                    uint256 internal constant RAY = 1e27;
                    uint256 internal constant HALF_RAY = 0.5e27;
                    uint256 internal constant WAD_RAY_RATIO = 1e9;
                    /**
                     * @dev Multiplies two wad, rounding half up to the nearest wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @param b Wad
                     * @return c = a*b, in wad
                     */
                    function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b
                      assembly {
                        if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, b), HALF_WAD), WAD)
                      }
                    }
                    /**
                     * @dev Divides two wad, rounding half up to the nearest wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @param b Wad
                     * @return c = a/b, in wad
                     */
                    function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - halfB) / WAD
                      assembly {
                        if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, WAD), div(b, 2)), b)
                      }
                    }
                    /**
                     * @notice Multiplies two ray, rounding half up to the nearest ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @param b Ray
                     * @return c = a raymul b
                     */
                    function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - HALF_RAY) / b
                      assembly {
                        if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, b), HALF_RAY), RAY)
                      }
                    }
                    /**
                     * @notice Divides two ray, rounding half up to the nearest ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @param b Ray
                     * @return c = a raydiv b
                     */
                    function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - halfB) / RAY
                      assembly {
                        if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, RAY), div(b, 2)), b)
                      }
                    }
                    /**
                     * @dev Casts ray down to wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @return b = a converted to wad, rounded half up to the nearest wad
                     */
                    function rayToWad(uint256 a) internal pure returns (uint256 b) {
                      assembly {
                        b := div(a, WAD_RAY_RATIO)
                        let remainder := mod(a, WAD_RAY_RATIO)
                        if iszero(lt(remainder, div(WAD_RAY_RATIO, 2))) {
                          b := add(b, 1)
                        }
                      }
                    }
                    /**
                     * @dev Converts wad up to ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @return b = a converted in ray
                     */
                    function wadToRay(uint256 a) internal pure returns (uint256 b) {
                      // to avoid overflow, b/WAD_RAY_RATIO == a
                      assembly {
                        b := mul(a, WAD_RAY_RATIO)
                        if iszero(eq(div(b, WAD_RAY_RATIO), a)) {
                          revert(0, 0)
                        }
                      }
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {Address} from '../../../dependencies/openzeppelin/contracts/Address.sol';
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                  import {IAToken} from '../../../interfaces/IAToken.sol';
                  import {IPriceOracleSentinel} from '../../../interfaces/IPriceOracleSentinel.sol';
                  import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                  import {IAccessControl} from '../../../dependencies/openzeppelin/contracts/IAccessControl.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                  import {EModeConfiguration} from '../configuration/EModeConfiguration.sol';
                  import {Errors} from '../helpers/Errors.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  import {GenericLogic} from './GenericLogic.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  import {IncentivizedERC20} from '../../tokenization/base/IncentivizedERC20.sol';
                  /**
                   * @title ValidationLogic library
                   * @author Aave
                   * @notice Implements functions to validate the different actions of the protocol
                   */
                  library ValidationLogic {
                    using ReserveLogic for DataTypes.ReserveData;
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    using SafeCast for uint256;
                    using GPv2SafeERC20 for IERC20;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    using Address for address;
                    // Factor to apply to "only-variable-debt" liquidity rate to get threshold for rebalancing, expressed in bps
                    // A value of 0.9e4 results in 90%
                    uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 0.9e4;
                    // Minimum health factor allowed under any circumstance
                    // A value of 0.95e18 results in 0.95
                    uint256 public constant MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 0.95e18;
                    /**
                     * @dev Minimum health factor to consider a user position healthy
                     * A value of 1e18 results in 1
                     */
                    uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1e18;
                    /**
                     * @dev Role identifier for the role allowed to supply isolated reserves as collateral
                     */
                    bytes32 public constant ISOLATED_COLLATERAL_SUPPLIER_ROLE =
                      keccak256('ISOLATED_COLLATERAL_SUPPLIER');
                    /**
                     * @notice Validates a supply action.
                     * @param reserveCache The cached data of the reserve
                     * @param amount The amount to be supplied
                     */
                    function validateSupply(
                      DataTypes.ReserveCache memory reserveCache,
                      DataTypes.ReserveData storage reserve,
                      uint256 amount,
                      address onBehalfOf
                    ) internal view {
                      require(amount != 0, Errors.INVALID_AMOUNT);
                      (bool isActive, bool isFrozen, , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                      require(isActive, Errors.RESERVE_INACTIVE);
                      require(!isPaused, Errors.RESERVE_PAUSED);
                      require(!isFrozen, Errors.RESERVE_FROZEN);
                      require(onBehalfOf != reserveCache.aTokenAddress, Errors.SUPPLY_TO_ATOKEN);
                      uint256 supplyCap = reserveCache.reserveConfiguration.getSupplyCap();
                      require(
                        supplyCap == 0 ||
                          ((IAToken(reserveCache.aTokenAddress).scaledTotalSupply() +
                            uint256(reserve.accruedToTreasury)).rayMul(reserveCache.nextLiquidityIndex) + amount) <=
                          supplyCap * (10 ** reserveCache.reserveConfiguration.getDecimals()),
                        Errors.SUPPLY_CAP_EXCEEDED
                      );
                    }
                    /**
                     * @notice Validates a withdraw action.
                     * @param reserveCache The cached data of the reserve
                     * @param amount The amount to be withdrawn
                     * @param userBalance The balance of the user
                     */
                    function validateWithdraw(
                      DataTypes.ReserveCache memory reserveCache,
                      uint256 amount,
                      uint256 userBalance
                    ) internal pure {
                      require(amount != 0, Errors.INVALID_AMOUNT);
                      require(amount <= userBalance, Errors.NOT_ENOUGH_AVAILABLE_USER_BALANCE);
                      (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                      require(isActive, Errors.RESERVE_INACTIVE);
                      require(!isPaused, Errors.RESERVE_PAUSED);
                    }
                    struct ValidateBorrowLocalVars {
                      uint256 currentLtv;
                      uint256 collateralNeededInBaseCurrency;
                      uint256 userCollateralInBaseCurrency;
                      uint256 userDebtInBaseCurrency;
                      uint256 availableLiquidity;
                      uint256 healthFactor;
                      uint256 totalDebt;
                      uint256 totalSupplyVariableDebt;
                      uint256 reserveDecimals;
                      uint256 borrowCap;
                      uint256 amountInBaseCurrency;
                      uint256 assetUnit;
                      address siloedBorrowingAddress;
                      bool isActive;
                      bool isFrozen;
                      bool isPaused;
                      bool borrowingEnabled;
                      bool siloedBorrowingEnabled;
                    }
                    /**
                     * @notice Validates a borrow action.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param params Additional params needed for the validation
                     */
                    function validateBorrow(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.ValidateBorrowParams memory params
                    ) internal view {
                      require(params.amount != 0, Errors.INVALID_AMOUNT);
                      ValidateBorrowLocalVars memory vars;
                      (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.isPaused) = params
                        .reserveCache
                        .reserveConfiguration
                        .getFlags();
                      require(vars.isActive, Errors.RESERVE_INACTIVE);
                      require(!vars.isPaused, Errors.RESERVE_PAUSED);
                      require(!vars.isFrozen, Errors.RESERVE_FROZEN);
                      require(vars.borrowingEnabled, Errors.BORROWING_NOT_ENABLED);
                      require(
                        !params.reserveCache.reserveConfiguration.getIsVirtualAccActive() ||
                          IERC20(params.reserveCache.aTokenAddress).totalSupply() >= params.amount,
                        Errors.INVALID_AMOUNT
                      );
                      require(
                        params.priceOracleSentinel == address(0) ||
                          IPriceOracleSentinel(params.priceOracleSentinel).isBorrowAllowed(),
                        Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
                      );
                      //validate interest rate mode
                      require(
                        params.interestRateMode == DataTypes.InterestRateMode.VARIABLE,
                        Errors.INVALID_INTEREST_RATE_MODE_SELECTED
                      );
                      vars.reserveDecimals = params.reserveCache.reserveConfiguration.getDecimals();
                      vars.borrowCap = params.reserveCache.reserveConfiguration.getBorrowCap();
                      unchecked {
                        vars.assetUnit = 10 ** vars.reserveDecimals;
                      }
                      if (vars.borrowCap != 0) {
                        vars.totalSupplyVariableDebt = params.reserveCache.currScaledVariableDebt.rayMul(
                          params.reserveCache.nextVariableBorrowIndex
                        );
                        vars.totalDebt = vars.totalSupplyVariableDebt + params.amount;
                        unchecked {
                          require(vars.totalDebt <= vars.borrowCap * vars.assetUnit, Errors.BORROW_CAP_EXCEEDED);
                        }
                      }
                      if (params.isolationModeActive) {
                        // check that the asset being borrowed is borrowable in isolation mode AND
                        // the total exposure is no bigger than the collateral debt ceiling
                        require(
                          params.reserveCache.reserveConfiguration.getBorrowableInIsolation(),
                          Errors.ASSET_NOT_BORROWABLE_IN_ISOLATION
                        );
                        require(
                          reservesData[params.isolationModeCollateralAddress].isolationModeTotalDebt +
                            (params.amount /
                              10 ** (vars.reserveDecimals - ReserveConfiguration.DEBT_CEILING_DECIMALS))
                              .toUint128() <=
                            params.isolationModeDebtCeiling,
                          Errors.DEBT_CEILING_EXCEEDED
                        );
                      }
                      if (params.userEModeCategory != 0) {
                        require(
                          EModeConfiguration.isReserveEnabledOnBitmap(
                            eModeCategories[params.userEModeCategory].borrowableBitmap,
                            reservesData[params.asset].id
                          ),
                          Errors.NOT_BORROWABLE_IN_EMODE
                        );
                      }
                      (
                        vars.userCollateralInBaseCurrency,
                        vars.userDebtInBaseCurrency,
                        vars.currentLtv,
                        ,
                        vars.healthFactor,
                      ) = GenericLogic.calculateUserAccountData(
                        reservesData,
                        reservesList,
                        eModeCategories,
                        DataTypes.CalculateUserAccountDataParams({
                          userConfig: params.userConfig,
                          reservesCount: params.reservesCount,
                          user: params.userAddress,
                          oracle: params.oracle,
                          userEModeCategory: params.userEModeCategory
                        })
                      );
                      require(vars.userCollateralInBaseCurrency != 0, Errors.COLLATERAL_BALANCE_IS_ZERO);
                      require(vars.currentLtv != 0, Errors.LTV_VALIDATION_FAILED);
                      require(
                        vars.healthFactor > HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                        Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
                      );
                      vars.amountInBaseCurrency =
                        IPriceOracleGetter(params.oracle).getAssetPrice(params.asset) *
                        params.amount;
                      unchecked {
                        vars.amountInBaseCurrency /= vars.assetUnit;
                      }
                      //add the current already borrowed amount to the amount requested to calculate the total collateral needed.
                      vars.collateralNeededInBaseCurrency = (vars.userDebtInBaseCurrency + vars.amountInBaseCurrency)
                        .percentDiv(vars.currentLtv); //LTV is calculated in percentage
                      require(
                        vars.collateralNeededInBaseCurrency <= vars.userCollateralInBaseCurrency,
                        Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW
                      );
                      if (params.userConfig.isBorrowingAny()) {
                        (vars.siloedBorrowingEnabled, vars.siloedBorrowingAddress) = params
                          .userConfig
                          .getSiloedBorrowingState(reservesData, reservesList);
                        if (vars.siloedBorrowingEnabled) {
                          require(vars.siloedBorrowingAddress == params.asset, Errors.SILOED_BORROWING_VIOLATION);
                        } else {
                          require(
                            !params.reserveCache.reserveConfiguration.getSiloedBorrowing(),
                            Errors.SILOED_BORROWING_VIOLATION
                          );
                        }
                      }
                    }
                    /**
                     * @notice Validates a repay action.
                     * @param reserveCache The cached data of the reserve
                     * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1)
                     * @param onBehalfOf The address of the user msg.sender is repaying for
                     * @param debt The borrow balance of the user
                     */
                    function validateRepay(
                      DataTypes.ReserveCache memory reserveCache,
                      uint256 amountSent,
                      DataTypes.InterestRateMode interestRateMode,
                      address onBehalfOf,
                      uint256 debt
                    ) internal view {
                      require(amountSent != 0, Errors.INVALID_AMOUNT);
                      require(
                        interestRateMode == DataTypes.InterestRateMode.VARIABLE,
                        Errors.INVALID_INTEREST_RATE_MODE_SELECTED
                      );
                      require(
                        amountSent != type(uint256).max || msg.sender == onBehalfOf,
                        Errors.NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF
                      );
                      (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                      require(isActive, Errors.RESERVE_INACTIVE);
                      require(!isPaused, Errors.RESERVE_PAUSED);
                      require(debt != 0, Errors.NO_DEBT_OF_SELECTED_TYPE);
                    }
                    /**
                     * @notice Validates the action of setting an asset as collateral.
                     * @param reserveCache The cached data of the reserve
                     * @param userBalance The balance of the user
                     */
                    function validateSetUseReserveAsCollateral(
                      DataTypes.ReserveCache memory reserveCache,
                      uint256 userBalance
                    ) internal pure {
                      require(userBalance != 0, Errors.UNDERLYING_BALANCE_ZERO);
                      (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                      require(isActive, Errors.RESERVE_INACTIVE);
                      require(!isPaused, Errors.RESERVE_PAUSED);
                    }
                    /**
                     * @notice Validates a flashloan action.
                     * @param reservesData The state of all the reserves
                     * @param assets The assets being flash-borrowed
                     * @param amounts The amounts for each asset being borrowed
                     */
                    function validateFlashloan(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      address[] memory assets,
                      uint256[] memory amounts
                    ) internal view {
                      require(assets.length == amounts.length, Errors.INCONSISTENT_FLASHLOAN_PARAMS);
                      for (uint256 i = 0; i < assets.length; i++) {
                        for (uint256 j = i + 1; j < assets.length; j++) {
                          require(assets[i] != assets[j], Errors.INCONSISTENT_FLASHLOAN_PARAMS);
                        }
                        validateFlashloanSimple(reservesData[assets[i]], amounts[i]);
                      }
                    }
                    /**
                     * @notice Validates a flashloan action.
                     * @param reserve The state of the reserve
                     */
                    function validateFlashloanSimple(
                      DataTypes.ReserveData storage reserve,
                      uint256 amount
                    ) internal view {
                      DataTypes.ReserveConfigurationMap memory configuration = reserve.configuration;
                      require(!configuration.getPaused(), Errors.RESERVE_PAUSED);
                      require(configuration.getActive(), Errors.RESERVE_INACTIVE);
                      require(configuration.getFlashLoanEnabled(), Errors.FLASHLOAN_DISABLED);
                      require(
                        !configuration.getIsVirtualAccActive() ||
                          IERC20(reserve.aTokenAddress).totalSupply() >= amount,
                        Errors.INVALID_AMOUNT
                      );
                    }
                    struct ValidateLiquidationCallLocalVars {
                      bool collateralReserveActive;
                      bool collateralReservePaused;
                      bool principalReserveActive;
                      bool principalReservePaused;
                      bool isCollateralEnabled;
                    }
                    /**
                     * @notice Validates the liquidation action.
                     * @param userConfig The user configuration mapping
                     * @param collateralReserve The reserve data of the collateral
                     * @param debtReserve The reserve data of the debt
                     * @param params Additional parameters needed for the validation
                     */
                    function validateLiquidationCall(
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ReserveData storage collateralReserve,
                      DataTypes.ReserveData storage debtReserve,
                      DataTypes.ValidateLiquidationCallParams memory params
                    ) internal view {
                      ValidateLiquidationCallLocalVars memory vars;
                      (vars.collateralReserveActive, , , vars.collateralReservePaused) = collateralReserve
                        .configuration
                        .getFlags();
                      (vars.principalReserveActive, , , vars.principalReservePaused) = params
                        .debtReserveCache
                        .reserveConfiguration
                        .getFlags();
                      require(vars.collateralReserveActive && vars.principalReserveActive, Errors.RESERVE_INACTIVE);
                      require(!vars.collateralReservePaused && !vars.principalReservePaused, Errors.RESERVE_PAUSED);
                      require(
                        params.priceOracleSentinel == address(0) ||
                          params.healthFactor < MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD ||
                          IPriceOracleSentinel(params.priceOracleSentinel).isLiquidationAllowed(),
                        Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
                      );
                      require(
                        collateralReserve.liquidationGracePeriodUntil < uint40(block.timestamp) &&
                          debtReserve.liquidationGracePeriodUntil < uint40(block.timestamp),
                        Errors.LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED
                      );
                      require(
                        params.healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                        Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD
                      );
                      vars.isCollateralEnabled =
                        collateralReserve.configuration.getLiquidationThreshold() != 0 &&
                        userConfig.isUsingAsCollateral(collateralReserve.id);
                      //if collateral isn't enabled as collateral by user, it cannot be liquidated
                      require(vars.isCollateralEnabled, Errors.COLLATERAL_CANNOT_BE_LIQUIDATED);
                      require(params.totalDebt != 0, Errors.SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER);
                    }
                    /**
                     * @notice Validates the health factor of a user.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param userConfig The state of the user for the specific reserve
                     * @param user The user to validate health factor of
                     * @param userEModeCategory The users active efficiency mode category
                     * @param reservesCount The number of available reserves
                     * @param oracle The price oracle
                     */
                    function validateHealthFactor(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap memory userConfig,
                      address user,
                      uint8 userEModeCategory,
                      uint256 reservesCount,
                      address oracle
                    ) internal view returns (uint256, bool) {
                      (, , , , uint256 healthFactor, bool hasZeroLtvCollateral) = GenericLogic
                        .calculateUserAccountData(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          DataTypes.CalculateUserAccountDataParams({
                            userConfig: userConfig,
                            reservesCount: reservesCount,
                            user: user,
                            oracle: oracle,
                            userEModeCategory: userEModeCategory
                          })
                        );
                      require(
                        healthFactor >= HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                        Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
                      );
                      return (healthFactor, hasZeroLtvCollateral);
                    }
                    /**
                     * @notice Validates the health factor of a user and the ltv of the asset being withdrawn.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param userConfig The state of the user for the specific reserve
                     * @param asset The asset for which the ltv will be validated
                     * @param from The user from which the aTokens are being transferred
                     * @param reservesCount The number of available reserves
                     * @param oracle The price oracle
                     * @param userEModeCategory The users active efficiency mode category
                     */
                    function validateHFAndLtv(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap memory userConfig,
                      address asset,
                      address from,
                      uint256 reservesCount,
                      address oracle,
                      uint8 userEModeCategory
                    ) internal view {
                      DataTypes.ReserveData memory reserve = reservesData[asset];
                      (, bool hasZeroLtvCollateral) = validateHealthFactor(
                        reservesData,
                        reservesList,
                        eModeCategories,
                        userConfig,
                        from,
                        userEModeCategory,
                        reservesCount,
                        oracle
                      );
                      require(
                        !hasZeroLtvCollateral || reserve.configuration.getLtv() == 0,
                        Errors.LTV_VALIDATION_FAILED
                      );
                    }
                    /**
                     * @notice Validates a transfer action.
                     * @param reserve The reserve object
                     */
                    function validateTransfer(DataTypes.ReserveData storage reserve) internal view {
                      require(!reserve.configuration.getPaused(), Errors.RESERVE_PAUSED);
                    }
                    /**
                     * @notice Validates a drop reserve action.
                     * @param reservesList The addresses of all the active reserves
                     * @param reserve The reserve object
                     * @param asset The address of the reserve's underlying asset
                     */
                    function validateDropReserve(
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.ReserveData storage reserve,
                      address asset
                    ) internal view {
                      require(asset != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                      require(reserve.id != 0 || reservesList[0] == asset, Errors.ASSET_NOT_LISTED);
                      require(
                        IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0,
                        Errors.VARIABLE_DEBT_SUPPLY_NOT_ZERO
                      );
                      require(
                        IERC20(reserve.aTokenAddress).totalSupply() == 0 && reserve.accruedToTreasury == 0,
                        Errors.UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO
                      );
                    }
                    /**
                     * @notice Validates the action of setting efficiency mode.
                     * @param eModeCategories a mapping storing configurations for all efficiency mode categories
                     * @param userConfig the user configuration
                     * @param reservesCount The total number of valid reserves
                     * @param categoryId The id of the category
                     */
                    function validateSetUserEMode(
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap memory userConfig,
                      uint256 reservesCount,
                      uint8 categoryId
                    ) internal view {
                      DataTypes.EModeCategory storage eModeCategory = eModeCategories[categoryId];
                      // category is invalid if the liq threshold is not set
                      require(
                        categoryId == 0 || eModeCategory.liquidationThreshold != 0,
                        Errors.INCONSISTENT_EMODE_CATEGORY
                      );
                      // eMode can always be enabled if the user hasn't supplied anything
                      if (userConfig.isEmpty()) {
                        return;
                      }
                      // if user is trying to set another category than default we require that
                      // either the user is not borrowing, or it's borrowing assets of categoryId
                      if (categoryId != 0) {
                        unchecked {
                          for (uint256 i = 0; i < reservesCount; i++) {
                            if (userConfig.isBorrowing(i)) {
                              require(
                                EModeConfiguration.isReserveEnabledOnBitmap(eModeCategory.borrowableBitmap, i),
                                Errors.NOT_BORROWABLE_IN_EMODE
                              );
                            }
                          }
                        }
                      }
                    }
                    /**
                     * @notice Validates the action of activating the asset as collateral.
                     * @dev Only possible if the asset has non-zero LTV and the user is not in isolation mode
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param userConfig the user configuration
                     * @param reserveConfig The reserve configuration
                     * @return True if the asset can be activated as collateral, false otherwise
                     */
                    function validateUseAsCollateral(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ReserveConfigurationMap memory reserveConfig
                    ) internal view returns (bool) {
                      if (reserveConfig.getLtv() == 0) {
                        return false;
                      }
                      if (!userConfig.isUsingAsCollateralAny()) {
                        return true;
                      }
                      (bool isolationModeActive, , ) = userConfig.getIsolationModeState(reservesData, reservesList);
                      return (!isolationModeActive && reserveConfig.getDebtCeiling() == 0);
                    }
                    /**
                     * @notice Validates if an asset should be automatically activated as collateral in the following actions: supply,
                     * transfer, mint unbacked, and liquidate
                     * @dev This is used to ensure that isolated assets are not enabled as collateral automatically
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param userConfig the user configuration
                     * @param reserveConfig The reserve configuration
                     * @return True if the asset can be activated as collateral, false otherwise
                     */
                    function validateAutomaticUseAsCollateral(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ReserveConfigurationMap memory reserveConfig,
                      address aTokenAddress
                    ) internal view returns (bool) {
                      if (reserveConfig.getDebtCeiling() != 0) {
                        // ensures only the ISOLATED_COLLATERAL_SUPPLIER_ROLE can enable collateral as side-effect of an action
                        IPoolAddressesProvider addressesProvider = IncentivizedERC20(aTokenAddress)
                          .POOL()
                          .ADDRESSES_PROVIDER();
                        if (
                          !IAccessControl(addressesProvider.getACLManager()).hasRole(
                            ISOLATED_COLLATERAL_SUPPLIER_ROLE,
                            msg.sender
                          )
                        ) return false;
                      }
                      return validateUseAsCollateral(reservesData, reservesList, userConfig, reserveConfig);
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
                  import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                  import {EModeConfiguration} from '../configuration/EModeConfiguration.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  import {EModeLogic} from './EModeLogic.sol';
                  /**
                   * @title GenericLogic library
                   * @author Aave
                   * @notice Implements protocol-level logic to calculate and validate the state of a user
                   */
                  library GenericLogic {
                    using ReserveLogic for DataTypes.ReserveData;
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    struct CalculateUserAccountDataVars {
                      uint256 assetPrice;
                      uint256 assetUnit;
                      uint256 userBalanceInBaseCurrency;
                      uint256 decimals;
                      uint256 ltv;
                      uint256 liquidationThreshold;
                      uint256 i;
                      uint256 healthFactor;
                      uint256 totalCollateralInBaseCurrency;
                      uint256 totalDebtInBaseCurrency;
                      uint256 avgLtv;
                      uint256 avgLiquidationThreshold;
                      uint256 eModeLtv;
                      uint256 eModeLiqThreshold;
                      address currentReserveAddress;
                      bool hasZeroLtvCollateral;
                      bool isInEModeCategory;
                    }
                    /**
                     * @notice Calculates the user data across the reserves.
                     * @dev It includes the total liquidity/collateral/borrow balances in the base currency used by the price feed,
                     * the average Loan To Value, the average Liquidation Ratio, and the Health factor.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param params Additional parameters needed for the calculation
                     * @return The total collateral of the user in the base currency used by the price feed
                     * @return The total debt of the user in the base currency used by the price feed
                     * @return The average ltv of the user
                     * @return The average liquidation threshold of the user
                     * @return The health factor of the user
                     * @return True if the ltv is zero, false otherwise
                     */
                    function calculateUserAccountData(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.CalculateUserAccountDataParams memory params
                    ) internal view returns (uint256, uint256, uint256, uint256, uint256, bool) {
                      if (params.userConfig.isEmpty()) {
                        return (0, 0, 0, 0, type(uint256).max, false);
                      }
                      CalculateUserAccountDataVars memory vars;
                      if (params.userEModeCategory != 0) {
                        vars.eModeLtv = eModeCategories[params.userEModeCategory].ltv;
                        vars.eModeLiqThreshold = eModeCategories[params.userEModeCategory].liquidationThreshold;
                      }
                      while (vars.i < params.reservesCount) {
                        if (!params.userConfig.isUsingAsCollateralOrBorrowing(vars.i)) {
                          unchecked {
                            ++vars.i;
                          }
                          continue;
                        }
                        vars.currentReserveAddress = reservesList[vars.i];
                        if (vars.currentReserveAddress == address(0)) {
                          unchecked {
                            ++vars.i;
                          }
                          continue;
                        }
                        DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress];
                        (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve
                          .configuration
                          .getParams();
                        unchecked {
                          vars.assetUnit = 10 ** vars.decimals;
                        }
                        vars.assetPrice = IPriceOracleGetter(params.oracle).getAssetPrice(vars.currentReserveAddress);
                        if (vars.liquidationThreshold != 0 && params.userConfig.isUsingAsCollateral(vars.i)) {
                          vars.userBalanceInBaseCurrency = _getUserBalanceInBaseCurrency(
                            params.user,
                            currentReserve,
                            vars.assetPrice,
                            vars.assetUnit
                          );
                          vars.totalCollateralInBaseCurrency += vars.userBalanceInBaseCurrency;
                          vars.isInEModeCategory =
                            params.userEModeCategory != 0 &&
                            EModeConfiguration.isReserveEnabledOnBitmap(
                              eModeCategories[params.userEModeCategory].collateralBitmap,
                              vars.i
                            );
                          if (vars.ltv != 0) {
                            vars.avgLtv +=
                              vars.userBalanceInBaseCurrency *
                              (vars.isInEModeCategory ? vars.eModeLtv : vars.ltv);
                          } else {
                            vars.hasZeroLtvCollateral = true;
                          }
                          vars.avgLiquidationThreshold +=
                            vars.userBalanceInBaseCurrency *
                            (vars.isInEModeCategory ? vars.eModeLiqThreshold : vars.liquidationThreshold);
                        }
                        if (params.userConfig.isBorrowing(vars.i)) {
                          if (currentReserve.configuration.getIsVirtualAccActive()) {
                            vars.totalDebtInBaseCurrency += _getUserDebtInBaseCurrency(
                              params.user,
                              currentReserve,
                              vars.assetPrice,
                              vars.assetUnit
                            );
                          } else {
                            // custom case for GHO, which applies the GHO discount on balanceOf
                            vars.totalDebtInBaseCurrency +=
                              (IERC20(currentReserve.variableDebtTokenAddress).balanceOf(params.user) *
                                vars.assetPrice) /
                              vars.assetUnit;
                          }
                        }
                        unchecked {
                          ++vars.i;
                        }
                      }
                      unchecked {
                        vars.avgLtv = vars.totalCollateralInBaseCurrency != 0
                          ? vars.avgLtv / vars.totalCollateralInBaseCurrency
                          : 0;
                        vars.avgLiquidationThreshold = vars.totalCollateralInBaseCurrency != 0
                          ? vars.avgLiquidationThreshold / vars.totalCollateralInBaseCurrency
                          : 0;
                      }
                      vars.healthFactor = (vars.totalDebtInBaseCurrency == 0)
                        ? type(uint256).max
                        : (vars.totalCollateralInBaseCurrency.percentMul(vars.avgLiquidationThreshold)).wadDiv(
                          vars.totalDebtInBaseCurrency
                        );
                      return (
                        vars.totalCollateralInBaseCurrency,
                        vars.totalDebtInBaseCurrency,
                        vars.avgLtv,
                        vars.avgLiquidationThreshold,
                        vars.healthFactor,
                        vars.hasZeroLtvCollateral
                      );
                    }
                    /**
                     * @notice Calculates the maximum amount that can be borrowed depending on the available collateral, the total debt
                     * and the average Loan To Value
                     * @param totalCollateralInBaseCurrency The total collateral in the base currency used by the price feed
                     * @param totalDebtInBaseCurrency The total borrow balance in the base currency used by the price feed
                     * @param ltv The average loan to value
                     * @return The amount available to borrow in the base currency of the used by the price feed
                     */
                    function calculateAvailableBorrows(
                      uint256 totalCollateralInBaseCurrency,
                      uint256 totalDebtInBaseCurrency,
                      uint256 ltv
                    ) internal pure returns (uint256) {
                      uint256 availableBorrowsInBaseCurrency = totalCollateralInBaseCurrency.percentMul(ltv);
                      if (availableBorrowsInBaseCurrency <= totalDebtInBaseCurrency) {
                        return 0;
                      }
                      availableBorrowsInBaseCurrency = availableBorrowsInBaseCurrency - totalDebtInBaseCurrency;
                      return availableBorrowsInBaseCurrency;
                    }
                    /**
                     * @notice Calculates total debt of the user in the based currency used to normalize the values of the assets
                     * @dev This fetches the `balanceOf` of the variable debt token for the user. For gas reasons, the
                     * variable debt balance is calculated by fetching `scaledBalancesOf` normalized debt, which is cheaper than
                     * fetching `balanceOf`
                     * @param user The address of the user
                     * @param reserve The data of the reserve for which the total debt of the user is being calculated
                     * @param assetPrice The price of the asset for which the total debt of the user is being calculated
                     * @param assetUnit The value representing one full unit of the asset (10^decimals)
                     * @return The total debt of the user normalized to the base currency
                     */
                    function _getUserDebtInBaseCurrency(
                      address user,
                      DataTypes.ReserveData storage reserve,
                      uint256 assetPrice,
                      uint256 assetUnit
                    ) private view returns (uint256) {
                      // fetching variable debt
                      uint256 userTotalDebt = IScaledBalanceToken(reserve.variableDebtTokenAddress).scaledBalanceOf(
                        user
                      );
                      if (userTotalDebt == 0) {
                        return 0;
                      }
                      userTotalDebt = userTotalDebt.rayMul(reserve.getNormalizedDebt()) * assetPrice;
                      unchecked {
                        return userTotalDebt / assetUnit;
                      }
                    }
                    /**
                     * @notice Calculates total aToken balance of the user in the based currency used by the price oracle
                     * @dev For gas reasons, the aToken balance is calculated by fetching `scaledBalancesOf` normalized debt, which
                     * is cheaper than fetching `balanceOf`
                     * @param user The address of the user
                     * @param reserve The data of the reserve for which the total aToken balance of the user is being calculated
                     * @param assetPrice The price of the asset for which the total aToken balance of the user is being calculated
                     * @param assetUnit The value representing one full unit of the asset (10^decimals)
                     * @return The total aToken balance of the user normalized to the base currency of the price oracle
                     */
                    function _getUserBalanceInBaseCurrency(
                      address user,
                      DataTypes.ReserveData storage reserve,
                      uint256 assetPrice,
                      uint256 assetUnit
                    ) private view returns (uint256) {
                      uint256 normalizedIncome = reserve.getNormalizedIncome();
                      uint256 balance = (
                        IScaledBalanceToken(reserve.aTokenAddress).scaledBalanceOf(user).rayMul(normalizedIncome)
                      ) * assetPrice;
                      unchecked {
                        return balance / assetUnit;
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                  import {IInitializableDebtToken} from './IInitializableDebtToken.sol';
                  /**
                   * @title IVariableDebtToken
                   * @author Aave
                   * @notice Defines the basic interface for a variable debt token.
                   */
                  interface IVariableDebtToken is IScaledBalanceToken, IInitializableDebtToken {
                    /**
                     * @notice Mints debt token to the `onBehalfOf` address
                     * @param user The address receiving the borrowed underlying, being the delegatee in case
                     * of credit delegate, or same as `onBehalfOf` otherwise
                     * @param onBehalfOf The address receiving the debt tokens
                     * @param amount The amount of debt being minted
                     * @param index The variable debt index of the reserve
                     * @return True if the previous balance of the user is 0, false otherwise
                     * @return The scaled total debt of the reserve
                     */
                    function mint(
                      address user,
                      address onBehalfOf,
                      uint256 amount,
                      uint256 index
                    ) external returns (bool, uint256);
                    /**
                     * @notice Burns user variable debt
                     * @dev In some instances, a burn transaction will emit a mint event
                     * if the amount to burn is less than the interest that the user accrued
                     * @param from The address from which the debt will be burned
                     * @param amount The amount getting burned
                     * @param index The variable debt index of the reserve
                     * @return The scaled total debt of the reserve
                     */
                    function burn(address from, uint256 amount, uint256 index) external returns (uint256);
                    /**
                     * @notice Returns the address of the underlying asset of this debtToken (E.g. WETH for variableDebtWETH)
                     * @return The address of the underlying asset
                     */
                    function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                  /**
                   * @title IReserveInterestRateStrategy
                   * @author BGD Labs
                   * @notice Basic interface for any rate strategy used by the Aave protocol
                   */
                  interface IReserveInterestRateStrategy {
                    /**
                     * @notice Sets interest rate data for an Aave rate strategy
                     * @param reserve The reserve to update
                     * @param rateData The abi encoded reserve interest rate data to apply to the given reserve
                     *   Abstracted this way as rate strategies can be custom
                     */
                    function setInterestRateParams(address reserve, bytes calldata rateData) external;
                    /**
                     * @notice Calculates the interest rates depending on the reserve's state and configurations
                     * @param params The parameters needed to calculate interest rates
                     * @return liquidityRate The liquidity rate expressed in ray
                     * @return variableBorrowRate The variable borrow rate expressed in ray
                     */
                    function calculateInterestRates(
                      DataTypes.CalculateInterestRatesParams memory params
                    ) external view returns (uint256, uint256);
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  import {WadRayMath} from './WadRayMath.sol';
                  /**
                   * @title MathUtils library
                   * @author Aave
                   * @notice Provides functions to perform linear and compounded interest calculations
                   */
                  library MathUtils {
                    using WadRayMath for uint256;
                    /// @dev Ignoring leap years
                    uint256 internal constant SECONDS_PER_YEAR = 365 days;
                    /**
                     * @dev Function to calculate the interest accumulated using a linear interest rate formula
                     * @param rate The interest rate, in ray
                     * @param lastUpdateTimestamp The timestamp of the last update of the interest
                     * @return The interest rate linearly accumulated during the timeDelta, in ray
                     */
                    function calculateLinearInterest(
                      uint256 rate,
                      uint40 lastUpdateTimestamp
                    ) internal view returns (uint256) {
                      //solium-disable-next-line
                      uint256 result = rate * (block.timestamp - uint256(lastUpdateTimestamp));
                      unchecked {
                        result = result / SECONDS_PER_YEAR;
                      }
                      return WadRayMath.RAY + result;
                    }
                    /**
                     * @dev Function to calculate the interest using a compounded interest rate formula
                     * To avoid expensive exponentiation, the calculation is performed using a binomial approximation:
                     *
                     *  (1+x)^n = 1+n*x+[n/2*(n-1)]*x^2+[n/6*(n-1)*(n-2)*x^3...
                     *
                     * The approximation slightly underpays liquidity providers and undercharges borrowers, with the advantage of great
                     * gas cost reductions. The whitepaper contains reference to the approximation and a table showing the margin of
                     * error per different time periods
                     *
                     * @param rate The interest rate, in ray
                     * @param lastUpdateTimestamp The timestamp of the last update of the interest
                     * @return The interest rate compounded during the timeDelta, in ray
                     */
                    function calculateCompoundedInterest(
                      uint256 rate,
                      uint40 lastUpdateTimestamp,
                      uint256 currentTimestamp
                    ) internal pure returns (uint256) {
                      //solium-disable-next-line
                      uint256 exp = currentTimestamp - uint256(lastUpdateTimestamp);
                      if (exp == 0) {
                        return WadRayMath.RAY;
                      }
                      uint256 expMinusOne;
                      uint256 expMinusTwo;
                      uint256 basePowerTwo;
                      uint256 basePowerThree;
                      unchecked {
                        expMinusOne = exp - 1;
                        expMinusTwo = exp > 2 ? exp - 2 : 0;
                        basePowerTwo = rate.rayMul(rate) / (SECONDS_PER_YEAR * SECONDS_PER_YEAR);
                        basePowerThree = basePowerTwo.rayMul(rate) / SECONDS_PER_YEAR;
                      }
                      uint256 secondTerm = exp * expMinusOne * basePowerTwo;
                      unchecked {
                        secondTerm /= 2;
                      }
                      uint256 thirdTerm = exp * expMinusOne * expMinusTwo * basePowerThree;
                      unchecked {
                        thirdTerm /= 6;
                      }
                      return WadRayMath.RAY + (rate * exp) / SECONDS_PER_YEAR + secondTerm + thirdTerm;
                    }
                    /**
                     * @dev Calculates the compounded interest between the timestamp of the last update and the current block timestamp
                     * @param rate The interest rate (in ray)
                     * @param lastUpdateTimestamp The timestamp from which the interest accumulation needs to be calculated
                     * @return The interest rate compounded between lastUpdateTimestamp and current block timestamp, in ray
                     */
                    function calculateCompoundedInterest(
                      uint256 rate,
                      uint40 lastUpdateTimestamp
                    ) internal view returns (uint256) {
                      return calculateCompoundedInterest(rate, lastUpdateTimestamp, block.timestamp);
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  /**
                   * @title PercentageMath library
                   * @author Aave
                   * @notice Provides functions to perform percentage calculations
                   * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR
                   * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                   */
                  library PercentageMath {
                    // Maximum percentage factor (100.00%)
                    uint256 internal constant PERCENTAGE_FACTOR = 1e4;
                    // Half percentage factor (50.00%)
                    uint256 internal constant HALF_PERCENTAGE_FACTOR = 0.5e4;
                    /**
                     * @notice Executes a percentage multiplication
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param value The value of which the percentage needs to be calculated
                     * @param percentage The percentage of the value to be calculated
                     * @return result value percentmul percentage
                     */
                    function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                      // to avoid overflow, value <= (type(uint256).max - HALF_PERCENTAGE_FACTOR) / percentage
                      assembly {
                        if iszero(
                          or(
                            iszero(percentage),
                            iszero(gt(value, div(sub(not(0), HALF_PERCENTAGE_FACTOR), percentage)))
                          )
                        ) {
                          revert(0, 0)
                        }
                        result := div(add(mul(value, percentage), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
                      }
                    }
                    /**
                     * @notice Executes a percentage division
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param value The value of which the percentage needs to be calculated
                     * @param percentage The percentage of the value to be calculated
                     * @return result value percentdiv percentage
                     */
                    function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                      // to avoid overflow, value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR
                      assembly {
                        if or(
                          iszero(percentage),
                          iszero(iszero(gt(value, div(sub(not(0), div(percentage, 2)), PERCENTAGE_FACTOR))))
                        ) {
                          revert(0, 0)
                        }
                        result := div(add(mul(value, PERCENTAGE_FACTOR), div(percentage, 2)), percentage)
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                  pragma solidity ^0.8.10;
                  /**
                   * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                   * checks.
                   *
                   * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                   * easily result in undesired exploitation or bugs, since developers usually
                   * assume that overflows raise errors. `SafeCast` restores this intuition by
                   * reverting the transaction when such an operation overflows.
                   *
                   * Using this library instead of the unchecked operations eliminates an entire
                   * class of bugs, so it's recommended to use it always.
                   *
                   * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                   * all math on `uint256` and `int256` and then downcasting.
                   */
                  library SafeCast {
                    /**
                     * @dev Returns the downcasted uint224 from uint256, reverting on
                     * overflow (when the input is greater than largest uint224).
                     *
                     * Counterpart to Solidity's `uint224` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 224 bits
                     */
                    function toUint224(uint256 value) internal pure returns (uint224) {
                      require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                      return uint224(value);
                    }
                    /**
                     * @dev Returns the downcasted uint128 from uint256, reverting on
                     * overflow (when the input is greater than largest uint128).
                     *
                     * Counterpart to Solidity's `uint128` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 128 bits
                     */
                    function toUint128(uint256 value) internal pure returns (uint128) {
                      require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                      return uint128(value);
                    }
                    /**
                     * @dev Returns the downcasted uint96 from uint256, reverting on
                     * overflow (when the input is greater than largest uint96).
                     *
                     * Counterpart to Solidity's `uint96` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 96 bits
                     */
                    function toUint96(uint256 value) internal pure returns (uint96) {
                      require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                      return uint96(value);
                    }
                    /**
                     * @dev Returns the downcasted uint64 from uint256, reverting on
                     * overflow (when the input is greater than largest uint64).
                     *
                     * Counterpart to Solidity's `uint64` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 64 bits
                     */
                    function toUint64(uint256 value) internal pure returns (uint64) {
                      require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                      return uint64(value);
                    }
                    /**
                     * @dev Returns the downcasted uint32 from uint256, reverting on
                     * overflow (when the input is greater than largest uint32).
                     *
                     * Counterpart to Solidity's `uint32` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 32 bits
                     */
                    function toUint32(uint256 value) internal pure returns (uint32) {
                      require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                      return uint32(value);
                    }
                    /**
                     * @dev Returns the downcasted uint16 from uint256, reverting on
                     * overflow (when the input is greater than largest uint16).
                     *
                     * Counterpart to Solidity's `uint16` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 16 bits
                     */
                    function toUint16(uint256 value) internal pure returns (uint16) {
                      require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                      return uint16(value);
                    }
                    /**
                     * @dev Returns the downcasted uint8 from uint256, reverting on
                     * overflow (when the input is greater than largest uint8).
                     *
                     * Counterpart to Solidity's `uint8` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 8 bits.
                     */
                    function toUint8(uint256 value) internal pure returns (uint8) {
                      require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                      return uint8(value);
                    }
                    /**
                     * @dev Converts a signed int256 into an unsigned uint256.
                     *
                     * Requirements:
                     *
                     * - input must be greater than or equal to 0.
                     */
                    function toUint256(int256 value) internal pure returns (uint256) {
                      require(value >= 0, 'SafeCast: value must be positive');
                      return uint256(value);
                    }
                    /**
                     * @dev Returns the downcasted int128 from int256, reverting on
                     * overflow (when the input is less than smallest int128 or
                     * greater than largest int128).
                     *
                     * Counterpart to Solidity's `int128` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 128 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt128(int256 value) internal pure returns (int128) {
                      require(
                        value >= type(int128).min && value <= type(int128).max,
                        "SafeCast: value doesn't fit in 128 bits"
                      );
                      return int128(value);
                    }
                    /**
                     * @dev Returns the downcasted int64 from int256, reverting on
                     * overflow (when the input is less than smallest int64 or
                     * greater than largest int64).
                     *
                     * Counterpart to Solidity's `int64` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 64 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt64(int256 value) internal pure returns (int64) {
                      require(
                        value >= type(int64).min && value <= type(int64).max,
                        "SafeCast: value doesn't fit in 64 bits"
                      );
                      return int64(value);
                    }
                    /**
                     * @dev Returns the downcasted int32 from int256, reverting on
                     * overflow (when the input is less than smallest int32 or
                     * greater than largest int32).
                     *
                     * Counterpart to Solidity's `int32` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 32 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt32(int256 value) internal pure returns (int32) {
                      require(
                        value >= type(int32).min && value <= type(int32).max,
                        "SafeCast: value doesn't fit in 32 bits"
                      );
                      return int32(value);
                    }
                    /**
                     * @dev Returns the downcasted int16 from int256, reverting on
                     * overflow (when the input is less than smallest int16 or
                     * greater than largest int16).
                     *
                     * Counterpart to Solidity's `int16` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 16 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt16(int256 value) internal pure returns (int16) {
                      require(
                        value >= type(int16).min && value <= type(int16).max,
                        "SafeCast: value doesn't fit in 16 bits"
                      );
                      return int16(value);
                    }
                    /**
                     * @dev Returns the downcasted int8 from int256, reverting on
                     * overflow (when the input is less than smallest int8 or
                     * greater than largest int8).
                     *
                     * Counterpart to Solidity's `int8` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 8 bits.
                     *
                     * _Available since v3.1._
                     */
                    function toInt8(int256 value) internal pure returns (int8) {
                      require(
                        value >= type(int8).min && value <= type(int8).max,
                        "SafeCast: value doesn't fit in 8 bits"
                      );
                      return int8(value);
                    }
                    /**
                     * @dev Converts an unsigned uint256 into a signed int256.
                     *
                     * Requirements:
                     *
                     * - input must be less than or equal to maxInt256.
                     */
                    function toInt256(uint256 value) internal pure returns (int256) {
                      // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                      require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                      return int256(value);
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {Errors} from '../helpers/Errors.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ReserveConfiguration} from './ReserveConfiguration.sol';
                  /**
                   * @title UserConfiguration library
                   * @author Aave
                   * @notice Implements the bitmap logic to handle the user configuration
                   */
                  library UserConfiguration {
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    uint256 internal constant BORROWING_MASK =
                      0x5555555555555555555555555555555555555555555555555555555555555555;
                    uint256 internal constant COLLATERAL_MASK =
                      0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;
                    /**
                     * @notice Sets if the user is borrowing the reserve identified by reserveIndex
                     * @param self The configuration object
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @param borrowing True if the user is borrowing the reserve, false otherwise
                     */
                    function setBorrowing(
                      DataTypes.UserConfigurationMap storage self,
                      uint256 reserveIndex,
                      bool borrowing
                    ) internal {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        uint256 bit = 1 << (reserveIndex << 1);
                        if (borrowing) {
                          self.data |= bit;
                        } else {
                          self.data &= ~bit;
                        }
                      }
                    }
                    /**
                     * @notice Sets if the user is using as collateral the reserve identified by reserveIndex
                     * @param self The configuration object
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @param usingAsCollateral True if the user is using the reserve as collateral, false otherwise
                     */
                    function setUsingAsCollateral(
                      DataTypes.UserConfigurationMap storage self,
                      uint256 reserveIndex,
                      bool usingAsCollateral
                    ) internal {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        uint256 bit = 1 << ((reserveIndex << 1) + 1);
                        if (usingAsCollateral) {
                          self.data |= bit;
                        } else {
                          self.data &= ~bit;
                        }
                      }
                    }
                    /**
                     * @notice Returns if a user has been using the reserve for borrowing or as collateral
                     * @param self The configuration object
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @return True if the user has been using a reserve for borrowing or as collateral, false otherwise
                     */
                    function isUsingAsCollateralOrBorrowing(
                      DataTypes.UserConfigurationMap memory self,
                      uint256 reserveIndex
                    ) internal pure returns (bool) {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        return (self.data >> (reserveIndex << 1)) & 3 != 0;
                      }
                    }
                    /**
                     * @notice Validate a user has been using the reserve for borrowing
                     * @param self The configuration object
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @return True if the user has been using a reserve for borrowing, false otherwise
                     */
                    function isBorrowing(
                      DataTypes.UserConfigurationMap memory self,
                      uint256 reserveIndex
                    ) internal pure returns (bool) {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        return (self.data >> (reserveIndex << 1)) & 1 != 0;
                      }
                    }
                    /**
                     * @notice Validate a user has been using the reserve as collateral
                     * @param self The configuration object
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @return True if the user has been using a reserve as collateral, false otherwise
                     */
                    function isUsingAsCollateral(
                      DataTypes.UserConfigurationMap memory self,
                      uint256 reserveIndex
                    ) internal pure returns (bool) {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        return (self.data >> ((reserveIndex << 1) + 1)) & 1 != 0;
                      }
                    }
                    /**
                     * @notice Checks if a user has been supplying only one reserve as collateral
                     * @dev this uses a simple trick - if a number is a power of two (only one bit set) then n & (n - 1) == 0
                     * @param self The configuration object
                     * @return True if the user has been supplying as collateral one reserve, false otherwise
                     */
                    function isUsingAsCollateralOne(
                      DataTypes.UserConfigurationMap memory self
                    ) internal pure returns (bool) {
                      uint256 collateralData = self.data & COLLATERAL_MASK;
                      return collateralData != 0 && (collateralData & (collateralData - 1) == 0);
                    }
                    /**
                     * @notice Checks if a user has been supplying any reserve as collateral
                     * @param self The configuration object
                     * @return True if the user has been supplying as collateral any reserve, false otherwise
                     */
                    function isUsingAsCollateralAny(
                      DataTypes.UserConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return self.data & COLLATERAL_MASK != 0;
                    }
                    /**
                     * @notice Checks if a user has been borrowing only one asset
                     * @dev this uses a simple trick - if a number is a power of two (only one bit set) then n & (n - 1) == 0
                     * @param self The configuration object
                     * @return True if the user has been supplying as collateral one reserve, false otherwise
                     */
                    function isBorrowingOne(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                      uint256 borrowingData = self.data & BORROWING_MASK;
                      return borrowingData != 0 && (borrowingData & (borrowingData - 1) == 0);
                    }
                    /**
                     * @notice Checks if a user has been borrowing from any reserve
                     * @param self The configuration object
                     * @return True if the user has been borrowing any reserve, false otherwise
                     */
                    function isBorrowingAny(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                      return self.data & BORROWING_MASK != 0;
                    }
                    /**
                     * @notice Checks if a user has not been using any reserve for borrowing or supply
                     * @param self The configuration object
                     * @return True if the user has not been borrowing or supplying any reserve, false otherwise
                     */
                    function isEmpty(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                      return self.data == 0;
                    }
                    /**
                     * @notice Returns the Isolation Mode state of the user
                     * @param self The configuration object
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @return True if the user is in isolation mode, false otherwise
                     * @return The address of the only asset used as collateral
                     * @return The debt ceiling of the reserve
                     */
                    function getIsolationModeState(
                      DataTypes.UserConfigurationMap memory self,
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList
                    ) internal view returns (bool, address, uint256) {
                      if (isUsingAsCollateralOne(self)) {
                        uint256 assetId = _getFirstAssetIdByMask(self, COLLATERAL_MASK);
                        address assetAddress = reservesList[assetId];
                        uint256 ceiling = reservesData[assetAddress].configuration.getDebtCeiling();
                        if (ceiling != 0) {
                          return (true, assetAddress, ceiling);
                        }
                      }
                      return (false, address(0), 0);
                    }
                    /**
                     * @notice Returns the siloed borrowing state for the user
                     * @param self The configuration object
                     * @param reservesData The data of all the reserves
                     * @param reservesList The reserve list
                     * @return True if the user has borrowed a siloed asset, false otherwise
                     * @return The address of the only borrowed asset
                     */
                    function getSiloedBorrowingState(
                      DataTypes.UserConfigurationMap memory self,
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList
                    ) internal view returns (bool, address) {
                      if (isBorrowingOne(self)) {
                        uint256 assetId = _getFirstAssetIdByMask(self, BORROWING_MASK);
                        address assetAddress = reservesList[assetId];
                        if (reservesData[assetAddress].configuration.getSiloedBorrowing()) {
                          return (true, assetAddress);
                        }
                      }
                      return (false, address(0));
                    }
                    /**
                     * @notice Returns the address of the first asset flagged in the bitmap given the corresponding bitmask
                     * @param self The configuration object
                     * @return The index of the first asset flagged in the bitmap once the corresponding mask is applied
                     */
                    function _getFirstAssetIdByMask(
                      DataTypes.UserConfigurationMap memory self,
                      uint256 mask
                    ) internal pure returns (uint256) {
                      unchecked {
                        uint256 bitmapData = self.data & mask;
                        uint256 firstAssetPosition = bitmapData & ~(bitmapData - 1);
                        uint256 id;
                        while ((firstAssetPosition >>= 2) != 0) {
                          id += 1;
                        }
                        return id;
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                  import {IPool} from '../../../interfaces/IPool.sol';
                  /**
                   * @title IFlashLoanReceiver
                   * @author Aave
                   * @notice Defines the basic interface of a flashloan-receiver contract.
                   * @dev Implement this interface to develop a flashloan-compatible flashLoanReceiver contract
                   */
                  interface IFlashLoanReceiver {
                    /**
                     * @notice Executes an operation after receiving the flash-borrowed assets
                     * @dev Ensure that the contract can return the debt + premium, e.g., has
                     *      enough funds to repay and has approved the Pool to pull the total amount
                     * @param assets The addresses of the flash-borrowed assets
                     * @param amounts The amounts of the flash-borrowed assets
                     * @param premiums The fee of each flash-borrowed asset
                     * @param initiator The address of the flashloan initiator
                     * @param params The byte-encoded params passed when initiating the flashloan
                     * @return True if the execution of the operation succeeds, false otherwise
                     */
                    function executeOperation(
                      address[] calldata assets,
                      uint256[] calldata amounts,
                      uint256[] calldata premiums,
                      address initiator,
                      bytes calldata params
                    ) external returns (bool);
                    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                    function POOL() external view returns (IPool);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                  import {IPool} from '../../../interfaces/IPool.sol';
                  /**
                   * @title IFlashLoanSimpleReceiver
                   * @author Aave
                   * @notice Defines the basic interface of a flashloan-receiver contract.
                   * @dev Implement this interface to develop a flashloan-compatible flashLoanReceiver contract
                   */
                  interface IFlashLoanSimpleReceiver {
                    /**
                     * @notice Executes an operation after receiving the flash-borrowed asset
                     * @dev Ensure that the contract can return the debt + premium, e.g., has
                     *      enough funds to repay and has approved the Pool to pull the total amount
                     * @param asset The address of the flash-borrowed asset
                     * @param amount The amount of the flash-borrowed asset
                     * @param premium The fee of the flash-borrowed asset
                     * @param initiator The address of the flashloan initiator
                     * @param params The byte-encoded params passed when initiating the flashloan
                     * @return True if the execution of the operation succeeds, false otherwise
                     */
                    function executeOperation(
                      address asset,
                      uint256 amount,
                      uint256 premium,
                      address initiator,
                      bytes calldata params
                    ) external returns (bool);
                    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                    function POOL() external view returns (IPool);
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  /**
                   * @title IsolationModeLogic library
                   * @author Aave
                   * @notice Implements the base logic for handling repayments for assets borrowed in isolation mode
                   */
                  library IsolationModeLogic {
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    using SafeCast for uint256;
                    // See `IPool` for descriptions
                    event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                    /**
                     * @notice updated the isolated debt whenever a position collateralized by an isolated asset is repaid
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param userConfig The user configuration mapping
                     * @param reserveCache The cached data of the reserve
                     * @param repayAmount The amount being repaid
                     */
                    function updateIsolatedDebtIfIsolated(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ReserveCache memory reserveCache,
                      uint256 repayAmount
                    ) internal {
                      (bool isolationModeActive, address isolationModeCollateralAddress, ) = userConfig
                        .getIsolationModeState(reservesData, reservesList);
                      if (isolationModeActive) {
                        updateIsolatedDebt(reservesData, reserveCache, repayAmount, isolationModeCollateralAddress);
                      }
                    }
                    /**
                     * @notice updated the isolated debt whenever a position collateralized by an isolated asset is liquidated
                     * @param reservesData The state of all the reserves
                     * @param reserveCache The cached data of the reserve
                     * @param repayAmount The amount being repaid
                     * @param isolationModeCollateralAddress The address of the isolated collateral
                     */
                    function updateIsolatedDebt(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      DataTypes.ReserveCache memory reserveCache,
                      uint256 repayAmount,
                      address isolationModeCollateralAddress
                    ) internal {
                      uint128 isolationModeTotalDebt = reservesData[isolationModeCollateralAddress]
                        .isolationModeTotalDebt;
                      uint128 isolatedDebtRepaid = (repayAmount /
                        10 **
                          (reserveCache.reserveConfiguration.getDecimals() -
                            ReserveConfiguration.DEBT_CEILING_DECIMALS)).toUint128();
                      // since the debt ceiling does not take into account the interest accrued, it might happen that amount
                      // repaid > debt in isolation mode
                      if (isolationModeTotalDebt <= isolatedDebtRepaid) {
                        reservesData[isolationModeCollateralAddress].isolationModeTotalDebt = 0;
                        emit IsolationModeTotalDebtUpdated(isolationModeCollateralAddress, 0);
                      } else {
                        uint256 nextIsolationModeTotalDebt = reservesData[isolationModeCollateralAddress]
                          .isolationModeTotalDebt = isolationModeTotalDebt - isolatedDebtRepaid;
                        emit IsolationModeTotalDebtUpdated(
                          isolationModeCollateralAddress,
                          nextIsolationModeTotalDebt
                        );
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {Errors} from '../helpers/Errors.sol';
                  import {ReserveConfiguration} from './ReserveConfiguration.sol';
                  /**
                   * @title EModeConfiguration library
                   * @author BGD Labs
                   * @notice Implements the bitmap logic to handle the eMode configuration
                   */
                  library EModeConfiguration {
                    /**
                     * @notice Sets a bit in a given bitmap that represents the reserve index range
                     * @dev The supplied bitmap is supposed to be a uint128 in which each bit represents a reserve
                     * @param bitmap The bitmap
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @param enabled True if the reserveIndex should be enabled on the bitmap, false otherwise
                     * @return The altered bitmap
                     */
                    function setReserveBitmapBit(
                      uint128 bitmap,
                      uint256 reserveIndex,
                      bool enabled
                    ) internal pure returns (uint128) {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        uint128 bit = uint128(1 << reserveIndex);
                        if (enabled) {
                          return bitmap | bit;
                        } else {
                          return bitmap & ~bit;
                        }
                      }
                    }
                    /**
                     * @notice Validates if a reserveIndex is flagged as enabled on a given bitmap
                     * @param bitmap The bitmap
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @return True if the reserveindex is flagged true
                     */
                    function isReserveEnabledOnBitmap(
                      uint128 bitmap,
                      uint256 reserveIndex
                    ) internal pure returns (bool) {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        return (bitmap >> reserveIndex) & 1 != 0;
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title IPriceOracleGetter
                   * @author Aave
                   * @notice Interface for the Aave price oracle.
                   */
                  interface IPriceOracleGetter {
                    /**
                     * @notice Returns the base currency address
                     * @dev Address 0x0 is reserved for USD as base currency.
                     * @return Returns the base currency address.
                     */
                    function BASE_CURRENCY() external view returns (address);
                    /**
                     * @notice Returns the base currency unit
                     * @dev 1 ether for ETH, 1e8 for USD.
                     * @return Returns the base currency unit.
                     */
                    function BASE_CURRENCY_UNIT() external view returns (uint256);
                    /**
                     * @notice Returns the asset price in the base currency
                     * @param asset The address of the asset
                     * @return The price of the asset
                     */
                    function getAssetPrice(address asset) external view returns (uint256);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title IScaledBalanceToken
                   * @author Aave
                   * @notice Defines the basic interface for a scaled-balance token.
                   */
                  interface IScaledBalanceToken {
                    /**
                     * @dev Emitted after the mint action
                     * @param caller The address performing the mint
                     * @param onBehalfOf The address of the user that will receive the minted tokens
                     * @param value The scaled-up amount being minted (based on user entered amount and balance increase from interest)
                     * @param balanceIncrease The increase in scaled-up balance since the last action of 'onBehalfOf'
                     * @param index The next liquidity index of the reserve
                     */
                    event Mint(
                      address indexed caller,
                      address indexed onBehalfOf,
                      uint256 value,
                      uint256 balanceIncrease,
                      uint256 index
                    );
                    /**
                     * @dev Emitted after the burn action
                     * @dev If the burn function does not involve a transfer of the underlying asset, the target defaults to zero address
                     * @param from The address from which the tokens will be burned
                     * @param target The address that will receive the underlying, if any
                     * @param value The scaled-up amount being burned (user entered amount - balance increase from interest)
                     * @param balanceIncrease The increase in scaled-up balance since the last action of 'from'
                     * @param index The next liquidity index of the reserve
                     */
                    event Burn(
                      address indexed from,
                      address indexed target,
                      uint256 value,
                      uint256 balanceIncrease,
                      uint256 index
                    );
                    /**
                     * @notice Returns the scaled balance of the user.
                     * @dev The scaled balance is the sum of all the updated stored balance divided by the reserve's liquidity index
                     * at the moment of the update
                     * @param user The user whose balance is calculated
                     * @return The scaled balance of the user
                     */
                    function scaledBalanceOf(address user) external view returns (uint256);
                    /**
                     * @notice Returns the scaled balance of the user and the scaled total supply.
                     * @param user The address of the user
                     * @return The scaled balance of the user
                     * @return The scaled total supply
                     */
                    function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
                    /**
                     * @notice Returns the scaled total supply of the scaled balance token. Represents sum(debt/index)
                     * @return The scaled total supply
                     */
                    function scaledTotalSupply() external view returns (uint256);
                    /**
                     * @notice Returns last index interest was accrued to the user's balance
                     * @param user The address of the user
                     * @return The last index interest was accrued to the user's balance, expressed in ray
                     */
                    function getPreviousIndex(address user) external view returns (uint256);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
                  import {IPool} from './IPool.sol';
                  /**
                   * @title IInitializableAToken
                   * @author Aave
                   * @notice Interface for the initialize function on AToken
                   */
                  interface IInitializableAToken {
                    /**
                     * @dev Emitted when an aToken is initialized
                     * @param underlyingAsset The address of the underlying asset
                     * @param pool The address of the associated pool
                     * @param treasury The address of the treasury
                     * @param incentivesController The address of the incentives controller for this aToken
                     * @param aTokenDecimals The decimals of the underlying
                     * @param aTokenName The name of the aToken
                     * @param aTokenSymbol The symbol of the aToken
                     * @param params A set of encoded parameters for additional initialization
                     */
                    event Initialized(
                      address indexed underlyingAsset,
                      address indexed pool,
                      address treasury,
                      address incentivesController,
                      uint8 aTokenDecimals,
                      string aTokenName,
                      string aTokenSymbol,
                      bytes params
                    );
                    /**
                     * @notice Initializes the aToken
                     * @param pool The pool contract that is initializing this contract
                     * @param treasury The address of the Aave treasury, receiving the fees on this aToken
                     * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
                     * @param incentivesController The smart contract managing potential incentives distribution
                     * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's
                     * @param aTokenName The name of the aToken
                     * @param aTokenSymbol The symbol of the aToken
                     * @param params A set of encoded parameters for additional initialization
                     */
                    function initialize(
                      IPool pool,
                      address treasury,
                      address underlyingAsset,
                      IAaveIncentivesController incentivesController,
                      uint8 aTokenDecimals,
                      string calldata aTokenName,
                      string calldata aTokenSymbol,
                      bytes calldata params
                    ) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                  /**
                   * @title IPriceOracleSentinel
                   * @author Aave
                   * @notice Defines the basic interface for the PriceOracleSentinel
                   */
                  interface IPriceOracleSentinel {
                    /**
                     * @dev Emitted after the sequencer oracle is updated
                     * @param newSequencerOracle The new sequencer oracle
                     */
                    event SequencerOracleUpdated(address newSequencerOracle);
                    /**
                     * @dev Emitted after the grace period is updated
                     * @param newGracePeriod The new grace period value
                     */
                    event GracePeriodUpdated(uint256 newGracePeriod);
                    /**
                     * @notice Returns the PoolAddressesProvider
                     * @return The address of the PoolAddressesProvider contract
                     */
                    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                    /**
                     * @notice Returns true if the `borrow` operation is allowed.
                     * @dev Operation not allowed when PriceOracle is down or grace period not passed.
                     * @return True if the `borrow` operation is allowed, false otherwise.
                     */
                    function isBorrowAllowed() external view returns (bool);
                    /**
                     * @notice Returns true if the `liquidation` operation is allowed.
                     * @dev Operation not allowed when PriceOracle is down or grace period not passed.
                     * @return True if the `liquidation` operation is allowed, false otherwise.
                     */
                    function isLiquidationAllowed() external view returns (bool);
                    /**
                     * @notice Updates the address of the sequencer oracle
                     * @param newSequencerOracle The address of the new Sequencer Oracle to use
                     */
                    function setSequencerOracle(address newSequencerOracle) external;
                    /**
                     * @notice Updates the duration of the grace period
                     * @param newGracePeriod The value of the new grace period duration
                     */
                    function setGracePeriod(uint256 newGracePeriod) external;
                    /**
                     * @notice Returns the SequencerOracle
                     * @return The address of the sequencer oracle contract
                     */
                    function getSequencerOracle() external view returns (address);
                    /**
                     * @notice Returns the grace period
                     * @return The duration of the grace period
                     */
                    function getGracePeriod() external view returns (uint256);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.10;
                  /**
                   * @dev External interface of AccessControl declared to support ERC165 detection.
                   */
                  interface IAccessControl {
                    /**
                     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                     *
                     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                     * {RoleAdminChanged} not being emitted signaling this.
                     *
                     * _Available since v3.1._
                     */
                    event RoleAdminChanged(
                      bytes32 indexed role,
                      bytes32 indexed previousAdminRole,
                      bytes32 indexed newAdminRole
                    );
                    /**
                     * @dev Emitted when `account` is granted `role`.
                     *
                     * `sender` is the account that originated the contract call, an admin role
                     * bearer except when using {AccessControl-_setupRole}.
                     */
                    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                    /**
                     * @dev Emitted when `account` is revoked `role`.
                     *
                     * `sender` is the account that originated the contract call:
                     *   - if using `revokeRole`, it is the admin role bearer
                     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                     */
                    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                    /**
                     * @dev Returns `true` if `account` has been granted `role`.
                     */
                    function hasRole(bytes32 role, address account) external view returns (bool);
                    /**
                     * @dev Returns the admin role that controls `role`. See {grantRole} and
                     * {revokeRole}.
                     *
                     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                     */
                    function getRoleAdmin(bytes32 role) external view returns (bytes32);
                    /**
                     * @dev Grants `role` to `account`.
                     *
                     * If `account` had not been already granted `role`, emits a {RoleGranted}
                     * event.
                     *
                     * Requirements:
                     *
                     * - the caller must have ``role``'s admin role.
                     */
                    function grantRole(bytes32 role, address account) external;
                    /**
                     * @dev Revokes `role` from `account`.
                     *
                     * If `account` had been granted `role`, emits a {RoleRevoked} event.
                     *
                     * Requirements:
                     *
                     * - the caller must have ``role``'s admin role.
                     */
                    function revokeRole(bytes32 role, address account) external;
                    /**
                     * @dev Revokes `role` from the calling account.
                     *
                     * Roles are often managed via {grantRole} and {revokeRole}: this function's
                     * purpose is to provide a mechanism for accounts to lose their privileges
                     * if they are compromised (such as when a trusted device is misplaced).
                     *
                     * If the calling account had been granted `role`, emits a {RoleRevoked}
                     * event.
                     *
                     * Requirements:
                     *
                     * - the caller must be `account`.
                     */
                    function renounceRole(bytes32 role, address account) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.10;
                  import {Context} from '../../../dependencies/openzeppelin/contracts/Context.sol';
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {IERC20Detailed} from '../../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                  import {Errors} from '../../libraries/helpers/Errors.sol';
                  import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
                  import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                  import {IPool} from '../../../interfaces/IPool.sol';
                  import {IACLManager} from '../../../interfaces/IACLManager.sol';
                  /**
                   * @title IncentivizedERC20
                   * @author Aave, inspired by the Openzeppelin ERC20 implementation
                   * @notice Basic ERC20 implementation
                   */
                  abstract contract IncentivizedERC20 is Context, IERC20Detailed {
                    using WadRayMath for uint256;
                    using SafeCast for uint256;
                    /**
                     * @dev Only pool admin can call functions marked by this modifier.
                     */
                    modifier onlyPoolAdmin() {
                      IACLManager aclManager = IACLManager(_addressesProvider.getACLManager());
                      require(aclManager.isPoolAdmin(msg.sender), Errors.CALLER_NOT_POOL_ADMIN);
                      _;
                    }
                    /**
                     * @dev Only pool can call functions marked by this modifier.
                     */
                    modifier onlyPool() {
                      require(_msgSender() == address(POOL), Errors.CALLER_MUST_BE_POOL);
                      _;
                    }
                    /**
                     * @dev UserState - additionalData is a flexible field.
                     * ATokens and VariableDebtTokens use this field store the index of the
                     * user's last supply/withdrawal/borrow/repayment.
                     */
                    struct UserState {
                      uint128 balance;
                      uint128 additionalData;
                    }
                    // Map of users address and their state data (userAddress => userStateData)
                    mapping(address => UserState) internal _userState;
                    // Map of allowances (delegator => delegatee => allowanceAmount)
                    mapping(address => mapping(address => uint256)) private _allowances;
                    uint256 internal _totalSupply;
                    string private _name;
                    string private _symbol;
                    uint8 private _decimals;
                    IAaveIncentivesController internal _incentivesController;
                    IPoolAddressesProvider internal immutable _addressesProvider;
                    IPool public immutable POOL;
                    /**
                     * @dev Constructor.
                     * @param pool The reference to the main Pool contract
                     * @param name_ The name of the token
                     * @param symbol_ The symbol of the token
                     * @param decimals_ The number of decimals of the token
                     */
                    constructor(IPool pool, string memory name_, string memory symbol_, uint8 decimals_) {
                      _addressesProvider = pool.ADDRESSES_PROVIDER();
                      _name = name_;
                      _symbol = symbol_;
                      _decimals = decimals_;
                      POOL = pool;
                    }
                    /// @inheritdoc IERC20Detailed
                    function name() public view override returns (string memory) {
                      return _name;
                    }
                    /// @inheritdoc IERC20Detailed
                    function symbol() external view override returns (string memory) {
                      return _symbol;
                    }
                    /// @inheritdoc IERC20Detailed
                    function decimals() external view override returns (uint8) {
                      return _decimals;
                    }
                    /// @inheritdoc IERC20
                    function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                    }
                    /// @inheritdoc IERC20
                    function balanceOf(address account) public view virtual override returns (uint256) {
                      return _userState[account].balance;
                    }
                    /**
                     * @notice Returns the address of the Incentives Controller contract
                     * @return The address of the Incentives Controller
                     */
                    function getIncentivesController() external view virtual returns (IAaveIncentivesController) {
                      return _incentivesController;
                    }
                    /**
                     * @notice Sets a new Incentives Controller
                     * @param controller the new Incentives controller
                     */
                    function setIncentivesController(IAaveIncentivesController controller) external onlyPoolAdmin {
                      _incentivesController = controller;
                    }
                    /// @inheritdoc IERC20
                    function transfer(address recipient, uint256 amount) external virtual override returns (bool) {
                      uint128 castAmount = amount.toUint128();
                      _transfer(_msgSender(), recipient, castAmount);
                      return true;
                    }
                    /// @inheritdoc IERC20
                    function allowance(
                      address owner,
                      address spender
                    ) external view virtual override returns (uint256) {
                      return _allowances[owner][spender];
                    }
                    /// @inheritdoc IERC20
                    function approve(address spender, uint256 amount) external virtual override returns (bool) {
                      _approve(_msgSender(), spender, amount);
                      return true;
                    }
                    /// @inheritdoc IERC20
                    function transferFrom(
                      address sender,
                      address recipient,
                      uint256 amount
                    ) external virtual override returns (bool) {
                      uint128 castAmount = amount.toUint128();
                      _approve(sender, _msgSender(), _allowances[sender][_msgSender()] - castAmount);
                      _transfer(sender, recipient, castAmount);
                      return true;
                    }
                    /**
                     * @notice Increases the allowance of spender to spend _msgSender() tokens
                     * @param spender The user allowed to spend on behalf of _msgSender()
                     * @param addedValue The amount being added to the allowance
                     * @return `true`
                     */
                    function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool) {
                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
                      return true;
                    }
                    /**
                     * @notice Decreases the allowance of spender to spend _msgSender() tokens
                     * @param spender The user allowed to spend on behalf of _msgSender()
                     * @param subtractedValue The amount being subtracted to the allowance
                     * @return `true`
                     */
                    function decreaseAllowance(
                      address spender,
                      uint256 subtractedValue
                    ) external virtual returns (bool) {
                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue);
                      return true;
                    }
                    /**
                     * @notice Transfers tokens between two users and apply incentives if defined.
                     * @param sender The source address
                     * @param recipient The destination address
                     * @param amount The amount getting transferred
                     */
                    function _transfer(address sender, address recipient, uint128 amount) internal virtual {
                      uint128 oldSenderBalance = _userState[sender].balance;
                      _userState[sender].balance = oldSenderBalance - amount;
                      uint128 oldRecipientBalance = _userState[recipient].balance;
                      _userState[recipient].balance = oldRecipientBalance + amount;
                      IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                      if (address(incentivesControllerLocal) != address(0)) {
                        uint256 currentTotalSupply = _totalSupply;
                        incentivesControllerLocal.handleAction(sender, currentTotalSupply, oldSenderBalance);
                        if (sender != recipient) {
                          incentivesControllerLocal.handleAction(recipient, currentTotalSupply, oldRecipientBalance);
                        }
                      }
                    }
                    /**
                     * @notice Approve `spender` to use `amount` of `owner`s balance
                     * @param owner The address owning the tokens
                     * @param spender The address approved for spending
                     * @param amount The amount of tokens to approve spending of
                     */
                    function _approve(address owner, address spender, uint256 amount) internal virtual {
                      _allowances[owner][spender] = amount;
                      emit Approval(owner, spender, amount);
                    }
                    /**
                     * @notice Update the name of the token
                     * @param newName The new name for the token
                     */
                    function _setName(string memory newName) internal {
                      _name = newName;
                    }
                    /**
                     * @notice Update the symbol for the token
                     * @param newSymbol The new symbol for the token
                     */
                    function _setSymbol(string memory newSymbol) internal {
                      _symbol = newSymbol;
                    }
                    /**
                     * @notice Update the number of decimals for the token
                     * @param newDecimals The new number of decimals for the token
                     */
                    function _setDecimals(uint8 newDecimals) internal {
                      _decimals = newDecimals;
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
                  import {IPool} from './IPool.sol';
                  /**
                   * @title IInitializableDebtToken
                   * @author Aave
                   * @notice Interface for the initialize function common between debt tokens
                   */
                  interface IInitializableDebtToken {
                    /**
                     * @dev Emitted when a debt token is initialized
                     * @param underlyingAsset The address of the underlying asset
                     * @param pool The address of the associated pool
                     * @param incentivesController The address of the incentives controller for this aToken
                     * @param debtTokenDecimals The decimals of the debt token
                     * @param debtTokenName The name of the debt token
                     * @param debtTokenSymbol The symbol of the debt token
                     * @param params A set of encoded parameters for additional initialization
                     */
                    event Initialized(
                      address indexed underlyingAsset,
                      address indexed pool,
                      address incentivesController,
                      uint8 debtTokenDecimals,
                      string debtTokenName,
                      string debtTokenSymbol,
                      bytes params
                    );
                    /**
                     * @notice Initializes the debt token.
                     * @param pool The pool contract that is initializing this contract
                     * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
                     * @param incentivesController The smart contract managing potential incentives distribution
                     * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's
                     * @param debtTokenName The name of the token
                     * @param debtTokenSymbol The symbol of the token
                     * @param params A set of encoded parameters for additional initialization
                     */
                    function initialize(
                      IPool pool,
                      address underlyingAsset,
                      IAaveIncentivesController incentivesController,
                      uint8 debtTokenDecimals,
                      string memory debtTokenName,
                      string memory debtTokenSymbol,
                      bytes calldata params
                    ) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title IAaveIncentivesController
                   * @author Aave
                   * @notice Defines the basic interface for an Aave Incentives Controller.
                   * @dev It only contains one single function, needed as a hook on aToken and debtToken transfers.
                   */
                  interface IAaveIncentivesController {
                    /**
                     * @dev Called by the corresponding asset on transfer hook in order to update the rewards distribution.
                     * @dev The units of `totalSupply` and `userBalance` should be the same.
                     * @param user The address of the user whose asset balance has changed
                     * @param totalSupply The total supply of the asset prior to user balance change
                     * @param userBalance The previous user balance prior to balance change
                     */
                    function handleAction(address user, uint256 totalSupply, uint256 userBalance) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.10;
                  /*
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with GSN meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                    function _msgSender() internal view virtual returns (address payable) {
                      return payable(msg.sender);
                    }
                    function _msgData() internal view virtual returns (bytes memory) {
                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                      return msg.data;
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.10;
                  import {IERC20} from './IERC20.sol';
                  interface IERC20Detailed is IERC20 {
                    function name() external view returns (string memory);
                    function symbol() external view returns (string memory);
                    function decimals() external view returns (uint8);
                  }
                  

                  File 8 of 10: SupplyLogic
                  // SPDX-License-Identifier: LGPL-3.0-or-later
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../openzeppelin/contracts/IERC20.sol';
                  /// @title Gnosis Protocol v2 Safe ERC20 Transfer Library
                  /// @author Gnosis Developers
                  /// @dev Gas-efficient version of Openzeppelin's SafeERC20 contract.
                  library GPv2SafeERC20 {
                    /// @dev Wrapper around a call to the ERC20 function `transfer` that reverts
                    /// also when the token returns `false`.
                    function safeTransfer(IERC20 token, address to, uint256 value) internal {
                      bytes4 selector_ = token.transfer.selector;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        let freeMemoryPointer := mload(0x40)
                        mstore(freeMemoryPointer, selector_)
                        mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                        mstore(add(freeMemoryPointer, 36), value)
                        if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)) {
                          returndatacopy(0, 0, returndatasize())
                          revert(0, returndatasize())
                        }
                      }
                      require(getLastTransferResult(token), 'GPv2: failed transfer');
                    }
                    /// @dev Wrapper around a call to the ERC20 function `transferFrom` that
                    /// reverts also when the token returns `false`.
                    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                      bytes4 selector_ = token.transferFrom.selector;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        let freeMemoryPointer := mload(0x40)
                        mstore(freeMemoryPointer, selector_)
                        mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
                        mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                        mstore(add(freeMemoryPointer, 68), value)
                        if iszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)) {
                          returndatacopy(0, 0, returndatasize())
                          revert(0, returndatasize())
                        }
                      }
                      require(getLastTransferResult(token), 'GPv2: failed transferFrom');
                    }
                    /// @dev Verifies that the last return was a successful `transfer*` call.
                    /// This is done by checking that the return data is either empty, or
                    /// is a valid ABI encoded boolean.
                    function getLastTransferResult(IERC20 token) private view returns (bool success) {
                      // NOTE: Inspecting previous return data requires assembly. Note that
                      // we write the return data to memory 0 in the case where the return
                      // data size is 32, this is OK since the first 64 bytes of memory are
                      // reserved by Solidy as a scratch space that can be used within
                      // assembly blocks.
                      // <https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html>
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        /// @dev Revert with an ABI encoded Solidity error with a message
                        /// that fits into 32-bytes.
                        ///
                        /// An ABI encoded Solidity error has the following memory layout:
                        ///
                        /// ------------+----------------------------------
                        ///  byte range | value
                        /// ------------+----------------------------------
                        ///  0x00..0x04 |        selector("Error(string)")
                        ///  0x04..0x24 |      string offset (always 0x20)
                        ///  0x24..0x44 |                    string length
                        ///  0x44..0x64 | string value, padded to 32-bytes
                        function revertWithMessage(length, message) {
                          mstore(0x00, '\\x08\\xc3\\x79\\xa0')
                          mstore(0x04, 0x20)
                          mstore(0x24, length)
                          mstore(0x44, message)
                          revert(0x00, 0x64)
                        }
                        switch returndatasize()
                        // Non-standard ERC20 transfer without return.
                        case 0 {
                          // NOTE: When the return data size is 0, verify that there
                          // is code at the address. This is done in order to maintain
                          // compatibility with Solidity calling conventions.
                          // <https://docs.soliditylang.org/en/v0.7.6/control-structures.html#external-function-calls>
                          if iszero(extcodesize(token)) {
                            revertWithMessage(20, 'GPv2: not a contract')
                          }
                          success := 1
                        }
                        // Standard ERC20 transfer returning boolean success value.
                        case 32 {
                          returndatacopy(0, 0, returndatasize())
                          // NOTE: For ABI encoding v1, any non-zero value is accepted
                          // as `true` for a boolean. In order to stay compatible with
                          // OpenZeppelin's `SafeERC20` library which is known to work
                          // with the existing ERC20 implementation we care about,
                          // make sure we return success for any non-zero return value
                          // from the `transfer*` call.
                          success := iszero(iszero(mload(0)))
                        }
                        default {
                          revertWithMessage(31, 'GPv2: malformed transfer result')
                        }
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize, which returns 0 for contracts in
                      // construction, since the code is only stored at the end of the
                      // constructor execution.
                      uint256 size;
                      assembly {
                        size := extcodesize(account)
                      }
                      return size > 0;
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, 'Address: insufficient balance');
                      (bool success, ) = recipient.call{value: amount}('');
                      require(success, 'Address: unable to send value, recipient may have reverted');
                    }
                    /**
                     * @dev Performs a Solidity function call using a low level `call`. A
                     * plain `call` is an unsafe replacement for a function call: use this
                     * function instead.
                     *
                     * If `target` reverts with a revert reason, it is bubbled up by this
                     * function (like regular Solidity function calls).
                     *
                     * Returns the raw returned data. To convert to the expected return value,
                     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                     *
                     * Requirements:
                     *
                     * - `target` must be a contract.
                     * - calling `target` with `data` must not revert.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCall(target, data, 'Address: low-level call failed');
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                     * `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                    ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but also transferring `value` wei to `target`.
                     *
                     * Requirements:
                     *
                     * - the calling contract must have an ETH balance of at least `value`.
                     * - the called Solidity function must be `payable`.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value
                    ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, 'Address: low-level call with value failed');
                    }
                    /**
                     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                     * with `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value,
                      string memory errorMessage
                    ) internal returns (bytes memory) {
                      require(address(this).balance >= value, 'Address: insufficient balance for call');
                      require(isContract(target), 'Address: call to non-contract');
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(
                      address target,
                      bytes memory data
                    ) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, 'Address: low-level static call failed');
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                    ) internal view returns (bytes memory) {
                      require(isContract(target), 'Address: static call to non-contract');
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, 'Address: low-level delegate call failed');
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                    ) internal returns (bytes memory) {
                      require(isContract(target), 'Address: delegate call to non-contract');
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                     * revert reason using the provided one.
                     *
                     * _Available since v4.3._
                     */
                    function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                    ) internal pure returns (bytes memory) {
                      if (success) {
                        return returndata;
                      } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                          }
                        } else {
                          revert(errorMessage);
                        }
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.10;
                  /*
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with GSN meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                    function _msgSender() internal view virtual returns (address payable) {
                      return payable(msg.sender);
                    }
                    function _msgData() internal view virtual returns (bytes memory) {
                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                      return msg.data;
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.10;
                  /**
                   * @dev External interface of AccessControl declared to support ERC165 detection.
                   */
                  interface IAccessControl {
                    /**
                     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                     *
                     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                     * {RoleAdminChanged} not being emitted signaling this.
                     *
                     * _Available since v3.1._
                     */
                    event RoleAdminChanged(
                      bytes32 indexed role,
                      bytes32 indexed previousAdminRole,
                      bytes32 indexed newAdminRole
                    );
                    /**
                     * @dev Emitted when `account` is granted `role`.
                     *
                     * `sender` is the account that originated the contract call, an admin role
                     * bearer except when using {AccessControl-_setupRole}.
                     */
                    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                    /**
                     * @dev Emitted when `account` is revoked `role`.
                     *
                     * `sender` is the account that originated the contract call:
                     *   - if using `revokeRole`, it is the admin role bearer
                     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                     */
                    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                    /**
                     * @dev Returns `true` if `account` has been granted `role`.
                     */
                    function hasRole(bytes32 role, address account) external view returns (bool);
                    /**
                     * @dev Returns the admin role that controls `role`. See {grantRole} and
                     * {revokeRole}.
                     *
                     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                     */
                    function getRoleAdmin(bytes32 role) external view returns (bytes32);
                    /**
                     * @dev Grants `role` to `account`.
                     *
                     * If `account` had not been already granted `role`, emits a {RoleGranted}
                     * event.
                     *
                     * Requirements:
                     *
                     * - the caller must have ``role``'s admin role.
                     */
                    function grantRole(bytes32 role, address account) external;
                    /**
                     * @dev Revokes `role` from `account`.
                     *
                     * If `account` had been granted `role`, emits a {RoleRevoked} event.
                     *
                     * Requirements:
                     *
                     * - the caller must have ``role``'s admin role.
                     */
                    function revokeRole(bytes32 role, address account) external;
                    /**
                     * @dev Revokes `role` from the calling account.
                     *
                     * Roles are often managed via {grantRole} and {revokeRole}: this function's
                     * purpose is to provide a mechanism for accounts to lose their privileges
                     * if they are compromised (such as when a trusted device is misplaced).
                     *
                     * If the calling account had been granted `role`, emits a {RoleRevoked}
                     * event.
                     *
                     * Requirements:
                     *
                     * - the caller must be `account`.
                     */
                    function renounceRole(bytes32 role, address account) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @dev Interface of the ERC20 standard as defined in the EIP.
                   */
                  interface IERC20 {
                    /**
                     * @dev Returns the amount of tokens in existence.
                     */
                    function totalSupply() external view returns (uint256);
                    /**
                     * @dev Returns the amount of tokens owned by `account`.
                     */
                    function balanceOf(address account) external view returns (uint256);
                    /**
                     * @dev Moves `amount` tokens from the caller's account to `recipient`.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transfer(address recipient, uint256 amount) external returns (bool);
                    /**
                     * @dev Returns the remaining number of tokens that `spender` will be
                     * allowed to spend on behalf of `owner` through {transferFrom}. This is
                     * zero by default.
                     *
                     * This value changes when {approve} or {transferFrom} are called.
                     */
                    function allowance(address owner, address spender) external view returns (uint256);
                    /**
                     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * IMPORTANT: Beware that changing an allowance with this method brings the risk
                     * that someone may use both the old and the new allowance by unfortunate
                     * transaction ordering. One possible solution to mitigate this race
                     * condition is to first reduce the spender's allowance to 0 and set the
                     * desired value afterwards:
                     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                     *
                     * Emits an {Approval} event.
                     */
                    function approve(address spender, uint256 amount) external returns (bool);
                    /**
                     * @dev Moves `amount` tokens from `sender` to `recipient` using the
                     * allowance mechanism. `amount` is then deducted from the caller's
                     * allowance.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                    /**
                     * @dev Emitted when `value` tokens are moved from one account (`from`) to
                     * another (`to`).
                     *
                     * Note that `value` may be zero.
                     */
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    /**
                     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                     * a call to {approve}. `value` is the new allowance.
                     */
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.10;
                  import {IERC20} from './IERC20.sol';
                  interface IERC20Detailed is IERC20 {
                    function name() external view returns (string memory);
                    function symbol() external view returns (string memory);
                    function decimals() external view returns (uint8);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                  pragma solidity ^0.8.10;
                  /**
                   * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                   * checks.
                   *
                   * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                   * easily result in undesired exploitation or bugs, since developers usually
                   * assume that overflows raise errors. `SafeCast` restores this intuition by
                   * reverting the transaction when such an operation overflows.
                   *
                   * Using this library instead of the unchecked operations eliminates an entire
                   * class of bugs, so it's recommended to use it always.
                   *
                   * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                   * all math on `uint256` and `int256` and then downcasting.
                   */
                  library SafeCast {
                    /**
                     * @dev Returns the downcasted uint224 from uint256, reverting on
                     * overflow (when the input is greater than largest uint224).
                     *
                     * Counterpart to Solidity's `uint224` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 224 bits
                     */
                    function toUint224(uint256 value) internal pure returns (uint224) {
                      require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                      return uint224(value);
                    }
                    /**
                     * @dev Returns the downcasted uint128 from uint256, reverting on
                     * overflow (when the input is greater than largest uint128).
                     *
                     * Counterpart to Solidity's `uint128` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 128 bits
                     */
                    function toUint128(uint256 value) internal pure returns (uint128) {
                      require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                      return uint128(value);
                    }
                    /**
                     * @dev Returns the downcasted uint96 from uint256, reverting on
                     * overflow (when the input is greater than largest uint96).
                     *
                     * Counterpart to Solidity's `uint96` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 96 bits
                     */
                    function toUint96(uint256 value) internal pure returns (uint96) {
                      require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                      return uint96(value);
                    }
                    /**
                     * @dev Returns the downcasted uint64 from uint256, reverting on
                     * overflow (when the input is greater than largest uint64).
                     *
                     * Counterpart to Solidity's `uint64` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 64 bits
                     */
                    function toUint64(uint256 value) internal pure returns (uint64) {
                      require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                      return uint64(value);
                    }
                    /**
                     * @dev Returns the downcasted uint32 from uint256, reverting on
                     * overflow (when the input is greater than largest uint32).
                     *
                     * Counterpart to Solidity's `uint32` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 32 bits
                     */
                    function toUint32(uint256 value) internal pure returns (uint32) {
                      require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                      return uint32(value);
                    }
                    /**
                     * @dev Returns the downcasted uint16 from uint256, reverting on
                     * overflow (when the input is greater than largest uint16).
                     *
                     * Counterpart to Solidity's `uint16` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 16 bits
                     */
                    function toUint16(uint256 value) internal pure returns (uint16) {
                      require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                      return uint16(value);
                    }
                    /**
                     * @dev Returns the downcasted uint8 from uint256, reverting on
                     * overflow (when the input is greater than largest uint8).
                     *
                     * Counterpart to Solidity's `uint8` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 8 bits.
                     */
                    function toUint8(uint256 value) internal pure returns (uint8) {
                      require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                      return uint8(value);
                    }
                    /**
                     * @dev Converts a signed int256 into an unsigned uint256.
                     *
                     * Requirements:
                     *
                     * - input must be greater than or equal to 0.
                     */
                    function toUint256(int256 value) internal pure returns (uint256) {
                      require(value >= 0, 'SafeCast: value must be positive');
                      return uint256(value);
                    }
                    /**
                     * @dev Returns the downcasted int128 from int256, reverting on
                     * overflow (when the input is less than smallest int128 or
                     * greater than largest int128).
                     *
                     * Counterpart to Solidity's `int128` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 128 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt128(int256 value) internal pure returns (int128) {
                      require(
                        value >= type(int128).min && value <= type(int128).max,
                        "SafeCast: value doesn't fit in 128 bits"
                      );
                      return int128(value);
                    }
                    /**
                     * @dev Returns the downcasted int64 from int256, reverting on
                     * overflow (when the input is less than smallest int64 or
                     * greater than largest int64).
                     *
                     * Counterpart to Solidity's `int64` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 64 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt64(int256 value) internal pure returns (int64) {
                      require(
                        value >= type(int64).min && value <= type(int64).max,
                        "SafeCast: value doesn't fit in 64 bits"
                      );
                      return int64(value);
                    }
                    /**
                     * @dev Returns the downcasted int32 from int256, reverting on
                     * overflow (when the input is less than smallest int32 or
                     * greater than largest int32).
                     *
                     * Counterpart to Solidity's `int32` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 32 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt32(int256 value) internal pure returns (int32) {
                      require(
                        value >= type(int32).min && value <= type(int32).max,
                        "SafeCast: value doesn't fit in 32 bits"
                      );
                      return int32(value);
                    }
                    /**
                     * @dev Returns the downcasted int16 from int256, reverting on
                     * overflow (when the input is less than smallest int16 or
                     * greater than largest int16).
                     *
                     * Counterpart to Solidity's `int16` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 16 bits
                     *
                     * _Available since v3.1._
                     */
                    function toInt16(int256 value) internal pure returns (int16) {
                      require(
                        value >= type(int16).min && value <= type(int16).max,
                        "SafeCast: value doesn't fit in 16 bits"
                      );
                      return int16(value);
                    }
                    /**
                     * @dev Returns the downcasted int8 from int256, reverting on
                     * overflow (when the input is less than smallest int8 or
                     * greater than largest int8).
                     *
                     * Counterpart to Solidity's `int8` operator.
                     *
                     * Requirements:
                     *
                     * - input must fit into 8 bits.
                     *
                     * _Available since v3.1._
                     */
                    function toInt8(int256 value) internal pure returns (int8) {
                      require(
                        value >= type(int8).min && value <= type(int8).max,
                        "SafeCast: value doesn't fit in 8 bits"
                      );
                      return int8(value);
                    }
                    /**
                     * @dev Converts an unsigned uint256 into a signed int256.
                     *
                     * Requirements:
                     *
                     * - input must be less than or equal to maxInt256.
                     */
                    function toInt256(uint256 value) internal pure returns (int256) {
                      // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                      require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                      return int256(value);
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                  /**
                   * @title IACLManager
                   * @author Aave
                   * @notice Defines the basic interface for the ACL Manager
                   */
                  interface IACLManager {
                    /**
                     * @notice Returns the contract address of the PoolAddressesProvider
                     * @return The address of the PoolAddressesProvider
                     */
                    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                    /**
                     * @notice Returns the identifier of the PoolAdmin role
                     * @return The id of the PoolAdmin role
                     */
                    function POOL_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the EmergencyAdmin role
                     * @return The id of the EmergencyAdmin role
                     */
                    function EMERGENCY_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the RiskAdmin role
                     * @return The id of the RiskAdmin role
                     */
                    function RISK_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the FlashBorrower role
                     * @return The id of the FlashBorrower role
                     */
                    function FLASH_BORROWER_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the Bridge role
                     * @return The id of the Bridge role
                     */
                    function BRIDGE_ROLE() external view returns (bytes32);
                    /**
                     * @notice Returns the identifier of the AssetListingAdmin role
                     * @return The id of the AssetListingAdmin role
                     */
                    function ASSET_LISTING_ADMIN_ROLE() external view returns (bytes32);
                    /**
                     * @notice Set the role as admin of a specific role.
                     * @dev By default the admin role for all roles is `DEFAULT_ADMIN_ROLE`.
                     * @param role The role to be managed by the admin role
                     * @param adminRole The admin role
                     */
                    function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
                    /**
                     * @notice Adds a new admin as PoolAdmin
                     * @param admin The address of the new admin
                     */
                    function addPoolAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as PoolAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removePoolAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is PoolAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is PoolAdmin, false otherwise
                     */
                    function isPoolAdmin(address admin) external view returns (bool);
                    /**
                     * @notice Adds a new admin as EmergencyAdmin
                     * @param admin The address of the new admin
                     */
                    function addEmergencyAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as EmergencyAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removeEmergencyAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is EmergencyAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is EmergencyAdmin, false otherwise
                     */
                    function isEmergencyAdmin(address admin) external view returns (bool);
                    /**
                     * @notice Adds a new admin as RiskAdmin
                     * @param admin The address of the new admin
                     */
                    function addRiskAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as RiskAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removeRiskAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is RiskAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is RiskAdmin, false otherwise
                     */
                    function isRiskAdmin(address admin) external view returns (bool);
                    /**
                     * @notice Adds a new address as FlashBorrower
                     * @param borrower The address of the new FlashBorrower
                     */
                    function addFlashBorrower(address borrower) external;
                    /**
                     * @notice Removes an address as FlashBorrower
                     * @param borrower The address of the FlashBorrower to remove
                     */
                    function removeFlashBorrower(address borrower) external;
                    /**
                     * @notice Returns true if the address is FlashBorrower, false otherwise
                     * @param borrower The address to check
                     * @return True if the given address is FlashBorrower, false otherwise
                     */
                    function isFlashBorrower(address borrower) external view returns (bool);
                    /**
                     * @notice Adds a new address as Bridge
                     * @param bridge The address of the new Bridge
                     */
                    function addBridge(address bridge) external;
                    /**
                     * @notice Removes an address as Bridge
                     * @param bridge The address of the bridge to remove
                     */
                    function removeBridge(address bridge) external;
                    /**
                     * @notice Returns true if the address is Bridge, false otherwise
                     * @param bridge The address to check
                     * @return True if the given address is Bridge, false otherwise
                     */
                    function isBridge(address bridge) external view returns (bool);
                    /**
                     * @notice Adds a new admin as AssetListingAdmin
                     * @param admin The address of the new admin
                     */
                    function addAssetListingAdmin(address admin) external;
                    /**
                     * @notice Removes an admin as AssetListingAdmin
                     * @param admin The address of the admin to remove
                     */
                    function removeAssetListingAdmin(address admin) external;
                    /**
                     * @notice Returns true if the address is AssetListingAdmin, false otherwise
                     * @param admin The address to check
                     * @return True if the given address is AssetListingAdmin, false otherwise
                     */
                    function isAssetListingAdmin(address admin) external view returns (bool);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                  import {IInitializableAToken} from './IInitializableAToken.sol';
                  /**
                   * @title IAToken
                   * @author Aave
                   * @notice Defines the basic interface for an AToken.
                   */
                  interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken {
                    /**
                     * @dev Emitted during the transfer action
                     * @param from The user whose tokens are being transferred
                     * @param to The recipient
                     * @param value The scaled amount being transferred
                     * @param index The next liquidity index of the reserve
                     */
                    event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index);
                    /**
                     * @notice Mints `amount` aTokens to `user`
                     * @param caller The address performing the mint
                     * @param onBehalfOf The address of the user that will receive the minted aTokens
                     * @param amount The amount of tokens getting minted
                     * @param index The next liquidity index of the reserve
                     * @return `true` if the the previous balance of the user was 0
                     */
                    function mint(
                      address caller,
                      address onBehalfOf,
                      uint256 amount,
                      uint256 index
                    ) external returns (bool);
                    /**
                     * @notice Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
                     * @dev In some instances, the mint event could be emitted from a burn transaction
                     * if the amount to burn is less than the interest that the user accrued
                     * @param from The address from which the aTokens will be burned
                     * @param receiverOfUnderlying The address that will receive the underlying
                     * @param amount The amount being burned
                     * @param index The next liquidity index of the reserve
                     */
                    function burn(address from, address receiverOfUnderlying, uint256 amount, uint256 index) external;
                    /**
                     * @notice Mints aTokens to the reserve treasury
                     * @param amount The amount of tokens getting minted
                     * @param index The next liquidity index of the reserve
                     */
                    function mintToTreasury(uint256 amount, uint256 index) external;
                    /**
                     * @notice Transfers aTokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken
                     * @param from The address getting liquidated, current owner of the aTokens
                     * @param to The recipient
                     * @param value The amount of tokens getting transferred
                     */
                    function transferOnLiquidation(address from, address to, uint256 value) external;
                    /**
                     * @notice Transfers the underlying asset to `target`.
                     * @dev Used by the Pool to transfer assets in borrow(), withdraw() and flashLoan()
                     * @param target The recipient of the underlying
                     * @param amount The amount getting transferred
                     */
                    function transferUnderlyingTo(address target, uint256 amount) external;
                    /**
                     * @notice Handles the underlying received by the aToken after the transfer has been completed.
                     * @dev The default implementation is empty as with standard ERC20 tokens, nothing needs to be done after the
                     * transfer is concluded. However in the future there may be aTokens that allow for example to stake the underlying
                     * to receive LM rewards. In that case, `handleRepayment()` would perform the staking of the underlying asset.
                     * @param user The user executing the repayment
                     * @param onBehalfOf The address of the user who will get his debt reduced/removed
                     * @param amount The amount getting repaid
                     */
                    function handleRepayment(address user, address onBehalfOf, uint256 amount) external;
                    /**
                     * @notice Allow passing a signed message to approve spending
                     * @dev implements the permit function as for
                     * https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
                     * @param owner The owner of the funds
                     * @param spender The spender
                     * @param value The amount
                     * @param deadline The deadline timestamp, type(uint256).max for max deadline
                     * @param v Signature param
                     * @param s Signature param
                     * @param r Signature param
                     */
                    function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                    ) external;
                    /**
                     * @notice Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH)
                     * @return The address of the underlying asset
                     */
                    function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                    /**
                     * @notice Returns the address of the Aave treasury, receiving the fees on this aToken.
                     * @return Address of the Aave treasury
                     */
                    function RESERVE_TREASURY_ADDRESS() external view returns (address);
                    /**
                     * @notice Get the domain separator for the token
                     * @dev Return cached value if chainId matches cache, otherwise recomputes separator
                     * @return The domain separator of the token at current chain
                     */
                    function DOMAIN_SEPARATOR() external view returns (bytes32);
                    /**
                     * @notice Returns the nonce for owner.
                     * @param owner The address of the owner
                     * @return The nonce of the owner
                     */
                    function nonces(address owner) external view returns (uint256);
                    /**
                     * @notice Rescue and transfer tokens locked in this contract
                     * @param token The address of the token
                     * @param to The address of the recipient
                     * @param amount The amount of token to transfer
                     */
                    function rescueTokens(address token, address to, uint256 amount) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title IAaveIncentivesController
                   * @author Aave
                   * @notice Defines the basic interface for an Aave Incentives Controller.
                   * @dev It only contains one single function, needed as a hook on aToken and debtToken transfers.
                   */
                  interface IAaveIncentivesController {
                    /**
                     * @dev Called by the corresponding asset on transfer hook in order to update the rewards distribution.
                     * @dev The units of `totalSupply` and `userBalance` should be the same.
                     * @param user The address of the user whose asset balance has changed
                     * @param totalSupply The total supply of the asset prior to user balance change
                     * @param userBalance The previous user balance prior to balance change
                     */
                    function handleAction(address user, uint256 totalSupply, uint256 userBalance) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
                  import {IPool} from './IPool.sol';
                  /**
                   * @title IInitializableAToken
                   * @author Aave
                   * @notice Interface for the initialize function on AToken
                   */
                  interface IInitializableAToken {
                    /**
                     * @dev Emitted when an aToken is initialized
                     * @param underlyingAsset The address of the underlying asset
                     * @param pool The address of the associated pool
                     * @param treasury The address of the treasury
                     * @param incentivesController The address of the incentives controller for this aToken
                     * @param aTokenDecimals The decimals of the underlying
                     * @param aTokenName The name of the aToken
                     * @param aTokenSymbol The symbol of the aToken
                     * @param params A set of encoded parameters for additional initialization
                     */
                    event Initialized(
                      address indexed underlyingAsset,
                      address indexed pool,
                      address treasury,
                      address incentivesController,
                      uint8 aTokenDecimals,
                      string aTokenName,
                      string aTokenSymbol,
                      bytes params
                    );
                    /**
                     * @notice Initializes the aToken
                     * @param pool The pool contract that is initializing this contract
                     * @param treasury The address of the Aave treasury, receiving the fees on this aToken
                     * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
                     * @param incentivesController The smart contract managing potential incentives distribution
                     * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's
                     * @param aTokenName The name of the aToken
                     * @param aTokenSymbol The symbol of the aToken
                     * @param params A set of encoded parameters for additional initialization
                     */
                    function initialize(
                      IPool pool,
                      address treasury,
                      address underlyingAsset,
                      IAaveIncentivesController incentivesController,
                      uint8 aTokenDecimals,
                      string calldata aTokenName,
                      string calldata aTokenSymbol,
                      bytes calldata params
                    ) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
                  import {IPool} from './IPool.sol';
                  /**
                   * @title IInitializableDebtToken
                   * @author Aave
                   * @notice Interface for the initialize function common between debt tokens
                   */
                  interface IInitializableDebtToken {
                    /**
                     * @dev Emitted when a debt token is initialized
                     * @param underlyingAsset The address of the underlying asset
                     * @param pool The address of the associated pool
                     * @param incentivesController The address of the incentives controller for this aToken
                     * @param debtTokenDecimals The decimals of the debt token
                     * @param debtTokenName The name of the debt token
                     * @param debtTokenSymbol The symbol of the debt token
                     * @param params A set of encoded parameters for additional initialization
                     */
                    event Initialized(
                      address indexed underlyingAsset,
                      address indexed pool,
                      address incentivesController,
                      uint8 debtTokenDecimals,
                      string debtTokenName,
                      string debtTokenSymbol,
                      bytes params
                    );
                    /**
                     * @notice Initializes the debt token.
                     * @param pool The pool contract that is initializing this contract
                     * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
                     * @param incentivesController The smart contract managing potential incentives distribution
                     * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's
                     * @param debtTokenName The name of the token
                     * @param debtTokenSymbol The symbol of the token
                     * @param params A set of encoded parameters for additional initialization
                     */
                    function initialize(
                      IPool pool,
                      address underlyingAsset,
                      IAaveIncentivesController incentivesController,
                      uint8 debtTokenDecimals,
                      string memory debtTokenName,
                      string memory debtTokenSymbol,
                      bytes calldata params
                    ) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                  import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                  /**
                   * @title IPool
                   * @author Aave
                   * @notice Defines the basic interface for an Aave Pool.
                   */
                  interface IPool {
                    /**
                     * @dev Emitted on mintUnbacked()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address initiating the supply
                     * @param onBehalfOf The beneficiary of the supplied assets, receiving the aTokens
                     * @param amount The amount of supplied assets
                     * @param referralCode The referral code used
                     */
                    event MintUnbacked(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted on backUnbacked()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param backer The address paying for the backing
                     * @param amount The amount added as backing
                     * @param fee The amount paid in fees
                     */
                    event BackUnbacked(address indexed reserve, address indexed backer, uint256 amount, uint256 fee);
                    /**
                     * @dev Emitted on supply()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address initiating the supply
                     * @param onBehalfOf The beneficiary of the supply, receiving the aTokens
                     * @param amount The amount supplied
                     * @param referralCode The referral code used
                     */
                    event Supply(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted on withdraw()
                     * @param reserve The address of the underlying asset being withdrawn
                     * @param user The address initiating the withdrawal, owner of aTokens
                     * @param to The address that will receive the underlying
                     * @param amount The amount to be withdrawn
                     */
                    event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
                    /**
                     * @dev Emitted on borrow() and flashLoan() when debt needs to be opened
                     * @param reserve The address of the underlying asset being borrowed
                     * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just
                     * initiator of the transaction on flashLoan()
                     * @param onBehalfOf The address that will be getting the debt
                     * @param amount The amount borrowed out
                     * @param interestRateMode The rate mode: 2 for Variable, 1 is deprecated (changed on v3.2.0)
                     * @param borrowRate The numeric rate at which the user has borrowed, expressed in ray
                     * @param referralCode The referral code used
                     */
                    event Borrow(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      DataTypes.InterestRateMode interestRateMode,
                      uint256 borrowRate,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted on repay()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The beneficiary of the repayment, getting his debt reduced
                     * @param repayer The address of the user initiating the repay(), providing the funds
                     * @param amount The amount repaid
                     * @param useATokens True if the repayment is done using aTokens, `false` if done with underlying asset directly
                     */
                    event Repay(
                      address indexed reserve,
                      address indexed user,
                      address indexed repayer,
                      uint256 amount,
                      bool useATokens
                    );
                    /**
                     * @dev Emitted on borrow(), repay() and liquidationCall() when using isolated assets
                     * @param asset The address of the underlying asset of the reserve
                     * @param totalDebt The total isolation mode debt for the reserve
                     */
                    event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                    /**
                     * @dev Emitted when the user selects a certain asset category for eMode
                     * @param user The address of the user
                     * @param categoryId The category id
                     */
                    event UserEModeSet(address indexed user, uint8 categoryId);
                    /**
                     * @dev Emitted on setUserUseReserveAsCollateral()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address of the user enabling the usage as collateral
                     */
                    event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                    /**
                     * @dev Emitted on setUserUseReserveAsCollateral()
                     * @param reserve The address of the underlying asset of the reserve
                     * @param user The address of the user enabling the usage as collateral
                     */
                    event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                    /**
                     * @dev Emitted on flashLoan()
                     * @param target The address of the flash loan receiver contract
                     * @param initiator The address initiating the flash loan
                     * @param asset The address of the asset being flash borrowed
                     * @param amount The amount flash borrowed
                     * @param interestRateMode The flashloan mode: 0 for regular flashloan,
                     *        1 for Stable (Deprecated on v3.2.0), 2 for Variable
                     * @param premium The fee flash borrowed
                     * @param referralCode The referral code used
                     */
                    event FlashLoan(
                      address indexed target,
                      address initiator,
                      address indexed asset,
                      uint256 amount,
                      DataTypes.InterestRateMode interestRateMode,
                      uint256 premium,
                      uint16 indexed referralCode
                    );
                    /**
                     * @dev Emitted when a borrower is liquidated.
                     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                     * @param user The address of the borrower getting liquidated
                     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                     * @param liquidatedCollateralAmount The amount of collateral received by the liquidator
                     * @param liquidator The address of the liquidator
                     * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants
                     * to receive the underlying collateral asset directly
                     */
                    event LiquidationCall(
                      address indexed collateralAsset,
                      address indexed debtAsset,
                      address indexed user,
                      uint256 debtToCover,
                      uint256 liquidatedCollateralAmount,
                      address liquidator,
                      bool receiveAToken
                    );
                    /**
                     * @dev Emitted when the state of a reserve is updated.
                     * @param reserve The address of the underlying asset of the reserve
                     * @param liquidityRate The next liquidity rate
                     * @param stableBorrowRate The next stable borrow rate @note deprecated on v3.2.0
                     * @param variableBorrowRate The next variable borrow rate
                     * @param liquidityIndex The next liquidity index
                     * @param variableBorrowIndex The next variable borrow index
                     */
                    event ReserveDataUpdated(
                      address indexed reserve,
                      uint256 liquidityRate,
                      uint256 stableBorrowRate,
                      uint256 variableBorrowRate,
                      uint256 liquidityIndex,
                      uint256 variableBorrowIndex
                    );
                    /**
                     * @dev Emitted when the deficit of a reserve is covered.
                     * @param reserve The address of the underlying asset of the reserve
                     * @param caller The caller that triggered the DeficitCovered event
                     * @param amountCovered The amount of deficit covered
                     */
                    event DeficitCovered(address indexed reserve, address caller, uint256 amountCovered);
                    /**
                     * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest.
                     * @param reserve The address of the reserve
                     * @param amountMinted The amount minted to the treasury
                     */
                    event MintedToTreasury(address indexed reserve, uint256 amountMinted);
                    /**
                     * @dev Emitted when deficit is realized on a liquidation.
                     * @param user The user address where the bad debt will be burned
                     * @param debtAsset The address of the underlying borrowed asset to be burned
                     * @param amountCreated The amount of deficit created
                     */
                    event DeficitCreated(address indexed user, address indexed debtAsset, uint256 amountCreated);
                    /**
                     * @notice Mints an `amount` of aTokens to the `onBehalfOf`
                     * @param asset The address of the underlying asset to mint
                     * @param amount The amount to mint
                     * @param onBehalfOf The address that will receive the aTokens
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function mintUnbacked(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode
                    ) external;
                    /**
                     * @notice Back the current unbacked underlying with `amount` and pay `fee`.
                     * @param asset The address of the underlying asset to back
                     * @param amount The amount to back
                     * @param fee The amount paid in fees
                     * @return The backed amount
                     */
                    function backUnbacked(address asset, uint256 amount, uint256 fee) external returns (uint256);
                    /**
                     * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                     * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                     * @param asset The address of the underlying asset to supply
                     * @param amount The amount to be supplied
                     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                     *   is a different wallet
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
                    /**
                     * @notice Supply with transfer approval of asset to be supplied done via permit function
                     * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                     * @param asset The address of the underlying asset to supply
                     * @param amount The amount to be supplied
                     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                     *   is a different wallet
                     * @param deadline The deadline timestamp that the permit is valid
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     * @param permitV The V parameter of ERC712 permit sig
                     * @param permitR The R parameter of ERC712 permit sig
                     * @param permitS The S parameter of ERC712 permit sig
                     */
                    function supplyWithPermit(
                      address asset,
                      uint256 amount,
                      address onBehalfOf,
                      uint16 referralCode,
                      uint256 deadline,
                      uint8 permitV,
                      bytes32 permitR,
                      bytes32 permitS
                    ) external;
                    /**
                     * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
                     * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
                     * @param asset The address of the underlying asset to withdraw
                     * @param amount The underlying amount to be withdrawn
                     *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
                     * @param to The address that will receive the underlying, same as msg.sender if the user
                     *   wants to receive it on his own wallet, or a different address if the beneficiary is a
                     *   different wallet
                     * @return The final amount withdrawn
                     */
                    function withdraw(address asset, uint256 amount, address to) external returns (uint256);
                    /**
                     * @notice Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
                     * already supplied enough collateral, or he was given enough allowance by a credit delegator on the VariableDebtToken
                     * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
                     *   and 100 variable debt tokens
                     * @param asset The address of the underlying asset to borrow
                     * @param amount The amount to be borrowed
                     * @param interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     * @param onBehalfOf The address of the user who will receive the debt. Should be the address of the borrower itself
                     * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
                     * if he has been given credit delegation allowance
                     */
                    function borrow(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      uint16 referralCode,
                      address onBehalfOf
                    ) external;
                    /**
                     * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
                     * - E.g. User repays 100 USDC, burning 100 variable debt tokens of the `onBehalfOf` address
                     * @param asset The address of the borrowed underlying asset previously borrowed
                     * @param amount The amount to repay
                     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                     * @param interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                     * @param onBehalfOf The address of the user who will get his debt reduced/removed. Should be the address of the
                     * user calling the function if he wants to reduce/remove his own debt, or the address of any other
                     * other borrower whose debt should be removed
                     * @return The final amount repaid
                     */
                    function repay(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      address onBehalfOf
                    ) external returns (uint256);
                    /**
                     * @notice Repay with transfer approval of asset to be repaid done via permit function
                     * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                     * @param asset The address of the borrowed underlying asset previously borrowed
                     * @param amount The amount to repay
                     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                     * @param interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                     * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
                     * user calling the function if he wants to reduce/remove his own debt, or the address of any other
                     * other borrower whose debt should be removed
                     * @param deadline The deadline timestamp that the permit is valid
                     * @param permitV The V parameter of ERC712 permit sig
                     * @param permitR The R parameter of ERC712 permit sig
                     * @param permitS The S parameter of ERC712 permit sig
                     * @return The final amount repaid
                     */
                    function repayWithPermit(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode,
                      address onBehalfOf,
                      uint256 deadline,
                      uint8 permitV,
                      bytes32 permitR,
                      bytes32 permitS
                    ) external returns (uint256);
                    /**
                     * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the
                     * equivalent debt tokens
                     * - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable debt tokens
                     * @dev  Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken
                     * balance is not enough to cover the whole debt
                     * @param asset The address of the borrowed underlying asset previously borrowed
                     * @param amount The amount to repay
                     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                     * @param interestRateMode DEPRECATED in v3.2.0
                     * @return The final amount repaid
                     */
                    function repayWithATokens(
                      address asset,
                      uint256 amount,
                      uint256 interestRateMode
                    ) external returns (uint256);
                    /**
                     * @notice Allows suppliers to enable/disable a specific supplied asset as collateral
                     * @param asset The address of the underlying asset supplied
                     * @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise
                     */
                    function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
                    /**
                     * @notice Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
                     * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
                     *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
                     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                     * @param user The address of the borrower getting liquidated
                     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                     * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants
                     * to receive the underlying collateral asset directly
                     */
                    function liquidationCall(
                      address collateralAsset,
                      address debtAsset,
                      address user,
                      uint256 debtToCover,
                      bool receiveAToken
                    ) external;
                    /**
                     * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                     * as long as the amount taken plus a fee is returned.
                     * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
                     * into consideration. For further details please visit https://docs.aave.com/developers/
                     * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanReceiver interface
                     * @param assets The addresses of the assets being flash-borrowed
                     * @param amounts The amounts of the assets being flash-borrowed
                     * @param interestRateModes Types of the debt to open if the flash loan is not returned:
                     *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
                     *   1 -> Deprecated on v3.2.0
                     *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                     * @param onBehalfOf The address  that will receive the debt in the case of using 2 on `modes`
                     * @param params Variadic packed params to pass to the receiver as extra information
                     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function flashLoan(
                      address receiverAddress,
                      address[] calldata assets,
                      uint256[] calldata amounts,
                      uint256[] calldata interestRateModes,
                      address onBehalfOf,
                      bytes calldata params,
                      uint16 referralCode
                    ) external;
                    /**
                     * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                     * as long as the amount taken plus a fee is returned.
                     * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
                     * into consideration. For further details please visit https://docs.aave.com/developers/
                     * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanSimpleReceiver interface
                     * @param asset The address of the asset being flash-borrowed
                     * @param amount The amount of the asset being flash-borrowed
                     * @param params Variadic packed params to pass to the receiver as extra information
                     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function flashLoanSimple(
                      address receiverAddress,
                      address asset,
                      uint256 amount,
                      bytes calldata params,
                      uint16 referralCode
                    ) external;
                    /**
                     * @notice Returns the user account data across all the reserves
                     * @param user The address of the user
                     * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
                     * @return totalDebtBase The total debt of the user in the base currency used by the price feed
                     * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
                     * @return currentLiquidationThreshold The liquidation threshold of the user
                     * @return ltv The loan to value of The user
                     * @return healthFactor The current health factor of the user
                     */
                    function getUserAccountData(
                      address user
                    )
                      external
                      view
                      returns (
                        uint256 totalCollateralBase,
                        uint256 totalDebtBase,
                        uint256 availableBorrowsBase,
                        uint256 currentLiquidationThreshold,
                        uint256 ltv,
                        uint256 healthFactor
                      );
                    /**
                     * @notice Initializes a reserve, activating it, assigning an aToken and debt tokens and an
                     * interest rate strategy
                     * @dev Only callable by the PoolConfigurator contract
                     * @param asset The address of the underlying asset of the reserve
                     * @param aTokenAddress The address of the aToken that will be assigned to the reserve
                     * @param variableDebtAddress The address of the VariableDebtToken that will be assigned to the reserve
                     * @param interestRateStrategyAddress The address of the interest rate strategy contract
                     */
                    function initReserve(
                      address asset,
                      address aTokenAddress,
                      address variableDebtAddress,
                      address interestRateStrategyAddress
                    ) external;
                    /**
                     * @notice Drop a reserve
                     * @dev Only callable by the PoolConfigurator contract
                     * @dev Does not reset eMode flags, which must be considered when reusing the same reserve id for a different reserve.
                     * @param asset The address of the underlying asset of the reserve
                     */
                    function dropReserve(address asset) external;
                    /**
                     * @notice Updates the address of the interest rate strategy contract
                     * @dev Only callable by the PoolConfigurator contract
                     * @param asset The address of the underlying asset of the reserve
                     * @param rateStrategyAddress The address of the interest rate strategy contract
                     */
                    function setReserveInterestRateStrategyAddress(
                      address asset,
                      address rateStrategyAddress
                    ) external;
                    /**
                     * @notice Accumulates interest to all indexes of the reserve
                     * @dev Only callable by the PoolConfigurator contract
                     * @dev To be used when required by the configurator, for example when updating interest rates strategy data
                     * @param asset The address of the underlying asset of the reserve
                     */
                    function syncIndexesState(address asset) external;
                    /**
                     * @notice Updates interest rates on the reserve data
                     * @dev Only callable by the PoolConfigurator contract
                     * @dev To be used when required by the configurator, for example when updating interest rates strategy data
                     * @param asset The address of the underlying asset of the reserve
                     */
                    function syncRatesState(address asset) external;
                    /**
                     * @notice Sets the configuration bitmap of the reserve as a whole
                     * @dev Only callable by the PoolConfigurator contract
                     * @param asset The address of the underlying asset of the reserve
                     * @param configuration The new configuration bitmap
                     */
                    function setConfiguration(
                      address asset,
                      DataTypes.ReserveConfigurationMap calldata configuration
                    ) external;
                    /**
                     * @notice Returns the configuration of the reserve
                     * @param asset The address of the underlying asset of the reserve
                     * @return The configuration of the reserve
                     */
                    function getConfiguration(
                      address asset
                    ) external view returns (DataTypes.ReserveConfigurationMap memory);
                    /**
                     * @notice Returns the configuration of the user across all the reserves
                     * @param user The user address
                     * @return The configuration of the user
                     */
                    function getUserConfiguration(
                      address user
                    ) external view returns (DataTypes.UserConfigurationMap memory);
                    /**
                     * @notice Returns the normalized income of the reserve
                     * @param asset The address of the underlying asset of the reserve
                     * @return The reserve's normalized income
                     */
                    function getReserveNormalizedIncome(address asset) external view returns (uint256);
                    /**
                     * @notice Returns the normalized variable debt per unit of asset
                     * @dev WARNING: This function is intended to be used primarily by the protocol itself to get a
                     * "dynamic" variable index based on time, current stored index and virtual rate at the current
                     * moment (approx. a borrower would get if opening a position). This means that is always used in
                     * combination with variable debt supply/balances.
                     * If using this function externally, consider that is possible to have an increasing normalized
                     * variable debt that is not equivalent to how the variable debt index would be updated in storage
                     * (e.g. only updates with non-zero variable debt supply)
                     * @param asset The address of the underlying asset of the reserve
                     * @return The reserve normalized variable debt
                     */
                    function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);
                    /**
                     * @notice Returns the state and configuration of the reserve
                     * @param asset The address of the underlying asset of the reserve
                     * @return The state and configuration data of the reserve
                     */
                    function getReserveData(address asset) external view returns (DataTypes.ReserveDataLegacy memory);
                    /**
                     * @notice Returns the virtual underlying balance of the reserve
                     * @param asset The address of the underlying asset of the reserve
                     * @return The reserve virtual underlying balance
                     */
                    function getVirtualUnderlyingBalance(address asset) external view returns (uint128);
                    /**
                     * @notice Validates and finalizes an aToken transfer
                     * @dev Only callable by the overlying aToken of the `asset`
                     * @param asset The address of the underlying asset of the aToken
                     * @param from The user from which the aTokens are transferred
                     * @param to The user receiving the aTokens
                     * @param amount The amount being transferred/withdrawn
                     * @param balanceFromBefore The aToken balance of the `from` user before the transfer
                     * @param balanceToBefore The aToken balance of the `to` user before the transfer
                     */
                    function finalizeTransfer(
                      address asset,
                      address from,
                      address to,
                      uint256 amount,
                      uint256 balanceFromBefore,
                      uint256 balanceToBefore
                    ) external;
                    /**
                     * @notice Returns the list of the underlying assets of all the initialized reserves
                     * @dev It does not include dropped reserves
                     * @return The addresses of the underlying assets of the initialized reserves
                     */
                    function getReservesList() external view returns (address[] memory);
                    /**
                     * @notice Returns the number of initialized reserves
                     * @dev It includes dropped reserves
                     * @return The count
                     */
                    function getReservesCount() external view returns (uint256);
                    /**
                     * @notice Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypes.ReserveData struct
                     * @param id The id of the reserve as stored in the DataTypes.ReserveData struct
                     * @return The address of the reserve associated with id
                     */
                    function getReserveAddressById(uint16 id) external view returns (address);
                    /**
                     * @notice Returns the PoolAddressesProvider connected to this contract
                     * @return The address of the PoolAddressesProvider
                     */
                    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                    /**
                     * @notice Updates the protocol fee on the bridging
                     * @param bridgeProtocolFee The part of the premium sent to the protocol treasury
                     */
                    function updateBridgeProtocolFee(uint256 bridgeProtocolFee) external;
                    /**
                     * @notice Updates flash loan premiums. Flash loan premium consists of two parts:
                     * - A part is sent to aToken holders as extra, one time accumulated interest
                     * - A part is collected by the protocol treasury
                     * @dev The total premium is calculated on the total borrowed amount
                     * @dev The premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal`
                     * @dev Only callable by the PoolConfigurator contract
                     * @param flashLoanPremiumTotal The total premium, expressed in bps
                     * @param flashLoanPremiumToProtocol The part of the premium sent to the protocol treasury, expressed in bps
                     */
                    function updateFlashloanPremiums(
                      uint128 flashLoanPremiumTotal,
                      uint128 flashLoanPremiumToProtocol
                    ) external;
                    /**
                     * @notice Configures a new or alters an existing collateral configuration of an eMode.
                     * @dev In eMode, the protocol allows very high borrowing power to borrow assets of the same category.
                     * The category 0 is reserved as it's the default for volatile assets
                     * @param id The id of the category
                     * @param config The configuration of the category
                     */
                    function configureEModeCategory(
                      uint8 id,
                      DataTypes.EModeCategoryBaseConfiguration memory config
                    ) external;
                    /**
                     * @notice Replaces the current eMode collateralBitmap.
                     * @param id The id of the category
                     * @param collateralBitmap The collateralBitmap of the category
                     */
                    function configureEModeCategoryCollateralBitmap(uint8 id, uint128 collateralBitmap) external;
                    /**
                     * @notice Replaces the current eMode borrowableBitmap.
                     * @param id The id of the category
                     * @param borrowableBitmap The borrowableBitmap of the category
                     */
                    function configureEModeCategoryBorrowableBitmap(uint8 id, uint128 borrowableBitmap) external;
                    /**
                     * @notice Returns the data of an eMode category
                     * @dev DEPRECATED use independent getters instead
                     * @param id The id of the category
                     * @return The configuration data of the category
                     */
                    function getEModeCategoryData(
                      uint8 id
                    ) external view returns (DataTypes.EModeCategoryLegacy memory);
                    /**
                     * @notice Returns the label of an eMode category
                     * @param id The id of the category
                     * @return The label of the category
                     */
                    function getEModeCategoryLabel(uint8 id) external view returns (string memory);
                    /**
                     * @notice Returns the collateral config of an eMode category
                     * @param id The id of the category
                     * @return The ltv,lt,lb of the category
                     */
                    function getEModeCategoryCollateralConfig(
                      uint8 id
                    ) external view returns (DataTypes.CollateralConfig memory);
                    /**
                     * @notice Returns the collateralBitmap of an eMode category
                     * @param id The id of the category
                     * @return The collateralBitmap of the category
                     */
                    function getEModeCategoryCollateralBitmap(uint8 id) external view returns (uint128);
                    /**
                     * @notice Returns the borrowableBitmap of an eMode category
                     * @param id The id of the category
                     * @return The borrowableBitmap of the category
                     */
                    function getEModeCategoryBorrowableBitmap(uint8 id) external view returns (uint128);
                    /**
                     * @notice Allows a user to use the protocol in eMode
                     * @param categoryId The id of the category
                     */
                    function setUserEMode(uint8 categoryId) external;
                    /**
                     * @notice Returns the eMode the user is using
                     * @param user The address of the user
                     * @return The eMode id
                     */
                    function getUserEMode(address user) external view returns (uint256);
                    /**
                     * @notice Resets the isolation mode total debt of the given asset to zero
                     * @dev It requires the given asset has zero debt ceiling
                     * @param asset The address of the underlying asset to reset the isolationModeTotalDebt
                     */
                    function resetIsolationModeTotalDebt(address asset) external;
                    /**
                     * @notice Sets the liquidation grace period of the given asset
                     * @dev To enable a liquidation grace period, a timestamp in the future should be set,
                     *      To disable a liquidation grace period, any timestamp in the past works, like 0
                     * @param asset The address of the underlying asset to set the liquidationGracePeriod
                     * @param until Timestamp when the liquidation grace period will end
                     **/
                    function setLiquidationGracePeriod(address asset, uint40 until) external;
                    /**
                     * @notice Returns the liquidation grace period of the given asset
                     * @param asset The address of the underlying asset
                     * @return Timestamp when the liquidation grace period will end
                     **/
                    function getLiquidationGracePeriod(address asset) external view returns (uint40);
                    /**
                     * @notice Returns the total fee on flash loans
                     * @return The total fee on flashloans
                     */
                    function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint128);
                    /**
                     * @notice Returns the part of the bridge fees sent to protocol
                     * @return The bridge fee sent to the protocol treasury
                     */
                    function BRIDGE_PROTOCOL_FEE() external view returns (uint256);
                    /**
                     * @notice Returns the part of the flashloan fees sent to protocol
                     * @return The flashloan fee sent to the protocol treasury
                     */
                    function FLASHLOAN_PREMIUM_TO_PROTOCOL() external view returns (uint128);
                    /**
                     * @notice Returns the maximum number of reserves supported to be listed in this Pool
                     * @return The maximum number of reserves supported
                     */
                    function MAX_NUMBER_RESERVES() external view returns (uint16);
                    /**
                     * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens
                     * @param assets The list of reserves for which the minting needs to be executed
                     */
                    function mintToTreasury(address[] calldata assets) external;
                    /**
                     * @notice Rescue and transfer tokens locked in this contract
                     * @param token The address of the token
                     * @param to The address of the recipient
                     * @param amount The amount of token to transfer
                     */
                    function rescueTokens(address token, address to, uint256 amount) external;
                    /**
                     * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                     * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                     * @dev Deprecated: Use the `supply` function instead
                     * @param asset The address of the underlying asset to supply
                     * @param amount The amount to be supplied
                     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                     *   is a different wallet
                     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                     *   0 if the action is executed directly by the user, without any middle-man
                     */
                    function deposit(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
                    /**
                     * @notice It covers the deficit of a specified reserve by burning:
                     * - the equivalent aToken `amount` for assets with virtual accounting enabled
                     * - the equivalent `amount` of underlying for assets with virtual accounting disabled (e.g. GHO)
                     * @dev The deficit of a reserve can occur due to situations where borrowed assets are not repaid, leading to bad debt.
                     * @param asset The address of the underlying asset to cover the deficit.
                     * @param amount The amount to be covered, in aToken or underlying on non-virtual accounted assets
                     */
                    function eliminateReserveDeficit(address asset, uint256 amount) external;
                    /**
                     * @notice Returns the current deficit of a reserve.
                     * @param asset The address of the underlying asset of the reserve
                     * @return The current deficit of the reserve
                     */
                    function getReserveDeficit(address asset) external view returns (uint256);
                    /**
                     * @notice Returns the aToken address of a reserve.
                     * @param asset The address of the underlying asset of the reserve
                     * @return The address of the aToken
                     */
                    function getReserveAToken(address asset) external view returns (address);
                    /**
                     * @notice Returns the variableDebtToken address of a reserve.
                     * @param asset The address of the underlying asset of the reserve
                     * @return The address of the variableDebtToken
                     */
                    function getReserveVariableDebtToken(address asset) external view returns (address);
                    /**
                     * @notice Gets the address of the external FlashLoanLogic
                     */
                    function getFlashLoanLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external BorrowLogic
                     */
                    function getBorrowLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external BridgeLogic
                     */
                    function getBridgeLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external EModeLogic
                     */
                    function getEModeLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external LiquidationLogic
                     */
                    function getLiquidationLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external PoolLogic
                     */
                    function getPoolLogic() external view returns (address);
                    /**
                     * @notice Gets the address of the external SupplyLogic
                     */
                    function getSupplyLogic() external view returns (address);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title IPoolAddressesProvider
                   * @author Aave
                   * @notice Defines the basic interface for a Pool Addresses Provider.
                   */
                  interface IPoolAddressesProvider {
                    /**
                     * @dev Emitted when the market identifier is updated.
                     * @param oldMarketId The old id of the market
                     * @param newMarketId The new id of the market
                     */
                    event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);
                    /**
                     * @dev Emitted when the pool is updated.
                     * @param oldAddress The old address of the Pool
                     * @param newAddress The new address of the Pool
                     */
                    event PoolUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the pool configurator is updated.
                     * @param oldAddress The old address of the PoolConfigurator
                     * @param newAddress The new address of the PoolConfigurator
                     */
                    event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the price oracle is updated.
                     * @param oldAddress The old address of the PriceOracle
                     * @param newAddress The new address of the PriceOracle
                     */
                    event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the ACL manager is updated.
                     * @param oldAddress The old address of the ACLManager
                     * @param newAddress The new address of the ACLManager
                     */
                    event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the ACL admin is updated.
                     * @param oldAddress The old address of the ACLAdmin
                     * @param newAddress The new address of the ACLAdmin
                     */
                    event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the price oracle sentinel is updated.
                     * @param oldAddress The old address of the PriceOracleSentinel
                     * @param newAddress The new address of the PriceOracleSentinel
                     */
                    event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the pool data provider is updated.
                     * @param oldAddress The old address of the PoolDataProvider
                     * @param newAddress The new address of the PoolDataProvider
                     */
                    event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when a new proxy is created.
                     * @param id The identifier of the proxy
                     * @param proxyAddress The address of the created proxy contract
                     * @param implementationAddress The address of the implementation contract
                     */
                    event ProxyCreated(
                      bytes32 indexed id,
                      address indexed proxyAddress,
                      address indexed implementationAddress
                    );
                    /**
                     * @dev Emitted when a new non-proxied contract address is registered.
                     * @param id The identifier of the contract
                     * @param oldAddress The address of the old contract
                     * @param newAddress The address of the new contract
                     */
                    event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the implementation of the proxy registered with id is updated
                     * @param id The identifier of the contract
                     * @param proxyAddress The address of the proxy contract
                     * @param oldImplementationAddress The address of the old implementation contract
                     * @param newImplementationAddress The address of the new implementation contract
                     */
                    event AddressSetAsProxy(
                      bytes32 indexed id,
                      address indexed proxyAddress,
                      address oldImplementationAddress,
                      address indexed newImplementationAddress
                    );
                    /**
                     * @notice Returns the id of the Aave market to which this contract points to.
                     * @return The market id
                     */
                    function getMarketId() external view returns (string memory);
                    /**
                     * @notice Associates an id with a specific PoolAddressesProvider.
                     * @dev This can be used to create an onchain registry of PoolAddressesProviders to
                     * identify and validate multiple Aave markets.
                     * @param newMarketId The market id
                     */
                    function setMarketId(string calldata newMarketId) external;
                    /**
                     * @notice Returns an address by its identifier.
                     * @dev The returned address might be an EOA or a contract, potentially proxied
                     * @dev It returns ZERO if there is no registered address with the given id
                     * @param id The id
                     * @return The address of the registered for the specified id
                     */
                    function getAddress(bytes32 id) external view returns (address);
                    /**
                     * @notice General function to update the implementation of a proxy registered with
                     * certain `id`. If there is no proxy registered, it will instantiate one and
                     * set as implementation the `newImplementationAddress`.
                     * @dev IMPORTANT Use this function carefully, only for ids that don't have an explicit
                     * setter function, in order to avoid unexpected consequences
                     * @param id The id
                     * @param newImplementationAddress The address of the new implementation
                     */
                    function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;
                    /**
                     * @notice Sets an address for an id replacing the address saved in the addresses map.
                     * @dev IMPORTANT Use this function carefully, as it will do a hard replacement
                     * @param id The id
                     * @param newAddress The address to set
                     */
                    function setAddress(bytes32 id, address newAddress) external;
                    /**
                     * @notice Returns the address of the Pool proxy.
                     * @return The Pool proxy address
                     */
                    function getPool() external view returns (address);
                    /**
                     * @notice Updates the implementation of the Pool, or creates a proxy
                     * setting the new `pool` implementation when the function is called for the first time.
                     * @param newPoolImpl The new Pool implementation
                     */
                    function setPoolImpl(address newPoolImpl) external;
                    /**
                     * @notice Returns the address of the PoolConfigurator proxy.
                     * @return The PoolConfigurator proxy address
                     */
                    function getPoolConfigurator() external view returns (address);
                    /**
                     * @notice Updates the implementation of the PoolConfigurator, or creates a proxy
                     * setting the new `PoolConfigurator` implementation when the function is called for the first time.
                     * @param newPoolConfiguratorImpl The new PoolConfigurator implementation
                     */
                    function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;
                    /**
                     * @notice Returns the address of the price oracle.
                     * @return The address of the PriceOracle
                     */
                    function getPriceOracle() external view returns (address);
                    /**
                     * @notice Updates the address of the price oracle.
                     * @param newPriceOracle The address of the new PriceOracle
                     */
                    function setPriceOracle(address newPriceOracle) external;
                    /**
                     * @notice Returns the address of the ACL manager.
                     * @return The address of the ACLManager
                     */
                    function getACLManager() external view returns (address);
                    /**
                     * @notice Updates the address of the ACL manager.
                     * @param newAclManager The address of the new ACLManager
                     */
                    function setACLManager(address newAclManager) external;
                    /**
                     * @notice Returns the address of the ACL admin.
                     * @return The address of the ACL admin
                     */
                    function getACLAdmin() external view returns (address);
                    /**
                     * @notice Updates the address of the ACL admin.
                     * @param newAclAdmin The address of the new ACL admin
                     */
                    function setACLAdmin(address newAclAdmin) external;
                    /**
                     * @notice Returns the address of the price oracle sentinel.
                     * @return The address of the PriceOracleSentinel
                     */
                    function getPriceOracleSentinel() external view returns (address);
                    /**
                     * @notice Updates the address of the price oracle sentinel.
                     * @param newPriceOracleSentinel The address of the new PriceOracleSentinel
                     */
                    function setPriceOracleSentinel(address newPriceOracleSentinel) external;
                    /**
                     * @notice Returns the address of the data provider.
                     * @return The address of the DataProvider
                     */
                    function getPoolDataProvider() external view returns (address);
                    /**
                     * @notice Updates the address of the data provider.
                     * @param newDataProvider The address of the new DataProvider
                     */
                    function setPoolDataProvider(address newDataProvider) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title IPriceOracleGetter
                   * @author Aave
                   * @notice Interface for the Aave price oracle.
                   */
                  interface IPriceOracleGetter {
                    /**
                     * @notice Returns the base currency address
                     * @dev Address 0x0 is reserved for USD as base currency.
                     * @return Returns the base currency address.
                     */
                    function BASE_CURRENCY() external view returns (address);
                    /**
                     * @notice Returns the base currency unit
                     * @dev 1 ether for ETH, 1e8 for USD.
                     * @return Returns the base currency unit.
                     */
                    function BASE_CURRENCY_UNIT() external view returns (uint256);
                    /**
                     * @notice Returns the asset price in the base currency
                     * @param asset The address of the asset
                     * @return The price of the asset
                     */
                    function getAssetPrice(address asset) external view returns (uint256);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                  /**
                   * @title IPriceOracleSentinel
                   * @author Aave
                   * @notice Defines the basic interface for the PriceOracleSentinel
                   */
                  interface IPriceOracleSentinel {
                    /**
                     * @dev Emitted after the sequencer oracle is updated
                     * @param newSequencerOracle The new sequencer oracle
                     */
                    event SequencerOracleUpdated(address newSequencerOracle);
                    /**
                     * @dev Emitted after the grace period is updated
                     * @param newGracePeriod The new grace period value
                     */
                    event GracePeriodUpdated(uint256 newGracePeriod);
                    /**
                     * @notice Returns the PoolAddressesProvider
                     * @return The address of the PoolAddressesProvider contract
                     */
                    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                    /**
                     * @notice Returns true if the `borrow` operation is allowed.
                     * @dev Operation not allowed when PriceOracle is down or grace period not passed.
                     * @return True if the `borrow` operation is allowed, false otherwise.
                     */
                    function isBorrowAllowed() external view returns (bool);
                    /**
                     * @notice Returns true if the `liquidation` operation is allowed.
                     * @dev Operation not allowed when PriceOracle is down or grace period not passed.
                     * @return True if the `liquidation` operation is allowed, false otherwise.
                     */
                    function isLiquidationAllowed() external view returns (bool);
                    /**
                     * @notice Updates the address of the sequencer oracle
                     * @param newSequencerOracle The address of the new Sequencer Oracle to use
                     */
                    function setSequencerOracle(address newSequencerOracle) external;
                    /**
                     * @notice Updates the duration of the grace period
                     * @param newGracePeriod The value of the new grace period duration
                     */
                    function setGracePeriod(uint256 newGracePeriod) external;
                    /**
                     * @notice Returns the SequencerOracle
                     * @return The address of the sequencer oracle contract
                     */
                    function getSequencerOracle() external view returns (address);
                    /**
                     * @notice Returns the grace period
                     * @return The duration of the grace period
                     */
                    function getGracePeriod() external view returns (uint256);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                  /**
                   * @title IReserveInterestRateStrategy
                   * @author BGD Labs
                   * @notice Basic interface for any rate strategy used by the Aave protocol
                   */
                  interface IReserveInterestRateStrategy {
                    /**
                     * @notice Sets interest rate data for an Aave rate strategy
                     * @param reserve The reserve to update
                     * @param rateData The abi encoded reserve interest rate data to apply to the given reserve
                     *   Abstracted this way as rate strategies can be custom
                     */
                    function setInterestRateParams(address reserve, bytes calldata rateData) external;
                    /**
                     * @notice Calculates the interest rates depending on the reserve's state and configurations
                     * @param params The parameters needed to calculate interest rates
                     * @return liquidityRate The liquidity rate expressed in ray
                     * @return variableBorrowRate The variable borrow rate expressed in ray
                     */
                    function calculateInterestRates(
                      DataTypes.CalculateInterestRatesParams memory params
                    ) external view returns (uint256, uint256);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title IScaledBalanceToken
                   * @author Aave
                   * @notice Defines the basic interface for a scaled-balance token.
                   */
                  interface IScaledBalanceToken {
                    /**
                     * @dev Emitted after the mint action
                     * @param caller The address performing the mint
                     * @param onBehalfOf The address of the user that will receive the minted tokens
                     * @param value The scaled-up amount being minted (based on user entered amount and balance increase from interest)
                     * @param balanceIncrease The increase in scaled-up balance since the last action of 'onBehalfOf'
                     * @param index The next liquidity index of the reserve
                     */
                    event Mint(
                      address indexed caller,
                      address indexed onBehalfOf,
                      uint256 value,
                      uint256 balanceIncrease,
                      uint256 index
                    );
                    /**
                     * @dev Emitted after the burn action
                     * @dev If the burn function does not involve a transfer of the underlying asset, the target defaults to zero address
                     * @param from The address from which the tokens will be burned
                     * @param target The address that will receive the underlying, if any
                     * @param value The scaled-up amount being burned (user entered amount - balance increase from interest)
                     * @param balanceIncrease The increase in scaled-up balance since the last action of 'from'
                     * @param index The next liquidity index of the reserve
                     */
                    event Burn(
                      address indexed from,
                      address indexed target,
                      uint256 value,
                      uint256 balanceIncrease,
                      uint256 index
                    );
                    /**
                     * @notice Returns the scaled balance of the user.
                     * @dev The scaled balance is the sum of all the updated stored balance divided by the reserve's liquidity index
                     * at the moment of the update
                     * @param user The user whose balance is calculated
                     * @return The scaled balance of the user
                     */
                    function scaledBalanceOf(address user) external view returns (uint256);
                    /**
                     * @notice Returns the scaled balance of the user and the scaled total supply.
                     * @param user The address of the user
                     * @return The scaled balance of the user
                     * @return The scaled total supply
                     */
                    function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
                    /**
                     * @notice Returns the scaled total supply of the scaled balance token. Represents sum(debt/index)
                     * @return The scaled total supply
                     */
                    function scaledTotalSupply() external view returns (uint256);
                    /**
                     * @notice Returns last index interest was accrued to the user's balance
                     * @param user The address of the user
                     * @return The last index interest was accrued to the user's balance, expressed in ray
                     */
                    function getPreviousIndex(address user) external view returns (uint256);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                  import {IInitializableDebtToken} from './IInitializableDebtToken.sol';
                  /**
                   * @title IVariableDebtToken
                   * @author Aave
                   * @notice Defines the basic interface for a variable debt token.
                   */
                  interface IVariableDebtToken is IScaledBalanceToken, IInitializableDebtToken {
                    /**
                     * @notice Mints debt token to the `onBehalfOf` address
                     * @param user The address receiving the borrowed underlying, being the delegatee in case
                     * of credit delegate, or same as `onBehalfOf` otherwise
                     * @param onBehalfOf The address receiving the debt tokens
                     * @param amount The amount of debt being minted
                     * @param index The variable debt index of the reserve
                     * @return True if the previous balance of the user is 0, false otherwise
                     * @return The scaled total debt of the reserve
                     */
                    function mint(
                      address user,
                      address onBehalfOf,
                      uint256 amount,
                      uint256 index
                    ) external returns (bool, uint256);
                    /**
                     * @notice Burns user variable debt
                     * @dev In some instances, a burn transaction will emit a mint event
                     * if the amount to burn is less than the interest that the user accrued
                     * @param from The address from which the debt will be burned
                     * @param amount The amount getting burned
                     * @param index The variable debt index of the reserve
                     * @return The scaled total debt of the reserve
                     */
                    function burn(address from, uint256 amount, uint256 index) external returns (uint256);
                    /**
                     * @notice Returns the address of the underlying asset of this debtToken (E.g. WETH for variableDebtWETH)
                     * @return The address of the underlying asset
                     */
                    function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {Errors} from '../helpers/Errors.sol';
                  import {ReserveConfiguration} from './ReserveConfiguration.sol';
                  /**
                   * @title EModeConfiguration library
                   * @author BGD Labs
                   * @notice Implements the bitmap logic to handle the eMode configuration
                   */
                  library EModeConfiguration {
                    /**
                     * @notice Sets a bit in a given bitmap that represents the reserve index range
                     * @dev The supplied bitmap is supposed to be a uint128 in which each bit represents a reserve
                     * @param bitmap The bitmap
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @param enabled True if the reserveIndex should be enabled on the bitmap, false otherwise
                     * @return The altered bitmap
                     */
                    function setReserveBitmapBit(
                      uint128 bitmap,
                      uint256 reserveIndex,
                      bool enabled
                    ) internal pure returns (uint128) {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        uint128 bit = uint128(1 << reserveIndex);
                        if (enabled) {
                          return bitmap | bit;
                        } else {
                          return bitmap & ~bit;
                        }
                      }
                    }
                    /**
                     * @notice Validates if a reserveIndex is flagged as enabled on a given bitmap
                     * @param bitmap The bitmap
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @return True if the reserveindex is flagged true
                     */
                    function isReserveEnabledOnBitmap(
                      uint128 bitmap,
                      uint256 reserveIndex
                    ) internal pure returns (bool) {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        return (bitmap >> reserveIndex) & 1 != 0;
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {Errors} from '../helpers/Errors.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  /**
                   * @title ReserveConfiguration library
                   * @author Aave
                   * @notice Implements the bitmap logic to handle the reserve configuration
                   */
                  library ReserveConfiguration {
                    uint256 internal constant LTV_MASK =                       0x000000000000000000000000000000000000000000000000000000000000FFFF; // prettier-ignore
                    uint256 internal constant LIQUIDATION_THRESHOLD_MASK =     0x00000000000000000000000000000000000000000000000000000000FFFF0000; // prettier-ignore
                    uint256 internal constant LIQUIDATION_BONUS_MASK =         0x0000000000000000000000000000000000000000000000000000FFFF00000000; // prettier-ignore
                    uint256 internal constant DECIMALS_MASK =                  0x00000000000000000000000000000000000000000000000000FF000000000000; // prettier-ignore
                    uint256 internal constant ACTIVE_MASK =                    0x0000000000000000000000000000000000000000000000000100000000000000; // prettier-ignore
                    uint256 internal constant FROZEN_MASK =                    0x0000000000000000000000000000000000000000000000000200000000000000; // prettier-ignore
                    uint256 internal constant BORROWING_MASK =                 0x0000000000000000000000000000000000000000000000000400000000000000; // prettier-ignore
                    // @notice there is an unoccupied hole of 1 bit at position 59 from pre 3.2 stableBorrowRateEnabled
                    uint256 internal constant PAUSED_MASK =                    0x0000000000000000000000000000000000000000000000001000000000000000; // prettier-ignore
                    uint256 internal constant BORROWABLE_IN_ISOLATION_MASK =   0x0000000000000000000000000000000000000000000000002000000000000000; // prettier-ignore
                    uint256 internal constant SILOED_BORROWING_MASK =          0x0000000000000000000000000000000000000000000000004000000000000000; // prettier-ignore
                    uint256 internal constant FLASHLOAN_ENABLED_MASK =         0x0000000000000000000000000000000000000000000000008000000000000000; // prettier-ignore
                    uint256 internal constant RESERVE_FACTOR_MASK =            0x00000000000000000000000000000000000000000000FFFF0000000000000000; // prettier-ignore
                    uint256 internal constant BORROW_CAP_MASK =                0x00000000000000000000000000000000000FFFFFFFFF00000000000000000000; // prettier-ignore
                    uint256 internal constant SUPPLY_CAP_MASK =                0x00000000000000000000000000FFFFFFFFF00000000000000000000000000000; // prettier-ignore
                    uint256 internal constant LIQUIDATION_PROTOCOL_FEE_MASK =  0x0000000000000000000000FFFF00000000000000000000000000000000000000; // prettier-ignore
                    //@notice there is an unoccupied hole of 8 bits from 168 to 176 left from pre 3.2 eModeCategory
                    uint256 internal constant UNBACKED_MINT_CAP_MASK =         0x00000000000FFFFFFFFF00000000000000000000000000000000000000000000; // prettier-ignore
                    uint256 internal constant DEBT_CEILING_MASK =              0x0FFFFFFFFFF00000000000000000000000000000000000000000000000000000; // prettier-ignore
                    uint256 internal constant VIRTUAL_ACC_ACTIVE_MASK =        0x1000000000000000000000000000000000000000000000000000000000000000; // prettier-ignore
                    /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed
                    uint256 internal constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16;
                    uint256 internal constant LIQUIDATION_BONUS_START_BIT_POSITION = 32;
                    uint256 internal constant RESERVE_DECIMALS_START_BIT_POSITION = 48;
                    uint256 internal constant IS_ACTIVE_START_BIT_POSITION = 56;
                    uint256 internal constant IS_FROZEN_START_BIT_POSITION = 57;
                    uint256 internal constant BORROWING_ENABLED_START_BIT_POSITION = 58;
                    uint256 internal constant IS_PAUSED_START_BIT_POSITION = 60;
                    uint256 internal constant BORROWABLE_IN_ISOLATION_START_BIT_POSITION = 61;
                    uint256 internal constant SILOED_BORROWING_START_BIT_POSITION = 62;
                    uint256 internal constant FLASHLOAN_ENABLED_START_BIT_POSITION = 63;
                    uint256 internal constant RESERVE_FACTOR_START_BIT_POSITION = 64;
                    uint256 internal constant BORROW_CAP_START_BIT_POSITION = 80;
                    uint256 internal constant SUPPLY_CAP_START_BIT_POSITION = 116;
                    uint256 internal constant LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION = 152;
                    //@notice there is an unoccupied hole of 8 bits from 168 to 176 left from pre 3.2 eModeCategory
                    uint256 internal constant UNBACKED_MINT_CAP_START_BIT_POSITION = 176;
                    uint256 internal constant DEBT_CEILING_START_BIT_POSITION = 212;
                    uint256 internal constant VIRTUAL_ACC_START_BIT_POSITION = 252;
                    uint256 internal constant MAX_VALID_LTV = 65535;
                    uint256 internal constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535;
                    uint256 internal constant MAX_VALID_LIQUIDATION_BONUS = 65535;
                    uint256 internal constant MAX_VALID_DECIMALS = 255;
                    uint256 internal constant MAX_VALID_RESERVE_FACTOR = 65535;
                    uint256 internal constant MAX_VALID_BORROW_CAP = 68719476735;
                    uint256 internal constant MAX_VALID_SUPPLY_CAP = 68719476735;
                    uint256 internal constant MAX_VALID_LIQUIDATION_PROTOCOL_FEE = 65535;
                    uint256 internal constant MAX_VALID_UNBACKED_MINT_CAP = 68719476735;
                    uint256 internal constant MAX_VALID_DEBT_CEILING = 1099511627775;
                    uint256 public constant DEBT_CEILING_DECIMALS = 2;
                    uint16 public constant MAX_RESERVES_COUNT = 128;
                    /**
                     * @notice Sets the Loan to Value of the reserve
                     * @param self The reserve configuration
                     * @param ltv The new ltv
                     */
                    function setLtv(DataTypes.ReserveConfigurationMap memory self, uint256 ltv) internal pure {
                      require(ltv <= MAX_VALID_LTV, Errors.INVALID_LTV);
                      self.data = (self.data & ~LTV_MASK) | ltv;
                    }
                    /**
                     * @notice Gets the Loan to Value of the reserve
                     * @param self The reserve configuration
                     * @return The loan to value
                     */
                    function getLtv(DataTypes.ReserveConfigurationMap memory self) internal pure returns (uint256) {
                      return self.data & LTV_MASK;
                    }
                    /**
                     * @notice Sets the liquidation threshold of the reserve
                     * @param self The reserve configuration
                     * @param threshold The new liquidation threshold
                     */
                    function setLiquidationThreshold(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 threshold
                    ) internal pure {
                      require(threshold <= MAX_VALID_LIQUIDATION_THRESHOLD, Errors.INVALID_LIQ_THRESHOLD);
                      self.data =
                        (self.data & ~LIQUIDATION_THRESHOLD_MASK) |
                        (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the liquidation threshold of the reserve
                     * @param self The reserve configuration
                     * @return The liquidation threshold
                     */
                    function getLiquidationThreshold(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the liquidation bonus of the reserve
                     * @param self The reserve configuration
                     * @param bonus The new liquidation bonus
                     */
                    function setLiquidationBonus(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 bonus
                    ) internal pure {
                      require(bonus <= MAX_VALID_LIQUIDATION_BONUS, Errors.INVALID_LIQ_BONUS);
                      self.data =
                        (self.data & ~LIQUIDATION_BONUS_MASK) |
                        (bonus << LIQUIDATION_BONUS_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the liquidation bonus of the reserve
                     * @param self The reserve configuration
                     * @return The liquidation bonus
                     */
                    function getLiquidationBonus(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the decimals of the underlying asset of the reserve
                     * @param self The reserve configuration
                     * @param decimals The decimals
                     */
                    function setDecimals(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 decimals
                    ) internal pure {
                      require(decimals <= MAX_VALID_DECIMALS, Errors.INVALID_DECIMALS);
                      self.data = (self.data & ~DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the decimals of the underlying asset of the reserve
                     * @param self The reserve configuration
                     * @return The decimals of the asset
                     */
                    function getDecimals(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the active state of the reserve
                     * @param self The reserve configuration
                     * @param active The active state
                     */
                    function setActive(DataTypes.ReserveConfigurationMap memory self, bool active) internal pure {
                      self.data =
                        (self.data & ~ACTIVE_MASK) |
                        (uint256(active ? 1 : 0) << IS_ACTIVE_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the active state of the reserve
                     * @param self The reserve configuration
                     * @return The active state
                     */
                    function getActive(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                      return (self.data & ACTIVE_MASK) != 0;
                    }
                    /**
                     * @notice Sets the frozen state of the reserve
                     * @param self The reserve configuration
                     * @param frozen The frozen state
                     */
                    function setFrozen(DataTypes.ReserveConfigurationMap memory self, bool frozen) internal pure {
                      self.data =
                        (self.data & ~FROZEN_MASK) |
                        (uint256(frozen ? 1 : 0) << IS_FROZEN_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the frozen state of the reserve
                     * @param self The reserve configuration
                     * @return The frozen state
                     */
                    function getFrozen(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                      return (self.data & FROZEN_MASK) != 0;
                    }
                    /**
                     * @notice Sets the paused state of the reserve
                     * @param self The reserve configuration
                     * @param paused The paused state
                     */
                    function setPaused(DataTypes.ReserveConfigurationMap memory self, bool paused) internal pure {
                      self.data =
                        (self.data & ~PAUSED_MASK) |
                        (uint256(paused ? 1 : 0) << IS_PAUSED_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the paused state of the reserve
                     * @param self The reserve configuration
                     * @return The paused state
                     */
                    function getPaused(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                      return (self.data & PAUSED_MASK) != 0;
                    }
                    /**
                     * @notice Sets the borrowable in isolation flag for the reserve.
                     * @dev When this flag is set to true, the asset will be borrowable against isolated collaterals and the borrowed
                     * amount will be accumulated in the isolated collateral's total debt exposure.
                     * @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep
                     * consistency in the debt ceiling calculations.
                     * @param self The reserve configuration
                     * @param borrowable True if the asset is borrowable
                     */
                    function setBorrowableInIsolation(
                      DataTypes.ReserveConfigurationMap memory self,
                      bool borrowable
                    ) internal pure {
                      self.data =
                        (self.data & ~BORROWABLE_IN_ISOLATION_MASK) |
                        (uint256(borrowable ? 1 : 0) << BORROWABLE_IN_ISOLATION_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the borrowable in isolation flag for the reserve.
                     * @dev If the returned flag is true, the asset is borrowable against isolated collateral. Assets borrowed with
                     * isolated collateral is accounted for in the isolated collateral's total debt exposure.
                     * @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep
                     * consistency in the debt ceiling calculations.
                     * @param self The reserve configuration
                     * @return The borrowable in isolation flag
                     */
                    function getBorrowableInIsolation(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return (self.data & BORROWABLE_IN_ISOLATION_MASK) != 0;
                    }
                    /**
                     * @notice Sets the siloed borrowing flag for the reserve.
                     * @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset.
                     * @param self The reserve configuration
                     * @param siloed True if the asset is siloed
                     */
                    function setSiloedBorrowing(
                      DataTypes.ReserveConfigurationMap memory self,
                      bool siloed
                    ) internal pure {
                      self.data =
                        (self.data & ~SILOED_BORROWING_MASK) |
                        (uint256(siloed ? 1 : 0) << SILOED_BORROWING_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the siloed borrowing flag for the reserve.
                     * @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset.
                     * @param self The reserve configuration
                     * @return The siloed borrowing flag
                     */
                    function getSiloedBorrowing(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return (self.data & SILOED_BORROWING_MASK) != 0;
                    }
                    /**
                     * @notice Enables or disables borrowing on the reserve
                     * @param self The reserve configuration
                     * @param enabled True if the borrowing needs to be enabled, false otherwise
                     */
                    function setBorrowingEnabled(
                      DataTypes.ReserveConfigurationMap memory self,
                      bool enabled
                    ) internal pure {
                      self.data =
                        (self.data & ~BORROWING_MASK) |
                        (uint256(enabled ? 1 : 0) << BORROWING_ENABLED_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the borrowing state of the reserve
                     * @param self The reserve configuration
                     * @return The borrowing state
                     */
                    function getBorrowingEnabled(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return (self.data & BORROWING_MASK) != 0;
                    }
                    /**
                     * @notice Sets the reserve factor of the reserve
                     * @param self The reserve configuration
                     * @param reserveFactor The reserve factor
                     */
                    function setReserveFactor(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 reserveFactor
                    ) internal pure {
                      require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.INVALID_RESERVE_FACTOR);
                      self.data =
                        (self.data & ~RESERVE_FACTOR_MASK) |
                        (reserveFactor << RESERVE_FACTOR_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the reserve factor of the reserve
                     * @param self The reserve configuration
                     * @return The reserve factor
                     */
                    function getReserveFactor(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the borrow cap of the reserve
                     * @param self The reserve configuration
                     * @param borrowCap The borrow cap
                     */
                    function setBorrowCap(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 borrowCap
                    ) internal pure {
                      require(borrowCap <= MAX_VALID_BORROW_CAP, Errors.INVALID_BORROW_CAP);
                      self.data = (self.data & ~BORROW_CAP_MASK) | (borrowCap << BORROW_CAP_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the borrow cap of the reserve
                     * @param self The reserve configuration
                     * @return The borrow cap
                     */
                    function getBorrowCap(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the supply cap of the reserve
                     * @param self The reserve configuration
                     * @param supplyCap The supply cap
                     */
                    function setSupplyCap(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 supplyCap
                    ) internal pure {
                      require(supplyCap <= MAX_VALID_SUPPLY_CAP, Errors.INVALID_SUPPLY_CAP);
                      self.data = (self.data & ~SUPPLY_CAP_MASK) | (supplyCap << SUPPLY_CAP_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the supply cap of the reserve
                     * @param self The reserve configuration
                     * @return The supply cap
                     */
                    function getSupplyCap(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the debt ceiling in isolation mode for the asset
                     * @param self The reserve configuration
                     * @param ceiling The maximum debt ceiling for the asset
                     */
                    function setDebtCeiling(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 ceiling
                    ) internal pure {
                      require(ceiling <= MAX_VALID_DEBT_CEILING, Errors.INVALID_DEBT_CEILING);
                      self.data = (self.data & ~DEBT_CEILING_MASK) | (ceiling << DEBT_CEILING_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the debt ceiling for the asset if the asset is in isolation mode
                     * @param self The reserve configuration
                     * @return The debt ceiling (0 = isolation mode disabled)
                     */
                    function getDebtCeiling(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & DEBT_CEILING_MASK) >> DEBT_CEILING_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the liquidation protocol fee of the reserve
                     * @param self The reserve configuration
                     * @param liquidationProtocolFee The liquidation protocol fee
                     */
                    function setLiquidationProtocolFee(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 liquidationProtocolFee
                    ) internal pure {
                      require(
                        liquidationProtocolFee <= MAX_VALID_LIQUIDATION_PROTOCOL_FEE,
                        Errors.INVALID_LIQUIDATION_PROTOCOL_FEE
                      );
                      self.data =
                        (self.data & ~LIQUIDATION_PROTOCOL_FEE_MASK) |
                        (liquidationProtocolFee << LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION);
                    }
                    /**
                     * @dev Gets the liquidation protocol fee
                     * @param self The reserve configuration
                     * @return The liquidation protocol fee
                     */
                    function getLiquidationProtocolFee(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return
                        (self.data & LIQUIDATION_PROTOCOL_FEE_MASK) >> LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the unbacked mint cap of the reserve
                     * @param self The reserve configuration
                     * @param unbackedMintCap The unbacked mint cap
                     */
                    function setUnbackedMintCap(
                      DataTypes.ReserveConfigurationMap memory self,
                      uint256 unbackedMintCap
                    ) internal pure {
                      require(unbackedMintCap <= MAX_VALID_UNBACKED_MINT_CAP, Errors.INVALID_UNBACKED_MINT_CAP);
                      self.data =
                        (self.data & ~UNBACKED_MINT_CAP_MASK) |
                        (unbackedMintCap << UNBACKED_MINT_CAP_START_BIT_POSITION);
                    }
                    /**
                     * @dev Gets the unbacked mint cap of the reserve
                     * @param self The reserve configuration
                     * @return The unbacked mint cap
                     */
                    function getUnbackedMintCap(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256) {
                      return (self.data & UNBACKED_MINT_CAP_MASK) >> UNBACKED_MINT_CAP_START_BIT_POSITION;
                    }
                    /**
                     * @notice Sets the flashloanable flag for the reserve
                     * @param self The reserve configuration
                     * @param flashLoanEnabled True if the asset is flashloanable, false otherwise
                     */
                    function setFlashLoanEnabled(
                      DataTypes.ReserveConfigurationMap memory self,
                      bool flashLoanEnabled
                    ) internal pure {
                      self.data =
                        (self.data & ~FLASHLOAN_ENABLED_MASK) |
                        (uint256(flashLoanEnabled ? 1 : 0) << FLASHLOAN_ENABLED_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the flashloanable flag for the reserve
                     * @param self The reserve configuration
                     * @return The flashloanable flag
                     */
                    function getFlashLoanEnabled(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return (self.data & FLASHLOAN_ENABLED_MASK) != 0;
                    }
                    /**
                     * @notice Sets the virtual account active/not state of the reserve
                     * @param self The reserve configuration
                     * @param active The active state
                     */
                    function setVirtualAccActive(
                      DataTypes.ReserveConfigurationMap memory self,
                      bool active
                    ) internal pure {
                      self.data =
                        (self.data & ~VIRTUAL_ACC_ACTIVE_MASK) |
                        (uint256(active ? 1 : 0) << VIRTUAL_ACC_START_BIT_POSITION);
                    }
                    /**
                     * @notice Gets the virtual account active/not state of the reserve
                     * @dev The state should be true for all normal assets and should be false
                     * Virtual accounting being disabled means that the asset:
                     * - is GHO
                     * - can never be supplied
                     * - the interest rate strategy is not influenced by the virtual balance
                     * @param self The reserve configuration
                     * @return The active state
                     */
                    function getIsVirtualAccActive(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return (self.data & VIRTUAL_ACC_ACTIVE_MASK) != 0;
                    }
                    /**
                     * @notice Gets the configuration flags of the reserve
                     * @param self The reserve configuration
                     * @return The state flag representing active
                     * @return The state flag representing frozen
                     * @return The state flag representing borrowing enabled
                     * @return The state flag representing paused
                     */
                    function getFlags(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (bool, bool, bool, bool) {
                      uint256 dataLocal = self.data;
                      return (
                        (dataLocal & ACTIVE_MASK) != 0,
                        (dataLocal & FROZEN_MASK) != 0,
                        (dataLocal & BORROWING_MASK) != 0,
                        (dataLocal & PAUSED_MASK) != 0
                      );
                    }
                    /**
                     * @notice Gets the configuration parameters of the reserve from storage
                     * @param self The reserve configuration
                     * @return The state param representing ltv
                     * @return The state param representing liquidation threshold
                     * @return The state param representing liquidation bonus
                     * @return The state param representing reserve decimals
                     * @return The state param representing reserve factor
                     */
                    function getParams(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256, uint256, uint256, uint256, uint256) {
                      uint256 dataLocal = self.data;
                      return (
                        dataLocal & LTV_MASK,
                        (dataLocal & LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION,
                        (dataLocal & LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION,
                        (dataLocal & DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION,
                        (dataLocal & RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION
                      );
                    }
                    /**
                     * @notice Gets the caps parameters of the reserve from storage
                     * @param self The reserve configuration
                     * @return The state param representing borrow cap
                     * @return The state param representing supply cap.
                     */
                    function getCaps(
                      DataTypes.ReserveConfigurationMap memory self
                    ) internal pure returns (uint256, uint256) {
                      uint256 dataLocal = self.data;
                      return (
                        (dataLocal & BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION,
                        (dataLocal & SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION
                      );
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {Errors} from '../helpers/Errors.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ReserveConfiguration} from './ReserveConfiguration.sol';
                  /**
                   * @title UserConfiguration library
                   * @author Aave
                   * @notice Implements the bitmap logic to handle the user configuration
                   */
                  library UserConfiguration {
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    uint256 internal constant BORROWING_MASK =
                      0x5555555555555555555555555555555555555555555555555555555555555555;
                    uint256 internal constant COLLATERAL_MASK =
                      0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;
                    /**
                     * @notice Sets if the user is borrowing the reserve identified by reserveIndex
                     * @param self The configuration object
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @param borrowing True if the user is borrowing the reserve, false otherwise
                     */
                    function setBorrowing(
                      DataTypes.UserConfigurationMap storage self,
                      uint256 reserveIndex,
                      bool borrowing
                    ) internal {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        uint256 bit = 1 << (reserveIndex << 1);
                        if (borrowing) {
                          self.data |= bit;
                        } else {
                          self.data &= ~bit;
                        }
                      }
                    }
                    /**
                     * @notice Sets if the user is using as collateral the reserve identified by reserveIndex
                     * @param self The configuration object
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @param usingAsCollateral True if the user is using the reserve as collateral, false otherwise
                     */
                    function setUsingAsCollateral(
                      DataTypes.UserConfigurationMap storage self,
                      uint256 reserveIndex,
                      bool usingAsCollateral
                    ) internal {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        uint256 bit = 1 << ((reserveIndex << 1) + 1);
                        if (usingAsCollateral) {
                          self.data |= bit;
                        } else {
                          self.data &= ~bit;
                        }
                      }
                    }
                    /**
                     * @notice Returns if a user has been using the reserve for borrowing or as collateral
                     * @param self The configuration object
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @return True if the user has been using a reserve for borrowing or as collateral, false otherwise
                     */
                    function isUsingAsCollateralOrBorrowing(
                      DataTypes.UserConfigurationMap memory self,
                      uint256 reserveIndex
                    ) internal pure returns (bool) {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        return (self.data >> (reserveIndex << 1)) & 3 != 0;
                      }
                    }
                    /**
                     * @notice Validate a user has been using the reserve for borrowing
                     * @param self The configuration object
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @return True if the user has been using a reserve for borrowing, false otherwise
                     */
                    function isBorrowing(
                      DataTypes.UserConfigurationMap memory self,
                      uint256 reserveIndex
                    ) internal pure returns (bool) {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        return (self.data >> (reserveIndex << 1)) & 1 != 0;
                      }
                    }
                    /**
                     * @notice Validate a user has been using the reserve as collateral
                     * @param self The configuration object
                     * @param reserveIndex The index of the reserve in the bitmap
                     * @return True if the user has been using a reserve as collateral, false otherwise
                     */
                    function isUsingAsCollateral(
                      DataTypes.UserConfigurationMap memory self,
                      uint256 reserveIndex
                    ) internal pure returns (bool) {
                      unchecked {
                        require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                        return (self.data >> ((reserveIndex << 1) + 1)) & 1 != 0;
                      }
                    }
                    /**
                     * @notice Checks if a user has been supplying only one reserve as collateral
                     * @dev this uses a simple trick - if a number is a power of two (only one bit set) then n & (n - 1) == 0
                     * @param self The configuration object
                     * @return True if the user has been supplying as collateral one reserve, false otherwise
                     */
                    function isUsingAsCollateralOne(
                      DataTypes.UserConfigurationMap memory self
                    ) internal pure returns (bool) {
                      uint256 collateralData = self.data & COLLATERAL_MASK;
                      return collateralData != 0 && (collateralData & (collateralData - 1) == 0);
                    }
                    /**
                     * @notice Checks if a user has been supplying any reserve as collateral
                     * @param self The configuration object
                     * @return True if the user has been supplying as collateral any reserve, false otherwise
                     */
                    function isUsingAsCollateralAny(
                      DataTypes.UserConfigurationMap memory self
                    ) internal pure returns (bool) {
                      return self.data & COLLATERAL_MASK != 0;
                    }
                    /**
                     * @notice Checks if a user has been borrowing only one asset
                     * @dev this uses a simple trick - if a number is a power of two (only one bit set) then n & (n - 1) == 0
                     * @param self The configuration object
                     * @return True if the user has been supplying as collateral one reserve, false otherwise
                     */
                    function isBorrowingOne(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                      uint256 borrowingData = self.data & BORROWING_MASK;
                      return borrowingData != 0 && (borrowingData & (borrowingData - 1) == 0);
                    }
                    /**
                     * @notice Checks if a user has been borrowing from any reserve
                     * @param self The configuration object
                     * @return True if the user has been borrowing any reserve, false otherwise
                     */
                    function isBorrowingAny(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                      return self.data & BORROWING_MASK != 0;
                    }
                    /**
                     * @notice Checks if a user has not been using any reserve for borrowing or supply
                     * @param self The configuration object
                     * @return True if the user has not been borrowing or supplying any reserve, false otherwise
                     */
                    function isEmpty(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                      return self.data == 0;
                    }
                    /**
                     * @notice Returns the Isolation Mode state of the user
                     * @param self The configuration object
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @return True if the user is in isolation mode, false otherwise
                     * @return The address of the only asset used as collateral
                     * @return The debt ceiling of the reserve
                     */
                    function getIsolationModeState(
                      DataTypes.UserConfigurationMap memory self,
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList
                    ) internal view returns (bool, address, uint256) {
                      if (isUsingAsCollateralOne(self)) {
                        uint256 assetId = _getFirstAssetIdByMask(self, COLLATERAL_MASK);
                        address assetAddress = reservesList[assetId];
                        uint256 ceiling = reservesData[assetAddress].configuration.getDebtCeiling();
                        if (ceiling != 0) {
                          return (true, assetAddress, ceiling);
                        }
                      }
                      return (false, address(0), 0);
                    }
                    /**
                     * @notice Returns the siloed borrowing state for the user
                     * @param self The configuration object
                     * @param reservesData The data of all the reserves
                     * @param reservesList The reserve list
                     * @return True if the user has borrowed a siloed asset, false otherwise
                     * @return The address of the only borrowed asset
                     */
                    function getSiloedBorrowingState(
                      DataTypes.UserConfigurationMap memory self,
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList
                    ) internal view returns (bool, address) {
                      if (isBorrowingOne(self)) {
                        uint256 assetId = _getFirstAssetIdByMask(self, BORROWING_MASK);
                        address assetAddress = reservesList[assetId];
                        if (reservesData[assetAddress].configuration.getSiloedBorrowing()) {
                          return (true, assetAddress);
                        }
                      }
                      return (false, address(0));
                    }
                    /**
                     * @notice Returns the address of the first asset flagged in the bitmap given the corresponding bitmask
                     * @param self The configuration object
                     * @return The index of the first asset flagged in the bitmap once the corresponding mask is applied
                     */
                    function _getFirstAssetIdByMask(
                      DataTypes.UserConfigurationMap memory self,
                      uint256 mask
                    ) internal pure returns (uint256) {
                      unchecked {
                        uint256 bitmapData = self.data & mask;
                        uint256 firstAssetPosition = bitmapData & ~(bitmapData - 1);
                        uint256 id;
                        while ((firstAssetPosition >>= 2) != 0) {
                          id += 1;
                        }
                        return id;
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title Errors library
                   * @author Aave
                   * @notice Defines the error messages emitted by the different contracts of the Aave protocol
                   */
                  library Errors {
                    string public constant CALLER_NOT_POOL_ADMIN = '1'; // 'The caller of the function is not a pool admin'
                    string public constant CALLER_NOT_EMERGENCY_ADMIN = '2'; // 'The caller of the function is not an emergency admin'
                    string public constant CALLER_NOT_POOL_OR_EMERGENCY_ADMIN = '3'; // 'The caller of the function is not a pool or emergency admin'
                    string public constant CALLER_NOT_RISK_OR_POOL_ADMIN = '4'; // 'The caller of the function is not a risk or pool admin'
                    string public constant CALLER_NOT_ASSET_LISTING_OR_POOL_ADMIN = '5'; // 'The caller of the function is not an asset listing or pool admin'
                    string public constant CALLER_NOT_BRIDGE = '6'; // 'The caller of the function is not a bridge'
                    string public constant ADDRESSES_PROVIDER_NOT_REGISTERED = '7'; // 'Pool addresses provider is not registered'
                    string public constant INVALID_ADDRESSES_PROVIDER_ID = '8'; // 'Invalid id for the pool addresses provider'
                    string public constant NOT_CONTRACT = '9'; // 'Address is not a contract'
                    string public constant CALLER_NOT_POOL_CONFIGURATOR = '10'; // 'The caller of the function is not the pool configurator'
                    string public constant CALLER_NOT_ATOKEN = '11'; // 'The caller of the function is not an AToken'
                    string public constant INVALID_ADDRESSES_PROVIDER = '12'; // 'The address of the pool addresses provider is invalid'
                    string public constant INVALID_FLASHLOAN_EXECUTOR_RETURN = '13'; // 'Invalid return value of the flashloan executor function'
                    string public constant RESERVE_ALREADY_ADDED = '14'; // 'Reserve has already been added to reserve list'
                    string public constant NO_MORE_RESERVES_ALLOWED = '15'; // 'Maximum amount of reserves in the pool reached'
                    string public constant EMODE_CATEGORY_RESERVED = '16'; // 'Zero eMode category is reserved for volatile heterogeneous assets'
                    string public constant INVALID_EMODE_CATEGORY_ASSIGNMENT = '17'; // 'Invalid eMode category assignment to asset'
                    string public constant RESERVE_LIQUIDITY_NOT_ZERO = '18'; // 'The liquidity of the reserve needs to be 0'
                    string public constant FLASHLOAN_PREMIUM_INVALID = '19'; // 'Invalid flashloan premium'
                    string public constant INVALID_RESERVE_PARAMS = '20'; // 'Invalid risk parameters for the reserve'
                    string public constant INVALID_EMODE_CATEGORY_PARAMS = '21'; // 'Invalid risk parameters for the eMode category'
                    string public constant BRIDGE_PROTOCOL_FEE_INVALID = '22'; // 'Invalid bridge protocol fee'
                    string public constant CALLER_MUST_BE_POOL = '23'; // 'The caller of this function must be a pool'
                    string public constant INVALID_MINT_AMOUNT = '24'; // 'Invalid amount to mint'
                    string public constant INVALID_BURN_AMOUNT = '25'; // 'Invalid amount to burn'
                    string public constant INVALID_AMOUNT = '26'; // 'Amount must be greater than 0'
                    string public constant RESERVE_INACTIVE = '27'; // 'Action requires an active reserve'
                    string public constant RESERVE_FROZEN = '28'; // 'Action cannot be performed because the reserve is frozen'
                    string public constant RESERVE_PAUSED = '29'; // 'Action cannot be performed because the reserve is paused'
                    string public constant BORROWING_NOT_ENABLED = '30'; // 'Borrowing is not enabled'
                    string public constant NOT_ENOUGH_AVAILABLE_USER_BALANCE = '32'; // 'User cannot withdraw more than the available balance'
                    string public constant INVALID_INTEREST_RATE_MODE_SELECTED = '33'; // 'Invalid interest rate mode selected'
                    string public constant COLLATERAL_BALANCE_IS_ZERO = '34'; // 'The collateral balance is 0'
                    string public constant HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '35'; // 'Health factor is lesser than the liquidation threshold'
                    string public constant COLLATERAL_CANNOT_COVER_NEW_BORROW = '36'; // 'There is not enough collateral to cover a new borrow'
                    string public constant COLLATERAL_SAME_AS_BORROWING_CURRENCY = '37'; // 'Collateral is (mostly) the same currency that is being borrowed'
                    string public constant NO_DEBT_OF_SELECTED_TYPE = '39'; // 'For repayment of a specific type of debt, the user needs to have debt that type'
                    string public constant NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '40'; // 'To repay on behalf of a user an explicit amount to repay is needed'
                    string public constant NO_OUTSTANDING_VARIABLE_DEBT = '42'; // 'User does not have outstanding variable rate debt on this reserve'
                    string public constant UNDERLYING_BALANCE_ZERO = '43'; // 'The underlying balance needs to be greater than 0'
                    string public constant INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '44'; // 'Interest rate rebalance conditions were not met'
                    string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45'; // 'Health factor is not below the threshold'
                    string public constant COLLATERAL_CANNOT_BE_LIQUIDATED = '46'; // 'The collateral chosen cannot be liquidated'
                    string public constant SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47'; // 'User did not borrow the specified currency'
                    string public constant INCONSISTENT_FLASHLOAN_PARAMS = '49'; // 'Inconsistent flashloan parameters'
                    string public constant BORROW_CAP_EXCEEDED = '50'; // 'Borrow cap is exceeded'
                    string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'
                    string public constant UNBACKED_MINT_CAP_EXCEEDED = '52'; // 'Unbacked mint cap is exceeded'
                    string public constant DEBT_CEILING_EXCEEDED = '53'; // 'Debt ceiling is exceeded'
                    string public constant UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54'; // 'Claimable rights over underlying not zero (aToken supply or accruedToTreasury)'
                    string public constant VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56'; // 'Variable debt supply is not zero'
                    string public constant LTV_VALIDATION_FAILED = '57'; // 'Ltv validation failed'
                    string public constant INCONSISTENT_EMODE_CATEGORY = '58'; // 'Inconsistent eMode category'
                    string public constant PRICE_ORACLE_SENTINEL_CHECK_FAILED = '59'; // 'Price oracle sentinel validation failed'
                    string public constant ASSET_NOT_BORROWABLE_IN_ISOLATION = '60'; // 'Asset is not borrowable in isolation mode'
                    string public constant RESERVE_ALREADY_INITIALIZED = '61'; // 'Reserve has already been initialized'
                    string public constant USER_IN_ISOLATION_MODE_OR_LTV_ZERO = '62'; // 'User is in isolation mode or ltv is zero'
                    string public constant INVALID_LTV = '63'; // 'Invalid ltv parameter for the reserve'
                    string public constant INVALID_LIQ_THRESHOLD = '64'; // 'Invalid liquidity threshold parameter for the reserve'
                    string public constant INVALID_LIQ_BONUS = '65'; // 'Invalid liquidity bonus parameter for the reserve'
                    string public constant INVALID_DECIMALS = '66'; // 'Invalid decimals parameter of the underlying asset of the reserve'
                    string public constant INVALID_RESERVE_FACTOR = '67'; // 'Invalid reserve factor parameter for the reserve'
                    string public constant INVALID_BORROW_CAP = '68'; // 'Invalid borrow cap for the reserve'
                    string public constant INVALID_SUPPLY_CAP = '69'; // 'Invalid supply cap for the reserve'
                    string public constant INVALID_LIQUIDATION_PROTOCOL_FEE = '70'; // 'Invalid liquidation protocol fee for the reserve'
                    string public constant INVALID_EMODE_CATEGORY = '71'; // 'Invalid eMode category for the reserve'
                    string public constant INVALID_UNBACKED_MINT_CAP = '72'; // 'Invalid unbacked mint cap for the reserve'
                    string public constant INVALID_DEBT_CEILING = '73'; // 'Invalid debt ceiling for the reserve
                    string public constant INVALID_RESERVE_INDEX = '74'; // 'Invalid reserve index'
                    string public constant ACL_ADMIN_CANNOT_BE_ZERO = '75'; // 'ACL admin cannot be set to the zero address'
                    string public constant INCONSISTENT_PARAMS_LENGTH = '76'; // 'Array parameters that should be equal length are not'
                    string public constant ZERO_ADDRESS_NOT_VALID = '77'; // 'Zero address not valid'
                    string public constant INVALID_EXPIRATION = '78'; // 'Invalid expiration'
                    string public constant INVALID_SIGNATURE = '79'; // 'Invalid signature'
                    string public constant OPERATION_NOT_SUPPORTED = '80'; // 'Operation not supported'
                    string public constant DEBT_CEILING_NOT_ZERO = '81'; // 'Debt ceiling is not zero'
                    string public constant ASSET_NOT_LISTED = '82'; // 'Asset is not listed'
                    string public constant INVALID_OPTIMAL_USAGE_RATIO = '83'; // 'Invalid optimal usage ratio'
                    string public constant UNDERLYING_CANNOT_BE_RESCUED = '85'; // 'The underlying asset cannot be rescued'
                    string public constant ADDRESSES_PROVIDER_ALREADY_ADDED = '86'; // 'Reserve has already been added to reserve list'
                    string public constant POOL_ADDRESSES_DO_NOT_MATCH = '87'; // 'The token implementation pool address and the pool address provided by the initializing pool do not match'
                    string public constant SILOED_BORROWING_VIOLATION = '89'; // 'User is trying to borrow multiple assets including a siloed one'
                    string public constant RESERVE_DEBT_NOT_ZERO = '90'; // the total debt of the reserve needs to be 0
                    string public constant FLASHLOAN_DISABLED = '91'; // FlashLoaning for this asset is disabled
                    string public constant INVALID_MAX_RATE = '92'; // The expect maximum borrow rate is invalid
                    string public constant WITHDRAW_TO_ATOKEN = '93'; // Withdrawing to the aToken is not allowed
                    string public constant SUPPLY_TO_ATOKEN = '94'; // Supplying to the aToken is not allowed
                    string public constant SLOPE_2_MUST_BE_GTE_SLOPE_1 = '95'; // Variable interest rate slope 2 can not be lower than slope 1
                    string public constant CALLER_NOT_RISK_OR_POOL_OR_EMERGENCY_ADMIN = '96'; // 'The caller of the function is not a risk, pool or emergency admin'
                    string public constant LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED = '97'; // 'Liquidation grace sentinel validation failed'
                    string public constant INVALID_GRACE_PERIOD = '98'; // Grace period above a valid range
                    string public constant INVALID_FREEZE_STATE = '99'; // Reserve is already in the passed freeze state
                    string public constant NOT_BORROWABLE_IN_EMODE = '100'; // Asset not borrowable in eMode
                    string public constant CALLER_NOT_UMBRELLA = '101'; // The caller of the function is not the umbrella contract
                    string public constant RESERVE_NOT_IN_DEFICIT = '102'; // The reserve is not in deficit
                    string public constant MUST_NOT_LEAVE_DUST = '103'; // Below a certain threshold liquidators need to take the full position
                    string public constant USER_CANNOT_HAVE_DEBT = '104'; // Thrown when a user tries to interact with a method that requires a position without debt
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ValidationLogic} from './ValidationLogic.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  /**
                   * @title EModeLogic library
                   * @author Aave
                   * @notice Implements the base logic for all the actions related to the eMode
                   */
                  library EModeLogic {
                    using ReserveLogic for DataTypes.ReserveCache;
                    using ReserveLogic for DataTypes.ReserveData;
                    using GPv2SafeERC20 for IERC20;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    // See `IPool` for descriptions
                    event UserEModeSet(address indexed user, uint8 categoryId);
                    /**
                     * @notice Updates the user efficiency mode category
                     * @dev Will revert if user is borrowing non-compatible asset or change will drop HF < HEALTH_FACTOR_LIQUIDATION_THRESHOLD
                     * @dev Emits the `UserEModeSet` event
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param usersEModeCategory The state of all users efficiency mode category
                     * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the setUserEMode function
                     */
                    function executeSetUserEMode(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      mapping(address => uint8) storage usersEModeCategory,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ExecuteSetUserEModeParams memory params
                    ) external {
                      if (usersEModeCategory[msg.sender] == params.categoryId) return;
                      ValidationLogic.validateSetUserEMode(
                        eModeCategories,
                        userConfig,
                        params.reservesCount,
                        params.categoryId
                      );
                      usersEModeCategory[msg.sender] = params.categoryId;
                      ValidationLogic.validateHealthFactor(
                        reservesData,
                        reservesList,
                        eModeCategories,
                        userConfig,
                        msg.sender,
                        params.categoryId,
                        params.reservesCount,
                        params.oracle
                      );
                      emit UserEModeSet(msg.sender, params.categoryId);
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
                  import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                  import {EModeConfiguration} from '../configuration/EModeConfiguration.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  import {EModeLogic} from './EModeLogic.sol';
                  /**
                   * @title GenericLogic library
                   * @author Aave
                   * @notice Implements protocol-level logic to calculate and validate the state of a user
                   */
                  library GenericLogic {
                    using ReserveLogic for DataTypes.ReserveData;
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    struct CalculateUserAccountDataVars {
                      uint256 assetPrice;
                      uint256 assetUnit;
                      uint256 userBalanceInBaseCurrency;
                      uint256 decimals;
                      uint256 ltv;
                      uint256 liquidationThreshold;
                      uint256 i;
                      uint256 healthFactor;
                      uint256 totalCollateralInBaseCurrency;
                      uint256 totalDebtInBaseCurrency;
                      uint256 avgLtv;
                      uint256 avgLiquidationThreshold;
                      uint256 eModeLtv;
                      uint256 eModeLiqThreshold;
                      address currentReserveAddress;
                      bool hasZeroLtvCollateral;
                      bool isInEModeCategory;
                    }
                    /**
                     * @notice Calculates the user data across the reserves.
                     * @dev It includes the total liquidity/collateral/borrow balances in the base currency used by the price feed,
                     * the average Loan To Value, the average Liquidation Ratio, and the Health factor.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param params Additional parameters needed for the calculation
                     * @return The total collateral of the user in the base currency used by the price feed
                     * @return The total debt of the user in the base currency used by the price feed
                     * @return The average ltv of the user
                     * @return The average liquidation threshold of the user
                     * @return The health factor of the user
                     * @return True if the ltv is zero, false otherwise
                     */
                    function calculateUserAccountData(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.CalculateUserAccountDataParams memory params
                    ) internal view returns (uint256, uint256, uint256, uint256, uint256, bool) {
                      if (params.userConfig.isEmpty()) {
                        return (0, 0, 0, 0, type(uint256).max, false);
                      }
                      CalculateUserAccountDataVars memory vars;
                      if (params.userEModeCategory != 0) {
                        vars.eModeLtv = eModeCategories[params.userEModeCategory].ltv;
                        vars.eModeLiqThreshold = eModeCategories[params.userEModeCategory].liquidationThreshold;
                      }
                      while (vars.i < params.reservesCount) {
                        if (!params.userConfig.isUsingAsCollateralOrBorrowing(vars.i)) {
                          unchecked {
                            ++vars.i;
                          }
                          continue;
                        }
                        vars.currentReserveAddress = reservesList[vars.i];
                        if (vars.currentReserveAddress == address(0)) {
                          unchecked {
                            ++vars.i;
                          }
                          continue;
                        }
                        DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress];
                        (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve
                          .configuration
                          .getParams();
                        unchecked {
                          vars.assetUnit = 10 ** vars.decimals;
                        }
                        vars.assetPrice = IPriceOracleGetter(params.oracle).getAssetPrice(vars.currentReserveAddress);
                        if (vars.liquidationThreshold != 0 && params.userConfig.isUsingAsCollateral(vars.i)) {
                          vars.userBalanceInBaseCurrency = _getUserBalanceInBaseCurrency(
                            params.user,
                            currentReserve,
                            vars.assetPrice,
                            vars.assetUnit
                          );
                          vars.totalCollateralInBaseCurrency += vars.userBalanceInBaseCurrency;
                          vars.isInEModeCategory =
                            params.userEModeCategory != 0 &&
                            EModeConfiguration.isReserveEnabledOnBitmap(
                              eModeCategories[params.userEModeCategory].collateralBitmap,
                              vars.i
                            );
                          if (vars.ltv != 0) {
                            vars.avgLtv +=
                              vars.userBalanceInBaseCurrency *
                              (vars.isInEModeCategory ? vars.eModeLtv : vars.ltv);
                          } else {
                            vars.hasZeroLtvCollateral = true;
                          }
                          vars.avgLiquidationThreshold +=
                            vars.userBalanceInBaseCurrency *
                            (vars.isInEModeCategory ? vars.eModeLiqThreshold : vars.liquidationThreshold);
                        }
                        if (params.userConfig.isBorrowing(vars.i)) {
                          if (currentReserve.configuration.getIsVirtualAccActive()) {
                            vars.totalDebtInBaseCurrency += _getUserDebtInBaseCurrency(
                              params.user,
                              currentReserve,
                              vars.assetPrice,
                              vars.assetUnit
                            );
                          } else {
                            // custom case for GHO, which applies the GHO discount on balanceOf
                            vars.totalDebtInBaseCurrency +=
                              (IERC20(currentReserve.variableDebtTokenAddress).balanceOf(params.user) *
                                vars.assetPrice) /
                              vars.assetUnit;
                          }
                        }
                        unchecked {
                          ++vars.i;
                        }
                      }
                      unchecked {
                        vars.avgLtv = vars.totalCollateralInBaseCurrency != 0
                          ? vars.avgLtv / vars.totalCollateralInBaseCurrency
                          : 0;
                        vars.avgLiquidationThreshold = vars.totalCollateralInBaseCurrency != 0
                          ? vars.avgLiquidationThreshold / vars.totalCollateralInBaseCurrency
                          : 0;
                      }
                      vars.healthFactor = (vars.totalDebtInBaseCurrency == 0)
                        ? type(uint256).max
                        : (vars.totalCollateralInBaseCurrency.percentMul(vars.avgLiquidationThreshold)).wadDiv(
                          vars.totalDebtInBaseCurrency
                        );
                      return (
                        vars.totalCollateralInBaseCurrency,
                        vars.totalDebtInBaseCurrency,
                        vars.avgLtv,
                        vars.avgLiquidationThreshold,
                        vars.healthFactor,
                        vars.hasZeroLtvCollateral
                      );
                    }
                    /**
                     * @notice Calculates the maximum amount that can be borrowed depending on the available collateral, the total debt
                     * and the average Loan To Value
                     * @param totalCollateralInBaseCurrency The total collateral in the base currency used by the price feed
                     * @param totalDebtInBaseCurrency The total borrow balance in the base currency used by the price feed
                     * @param ltv The average loan to value
                     * @return The amount available to borrow in the base currency of the used by the price feed
                     */
                    function calculateAvailableBorrows(
                      uint256 totalCollateralInBaseCurrency,
                      uint256 totalDebtInBaseCurrency,
                      uint256 ltv
                    ) internal pure returns (uint256) {
                      uint256 availableBorrowsInBaseCurrency = totalCollateralInBaseCurrency.percentMul(ltv);
                      if (availableBorrowsInBaseCurrency <= totalDebtInBaseCurrency) {
                        return 0;
                      }
                      availableBorrowsInBaseCurrency = availableBorrowsInBaseCurrency - totalDebtInBaseCurrency;
                      return availableBorrowsInBaseCurrency;
                    }
                    /**
                     * @notice Calculates total debt of the user in the based currency used to normalize the values of the assets
                     * @dev This fetches the `balanceOf` of the variable debt token for the user. For gas reasons, the
                     * variable debt balance is calculated by fetching `scaledBalancesOf` normalized debt, which is cheaper than
                     * fetching `balanceOf`
                     * @param user The address of the user
                     * @param reserve The data of the reserve for which the total debt of the user is being calculated
                     * @param assetPrice The price of the asset for which the total debt of the user is being calculated
                     * @param assetUnit The value representing one full unit of the asset (10^decimals)
                     * @return The total debt of the user normalized to the base currency
                     */
                    function _getUserDebtInBaseCurrency(
                      address user,
                      DataTypes.ReserveData storage reserve,
                      uint256 assetPrice,
                      uint256 assetUnit
                    ) private view returns (uint256) {
                      // fetching variable debt
                      uint256 userTotalDebt = IScaledBalanceToken(reserve.variableDebtTokenAddress).scaledBalanceOf(
                        user
                      );
                      if (userTotalDebt == 0) {
                        return 0;
                      }
                      userTotalDebt = userTotalDebt.rayMul(reserve.getNormalizedDebt()) * assetPrice;
                      unchecked {
                        return userTotalDebt / assetUnit;
                      }
                    }
                    /**
                     * @notice Calculates total aToken balance of the user in the based currency used by the price oracle
                     * @dev For gas reasons, the aToken balance is calculated by fetching `scaledBalancesOf` normalized debt, which
                     * is cheaper than fetching `balanceOf`
                     * @param user The address of the user
                     * @param reserve The data of the reserve for which the total aToken balance of the user is being calculated
                     * @param assetPrice The price of the asset for which the total aToken balance of the user is being calculated
                     * @param assetUnit The value representing one full unit of the asset (10^decimals)
                     * @return The total aToken balance of the user normalized to the base currency of the price oracle
                     */
                    function _getUserBalanceInBaseCurrency(
                      address user,
                      DataTypes.ReserveData storage reserve,
                      uint256 assetPrice,
                      uint256 assetUnit
                    ) private view returns (uint256) {
                      uint256 normalizedIncome = reserve.getNormalizedIncome();
                      uint256 balance = (
                        IScaledBalanceToken(reserve.aTokenAddress).scaledBalanceOf(user).rayMul(normalizedIncome)
                      ) * assetPrice;
                      unchecked {
                        return balance / assetUnit;
                      }
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
                  import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  import {MathUtils} from '../math/MathUtils.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {Errors} from '../helpers/Errors.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  /**
                   * @title ReserveLogic library
                   * @author Aave
                   * @notice Implements the logic to update the reserves state
                   */
                  library ReserveLogic {
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    using SafeCast for uint256;
                    using GPv2SafeERC20 for IERC20;
                    using ReserveLogic for DataTypes.ReserveData;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    // See `IPool` for descriptions
                    event ReserveDataUpdated(
                      address indexed reserve,
                      uint256 liquidityRate,
                      uint256 stableBorrowRate,
                      uint256 variableBorrowRate,
                      uint256 liquidityIndex,
                      uint256 variableBorrowIndex
                    );
                    /**
                     * @notice Returns the ongoing normalized income for the reserve.
                     * @dev A value of 1e27 means there is no income. As time passes, the income is accrued
                     * @dev A value of 2*1e27 means for each unit of asset one unit of income has been accrued
                     * @param reserve The reserve object
                     * @return The normalized income, expressed in ray
                     */
                    function getNormalizedIncome(
                      DataTypes.ReserveData storage reserve
                    ) internal view returns (uint256) {
                      uint40 timestamp = reserve.lastUpdateTimestamp;
                      //solium-disable-next-line
                      if (timestamp == block.timestamp) {
                        //if the index was updated in the same block, no need to perform any calculation
                        return reserve.liquidityIndex;
                      } else {
                        return
                          MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul(
                            reserve.liquidityIndex
                          );
                      }
                    }
                    /**
                     * @notice Returns the ongoing normalized variable debt for the reserve.
                     * @dev A value of 1e27 means there is no debt. As time passes, the debt is accrued
                     * @dev A value of 2*1e27 means that for each unit of debt, one unit worth of interest has been accumulated
                     * @param reserve The reserve object
                     * @return The normalized variable debt, expressed in ray
                     */
                    function getNormalizedDebt(
                      DataTypes.ReserveData storage reserve
                    ) internal view returns (uint256) {
                      uint40 timestamp = reserve.lastUpdateTimestamp;
                      //solium-disable-next-line
                      if (timestamp == block.timestamp) {
                        //if the index was updated in the same block, no need to perform any calculation
                        return reserve.variableBorrowIndex;
                      } else {
                        return
                          MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp).rayMul(
                            reserve.variableBorrowIndex
                          );
                      }
                    }
                    /**
                     * @notice Updates the liquidity cumulative index and the variable borrow index.
                     * @param reserve The reserve object
                     * @param reserveCache The caching layer for the reserve data
                     */
                    function updateState(
                      DataTypes.ReserveData storage reserve,
                      DataTypes.ReserveCache memory reserveCache
                    ) internal {
                      // If time didn't pass since last stored timestamp, skip state update
                      //solium-disable-next-line
                      if (reserveCache.reserveLastUpdateTimestamp == uint40(block.timestamp)) {
                        return;
                      }
                      _updateIndexes(reserve, reserveCache);
                      _accrueToTreasury(reserve, reserveCache);
                      //solium-disable-next-line
                      reserve.lastUpdateTimestamp = uint40(block.timestamp);
                      reserveCache.reserveLastUpdateTimestamp = uint40(block.timestamp);
                    }
                    /**
                     * @notice Accumulates a predefined amount of asset to the reserve as a fixed, instantaneous income. Used for example
                     * to accumulate the flashloan fee to the reserve, and spread it between all the suppliers.
                     * @param reserve The reserve object
                     * @param totalLiquidity The total liquidity available in the reserve
                     * @param amount The amount to accumulate
                     * @return The next liquidity index of the reserve
                     */
                    function cumulateToLiquidityIndex(
                      DataTypes.ReserveData storage reserve,
                      uint256 totalLiquidity,
                      uint256 amount
                    ) internal returns (uint256) {
                      //next liquidity index is calculated this way: `((amount / totalLiquidity) + 1) * liquidityIndex`
                      //division `amount / totalLiquidity` done in ray for precision
                      uint256 result = (amount.wadToRay().rayDiv(totalLiquidity.wadToRay()) + WadRayMath.RAY).rayMul(
                        reserve.liquidityIndex
                      );
                      reserve.liquidityIndex = result.toUint128();
                      return result;
                    }
                    /**
                     * @notice Initializes a reserve.
                     * @param reserve The reserve object
                     * @param aTokenAddress The address of the overlying atoken contract
                     * @param variableDebtTokenAddress The address of the overlying variable debt token contract
                     * @param interestRateStrategyAddress The address of the interest rate strategy contract
                     */
                    function init(
                      DataTypes.ReserveData storage reserve,
                      address aTokenAddress,
                      address variableDebtTokenAddress,
                      address interestRateStrategyAddress
                    ) internal {
                      require(reserve.aTokenAddress == address(0), Errors.RESERVE_ALREADY_INITIALIZED);
                      reserve.liquidityIndex = uint128(WadRayMath.RAY);
                      reserve.variableBorrowIndex = uint128(WadRayMath.RAY);
                      reserve.aTokenAddress = aTokenAddress;
                      reserve.variableDebtTokenAddress = variableDebtTokenAddress;
                      reserve.interestRateStrategyAddress = interestRateStrategyAddress;
                    }
                    /**
                     * @notice Updates the reserve current variable borrow rate and the current liquidity rate.
                     * @param reserve The reserve reserve to be updated
                     * @param reserveCache The caching layer for the reserve data
                     * @param reserveAddress The address of the reserve to be updated
                     * @param liquidityAdded The amount of liquidity added to the protocol (supply or repay) in the previous action
                     * @param liquidityTaken The amount of liquidity taken from the protocol (redeem or borrow)
                     */
                    function updateInterestRatesAndVirtualBalance(
                      DataTypes.ReserveData storage reserve,
                      DataTypes.ReserveCache memory reserveCache,
                      address reserveAddress,
                      uint256 liquidityAdded,
                      uint256 liquidityTaken
                    ) internal {
                      uint256 totalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul(
                        reserveCache.nextVariableBorrowIndex
                      );
                      (uint256 nextLiquidityRate, uint256 nextVariableRate) = IReserveInterestRateStrategy(
                        reserve.interestRateStrategyAddress
                      ).calculateInterestRates(
                          DataTypes.CalculateInterestRatesParams({
                            unbacked: reserve.unbacked + reserve.deficit,
                            liquidityAdded: liquidityAdded,
                            liquidityTaken: liquidityTaken,
                            totalDebt: totalVariableDebt,
                            reserveFactor: reserveCache.reserveFactor,
                            reserve: reserveAddress,
                            usingVirtualBalance: reserveCache.reserveConfiguration.getIsVirtualAccActive(),
                            virtualUnderlyingBalance: reserve.virtualUnderlyingBalance
                          })
                        );
                      reserve.currentLiquidityRate = nextLiquidityRate.toUint128();
                      reserve.currentVariableBorrowRate = nextVariableRate.toUint128();
                      // Only affect virtual balance if the reserve uses it
                      if (reserveCache.reserveConfiguration.getIsVirtualAccActive()) {
                        if (liquidityAdded > 0) {
                          reserve.virtualUnderlyingBalance += liquidityAdded.toUint128();
                        }
                        if (liquidityTaken > 0) {
                          reserve.virtualUnderlyingBalance -= liquidityTaken.toUint128();
                        }
                      }
                      emit ReserveDataUpdated(
                        reserveAddress,
                        nextLiquidityRate,
                        0,
                        nextVariableRate,
                        reserveCache.nextLiquidityIndex,
                        reserveCache.nextVariableBorrowIndex
                      );
                    }
                    /**
                     * @notice Mints part of the repaid interest to the reserve treasury as a function of the reserve factor for the
                     * specific asset.
                     * @param reserve The reserve to be updated
                     * @param reserveCache The caching layer for the reserve data
                     */
                    function _accrueToTreasury(
                      DataTypes.ReserveData storage reserve,
                      DataTypes.ReserveCache memory reserveCache
                    ) internal {
                      if (reserveCache.reserveFactor == 0) {
                        return;
                      }
                      //calculate the total variable debt at moment of the last interaction
                      uint256 prevTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                        reserveCache.currVariableBorrowIndex
                      );
                      //calculate the new total variable debt after accumulation of the interest on the index
                      uint256 currTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                        reserveCache.nextVariableBorrowIndex
                      );
                      //debt accrued is the sum of the current debt minus the sum of the debt at the last update
                      uint256 totalDebtAccrued = currTotalVariableDebt - prevTotalVariableDebt;
                      uint256 amountToMint = totalDebtAccrued.percentMul(reserveCache.reserveFactor);
                      if (amountToMint != 0) {
                        reserve.accruedToTreasury += amountToMint.rayDiv(reserveCache.nextLiquidityIndex).toUint128();
                      }
                    }
                    /**
                     * @notice Updates the reserve indexes and the timestamp of the update.
                     * @param reserve The reserve reserve to be updated
                     * @param reserveCache The cache layer holding the cached protocol data
                     */
                    function _updateIndexes(
                      DataTypes.ReserveData storage reserve,
                      DataTypes.ReserveCache memory reserveCache
                    ) internal {
                      // Only cumulating on the supply side if there is any income being produced
                      // The case of Reserve Factor 100% is not a problem (currentLiquidityRate == 0),
                      // as liquidity index should not be updated
                      if (reserveCache.currLiquidityRate != 0) {
                        uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(
                          reserveCache.currLiquidityRate,
                          reserveCache.reserveLastUpdateTimestamp
                        );
                        reserveCache.nextLiquidityIndex = cumulatedLiquidityInterest.rayMul(
                          reserveCache.currLiquidityIndex
                        );
                        reserve.liquidityIndex = reserveCache.nextLiquidityIndex.toUint128();
                      }
                      // Variable borrow index only gets updated if there is any variable debt.
                      // reserveCache.currVariableBorrowRate != 0 is not a correct validation,
                      // because a positive base variable rate can be stored on
                      // reserveCache.currVariableBorrowRate, but the index should not increase
                      if (reserveCache.currScaledVariableDebt != 0) {
                        uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest(
                          reserveCache.currVariableBorrowRate,
                          reserveCache.reserveLastUpdateTimestamp
                        );
                        reserveCache.nextVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(
                          reserveCache.currVariableBorrowIndex
                        );
                        reserve.variableBorrowIndex = reserveCache.nextVariableBorrowIndex.toUint128();
                      }
                    }
                    /**
                     * @notice Creates a cache object to avoid repeated storage reads and external contract calls when updating state and
                     * interest rates.
                     * @param reserve The reserve object for which the cache will be filled
                     * @return The cache object
                     */
                    function cache(
                      DataTypes.ReserveData storage reserve
                    ) internal view returns (DataTypes.ReserveCache memory) {
                      DataTypes.ReserveCache memory reserveCache;
                      reserveCache.reserveConfiguration = reserve.configuration;
                      reserveCache.reserveFactor = reserveCache.reserveConfiguration.getReserveFactor();
                      reserveCache.currLiquidityIndex = reserveCache.nextLiquidityIndex = reserve.liquidityIndex;
                      reserveCache.currVariableBorrowIndex = reserveCache.nextVariableBorrowIndex = reserve
                        .variableBorrowIndex;
                      reserveCache.currLiquidityRate = reserve.currentLiquidityRate;
                      reserveCache.currVariableBorrowRate = reserve.currentVariableBorrowRate;
                      reserveCache.aTokenAddress = reserve.aTokenAddress;
                      reserveCache.variableDebtTokenAddress = reserve.variableDebtTokenAddress;
                      reserveCache.reserveLastUpdateTimestamp = reserve.lastUpdateTimestamp;
                      reserveCache.currScaledVariableDebt = reserveCache.nextScaledVariableDebt = IVariableDebtToken(
                        reserveCache.variableDebtTokenAddress
                      ).scaledTotalSupply();
                      return reserveCache;
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {IAToken} from '../../../interfaces/IAToken.sol';
                  import {Errors} from '../helpers/Errors.sol';
                  import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {ValidationLogic} from './ValidationLogic.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  /**
                   * @title SupplyLogic library
                   * @author Aave
                   * @notice Implements the base logic for supply/withdraw
                   */
                  library SupplyLogic {
                    using ReserveLogic for DataTypes.ReserveCache;
                    using ReserveLogic for DataTypes.ReserveData;
                    using GPv2SafeERC20 for IERC20;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    // See `IPool` for descriptions
                    event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                    event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                    event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
                    event Supply(
                      address indexed reserve,
                      address user,
                      address indexed onBehalfOf,
                      uint256 amount,
                      uint16 indexed referralCode
                    );
                    /**
                     * @notice Implements the supply feature. Through `supply()`, users supply assets to the Aave protocol.
                     * @dev Emits the `Supply()` event.
                     * @dev In the first supply action, `ReserveUsedAsCollateralEnabled()` is emitted, if the asset can be enabled as
                     * collateral.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the supply function
                     */
                    function executeSupply(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ExecuteSupplyParams memory params
                    ) external {
                      DataTypes.ReserveData storage reserve = reservesData[params.asset];
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      reserve.updateState(reserveCache);
                      ValidationLogic.validateSupply(reserveCache, reserve, params.amount, params.onBehalfOf);
                      reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, params.amount, 0);
                      IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, params.amount);
                      bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
                        msg.sender,
                        params.onBehalfOf,
                        params.amount,
                        reserveCache.nextLiquidityIndex
                      );
                      if (isFirstSupply) {
                        if (
                          ValidationLogic.validateAutomaticUseAsCollateral(
                            reservesData,
                            reservesList,
                            userConfig,
                            reserveCache.reserveConfiguration,
                            reserveCache.aTokenAddress
                          )
                        ) {
                          userConfig.setUsingAsCollateral(reserve.id, true);
                          emit ReserveUsedAsCollateralEnabled(params.asset, params.onBehalfOf);
                        }
                      }
                      emit Supply(params.asset, msg.sender, params.onBehalfOf, params.amount, params.referralCode);
                    }
                    /**
                     * @notice Implements the withdraw feature. Through `withdraw()`, users redeem their aTokens for the underlying asset
                     * previously supplied in the Aave protocol.
                     * @dev Emits the `Withdraw()` event.
                     * @dev If the user withdraws everything, `ReserveUsedAsCollateralDisabled()` is emitted.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the withdraw function
                     * @return The actual amount withdrawn
                     */
                    function executeWithdraw(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ExecuteWithdrawParams memory params
                    ) external returns (uint256) {
                      DataTypes.ReserveData storage reserve = reservesData[params.asset];
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      require(params.to != reserveCache.aTokenAddress, Errors.WITHDRAW_TO_ATOKEN);
                      reserve.updateState(reserveCache);
                      uint256 userBalance = IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul(
                        reserveCache.nextLiquidityIndex
                      );
                      uint256 amountToWithdraw = params.amount;
                      if (params.amount == type(uint256).max) {
                        amountToWithdraw = userBalance;
                      }
                      ValidationLogic.validateWithdraw(reserveCache, amountToWithdraw, userBalance);
                      reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, 0, amountToWithdraw);
                      bool isCollateral = userConfig.isUsingAsCollateral(reserve.id);
                      if (isCollateral && amountToWithdraw == userBalance) {
                        userConfig.setUsingAsCollateral(reserve.id, false);
                        emit ReserveUsedAsCollateralDisabled(params.asset, msg.sender);
                      }
                      IAToken(reserveCache.aTokenAddress).burn(
                        msg.sender,
                        params.to,
                        amountToWithdraw,
                        reserveCache.nextLiquidityIndex
                      );
                      if (isCollateral && userConfig.isBorrowingAny()) {
                        ValidationLogic.validateHFAndLtv(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          userConfig,
                          params.asset,
                          msg.sender,
                          params.reservesCount,
                          params.oracle,
                          params.userEModeCategory
                        );
                      }
                      emit Withdraw(params.asset, msg.sender, params.to, amountToWithdraw);
                      return amountToWithdraw;
                    }
                    /**
                     * @notice Validates a transfer of aTokens. The sender is subjected to health factor validation to avoid
                     * collateralization constraints violation.
                     * @dev Emits the `ReserveUsedAsCollateralEnabled()` event for the `to` account, if the asset is being activated as
                     * collateral.
                     * @dev In case the `from` user transfers everything, `ReserveUsedAsCollateralDisabled()` is emitted for `from`.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param usersConfig The users configuration mapping that track the supplied/borrowed assets
                     * @param params The additional parameters needed to execute the finalizeTransfer function
                     */
                    function executeFinalizeTransfer(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      mapping(address => DataTypes.UserConfigurationMap) storage usersConfig,
                      DataTypes.FinalizeTransferParams memory params
                    ) external {
                      DataTypes.ReserveData storage reserve = reservesData[params.asset];
                      ValidationLogic.validateTransfer(reserve);
                      uint256 reserveId = reserve.id;
                      uint256 scaledAmount = params.amount.rayDiv(reserve.getNormalizedIncome());
                      if (params.from != params.to && scaledAmount != 0) {
                        DataTypes.UserConfigurationMap storage fromConfig = usersConfig[params.from];
                        if (fromConfig.isUsingAsCollateral(reserveId)) {
                          if (fromConfig.isBorrowingAny()) {
                            ValidationLogic.validateHFAndLtv(
                              reservesData,
                              reservesList,
                              eModeCategories,
                              usersConfig[params.from],
                              params.asset,
                              params.from,
                              params.reservesCount,
                              params.oracle,
                              params.fromEModeCategory
                            );
                          }
                          if (params.balanceFromBefore == params.amount) {
                            fromConfig.setUsingAsCollateral(reserveId, false);
                            emit ReserveUsedAsCollateralDisabled(params.asset, params.from);
                          }
                        }
                        if (params.balanceToBefore == 0) {
                          DataTypes.UserConfigurationMap storage toConfig = usersConfig[params.to];
                          if (
                            ValidationLogic.validateAutomaticUseAsCollateral(
                              reservesData,
                              reservesList,
                              toConfig,
                              reserve.configuration,
                              reserve.aTokenAddress
                            )
                          ) {
                            toConfig.setUsingAsCollateral(reserveId, true);
                            emit ReserveUsedAsCollateralEnabled(params.asset, params.to);
                          }
                        }
                      }
                    }
                    /**
                     * @notice Executes the 'set as collateral' feature. A user can choose to activate or deactivate an asset as
                     * collateral at any point in time. Deactivating an asset as collateral is subjected to the usual health factor
                     * checks to ensure collateralization.
                     * @dev Emits the `ReserveUsedAsCollateralEnabled()` event if the asset can be activated as collateral.
                     * @dev In case the asset is being deactivated as collateral, `ReserveUsedAsCollateralDisabled()` is emitted.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param userConfig The users configuration mapping that track the supplied/borrowed assets
                     * @param asset The address of the asset being configured as collateral
                     * @param useAsCollateral True if the user wants to set the asset as collateral, false otherwise
                     * @param reservesCount The number of initialized reserves
                     * @param priceOracle The address of the price oracle
                     * @param userEModeCategory The eMode category chosen by the user
                     */
                    function executeUseReserveAsCollateral(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap storage userConfig,
                      address asset,
                      bool useAsCollateral,
                      uint256 reservesCount,
                      address priceOracle,
                      uint8 userEModeCategory
                    ) external {
                      DataTypes.ReserveData storage reserve = reservesData[asset];
                      DataTypes.ReserveCache memory reserveCache = reserve.cache();
                      uint256 userBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender);
                      ValidationLogic.validateSetUseReserveAsCollateral(reserveCache, userBalance);
                      if (useAsCollateral == userConfig.isUsingAsCollateral(reserve.id)) return;
                      if (useAsCollateral) {
                        require(
                          ValidationLogic.validateUseAsCollateral(
                            reservesData,
                            reservesList,
                            userConfig,
                            reserveCache.reserveConfiguration
                          ),
                          Errors.USER_IN_ISOLATION_MODE_OR_LTV_ZERO
                        );
                        userConfig.setUsingAsCollateral(reserve.id, true);
                        emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
                      } else {
                        userConfig.setUsingAsCollateral(reserve.id, false);
                        ValidationLogic.validateHFAndLtv(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          userConfig,
                          asset,
                          msg.sender,
                          reservesCount,
                          priceOracle,
                          userEModeCategory
                        );
                        emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
                      }
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.10;
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {Address} from '../../../dependencies/openzeppelin/contracts/Address.sol';
                  import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                  import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                  import {IAToken} from '../../../interfaces/IAToken.sol';
                  import {IPriceOracleSentinel} from '../../../interfaces/IPriceOracleSentinel.sol';
                  import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                  import {IAccessControl} from '../../../dependencies/openzeppelin/contracts/IAccessControl.sol';
                  import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                  import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                  import {EModeConfiguration} from '../configuration/EModeConfiguration.sol';
                  import {Errors} from '../helpers/Errors.sol';
                  import {WadRayMath} from '../math/WadRayMath.sol';
                  import {PercentageMath} from '../math/PercentageMath.sol';
                  import {DataTypes} from '../types/DataTypes.sol';
                  import {ReserveLogic} from './ReserveLogic.sol';
                  import {GenericLogic} from './GenericLogic.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  import {IncentivizedERC20} from '../../tokenization/base/IncentivizedERC20.sol';
                  /**
                   * @title ValidationLogic library
                   * @author Aave
                   * @notice Implements functions to validate the different actions of the protocol
                   */
                  library ValidationLogic {
                    using ReserveLogic for DataTypes.ReserveData;
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    using SafeCast for uint256;
                    using GPv2SafeERC20 for IERC20;
                    using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                    using UserConfiguration for DataTypes.UserConfigurationMap;
                    using Address for address;
                    // Factor to apply to "only-variable-debt" liquidity rate to get threshold for rebalancing, expressed in bps
                    // A value of 0.9e4 results in 90%
                    uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 0.9e4;
                    // Minimum health factor allowed under any circumstance
                    // A value of 0.95e18 results in 0.95
                    uint256 public constant MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 0.95e18;
                    /**
                     * @dev Minimum health factor to consider a user position healthy
                     * A value of 1e18 results in 1
                     */
                    uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1e18;
                    /**
                     * @dev Role identifier for the role allowed to supply isolated reserves as collateral
                     */
                    bytes32 public constant ISOLATED_COLLATERAL_SUPPLIER_ROLE =
                      keccak256('ISOLATED_COLLATERAL_SUPPLIER');
                    /**
                     * @notice Validates a supply action.
                     * @param reserveCache The cached data of the reserve
                     * @param amount The amount to be supplied
                     */
                    function validateSupply(
                      DataTypes.ReserveCache memory reserveCache,
                      DataTypes.ReserveData storage reserve,
                      uint256 amount,
                      address onBehalfOf
                    ) internal view {
                      require(amount != 0, Errors.INVALID_AMOUNT);
                      (bool isActive, bool isFrozen, , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                      require(isActive, Errors.RESERVE_INACTIVE);
                      require(!isPaused, Errors.RESERVE_PAUSED);
                      require(!isFrozen, Errors.RESERVE_FROZEN);
                      require(onBehalfOf != reserveCache.aTokenAddress, Errors.SUPPLY_TO_ATOKEN);
                      uint256 supplyCap = reserveCache.reserveConfiguration.getSupplyCap();
                      require(
                        supplyCap == 0 ||
                          ((IAToken(reserveCache.aTokenAddress).scaledTotalSupply() +
                            uint256(reserve.accruedToTreasury)).rayMul(reserveCache.nextLiquidityIndex) + amount) <=
                          supplyCap * (10 ** reserveCache.reserveConfiguration.getDecimals()),
                        Errors.SUPPLY_CAP_EXCEEDED
                      );
                    }
                    /**
                     * @notice Validates a withdraw action.
                     * @param reserveCache The cached data of the reserve
                     * @param amount The amount to be withdrawn
                     * @param userBalance The balance of the user
                     */
                    function validateWithdraw(
                      DataTypes.ReserveCache memory reserveCache,
                      uint256 amount,
                      uint256 userBalance
                    ) internal pure {
                      require(amount != 0, Errors.INVALID_AMOUNT);
                      require(amount <= userBalance, Errors.NOT_ENOUGH_AVAILABLE_USER_BALANCE);
                      (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                      require(isActive, Errors.RESERVE_INACTIVE);
                      require(!isPaused, Errors.RESERVE_PAUSED);
                    }
                    struct ValidateBorrowLocalVars {
                      uint256 currentLtv;
                      uint256 collateralNeededInBaseCurrency;
                      uint256 userCollateralInBaseCurrency;
                      uint256 userDebtInBaseCurrency;
                      uint256 availableLiquidity;
                      uint256 healthFactor;
                      uint256 totalDebt;
                      uint256 totalSupplyVariableDebt;
                      uint256 reserveDecimals;
                      uint256 borrowCap;
                      uint256 amountInBaseCurrency;
                      uint256 assetUnit;
                      address siloedBorrowingAddress;
                      bool isActive;
                      bool isFrozen;
                      bool isPaused;
                      bool borrowingEnabled;
                      bool siloedBorrowingEnabled;
                    }
                    /**
                     * @notice Validates a borrow action.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param params Additional params needed for the validation
                     */
                    function validateBorrow(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.ValidateBorrowParams memory params
                    ) internal view {
                      require(params.amount != 0, Errors.INVALID_AMOUNT);
                      ValidateBorrowLocalVars memory vars;
                      (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.isPaused) = params
                        .reserveCache
                        .reserveConfiguration
                        .getFlags();
                      require(vars.isActive, Errors.RESERVE_INACTIVE);
                      require(!vars.isPaused, Errors.RESERVE_PAUSED);
                      require(!vars.isFrozen, Errors.RESERVE_FROZEN);
                      require(vars.borrowingEnabled, Errors.BORROWING_NOT_ENABLED);
                      require(
                        !params.reserveCache.reserveConfiguration.getIsVirtualAccActive() ||
                          IERC20(params.reserveCache.aTokenAddress).totalSupply() >= params.amount,
                        Errors.INVALID_AMOUNT
                      );
                      require(
                        params.priceOracleSentinel == address(0) ||
                          IPriceOracleSentinel(params.priceOracleSentinel).isBorrowAllowed(),
                        Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
                      );
                      //validate interest rate mode
                      require(
                        params.interestRateMode == DataTypes.InterestRateMode.VARIABLE,
                        Errors.INVALID_INTEREST_RATE_MODE_SELECTED
                      );
                      vars.reserveDecimals = params.reserveCache.reserveConfiguration.getDecimals();
                      vars.borrowCap = params.reserveCache.reserveConfiguration.getBorrowCap();
                      unchecked {
                        vars.assetUnit = 10 ** vars.reserveDecimals;
                      }
                      if (vars.borrowCap != 0) {
                        vars.totalSupplyVariableDebt = params.reserveCache.currScaledVariableDebt.rayMul(
                          params.reserveCache.nextVariableBorrowIndex
                        );
                        vars.totalDebt = vars.totalSupplyVariableDebt + params.amount;
                        unchecked {
                          require(vars.totalDebt <= vars.borrowCap * vars.assetUnit, Errors.BORROW_CAP_EXCEEDED);
                        }
                      }
                      if (params.isolationModeActive) {
                        // check that the asset being borrowed is borrowable in isolation mode AND
                        // the total exposure is no bigger than the collateral debt ceiling
                        require(
                          params.reserveCache.reserveConfiguration.getBorrowableInIsolation(),
                          Errors.ASSET_NOT_BORROWABLE_IN_ISOLATION
                        );
                        require(
                          reservesData[params.isolationModeCollateralAddress].isolationModeTotalDebt +
                            (params.amount /
                              10 ** (vars.reserveDecimals - ReserveConfiguration.DEBT_CEILING_DECIMALS))
                              .toUint128() <=
                            params.isolationModeDebtCeiling,
                          Errors.DEBT_CEILING_EXCEEDED
                        );
                      }
                      if (params.userEModeCategory != 0) {
                        require(
                          EModeConfiguration.isReserveEnabledOnBitmap(
                            eModeCategories[params.userEModeCategory].borrowableBitmap,
                            reservesData[params.asset].id
                          ),
                          Errors.NOT_BORROWABLE_IN_EMODE
                        );
                      }
                      (
                        vars.userCollateralInBaseCurrency,
                        vars.userDebtInBaseCurrency,
                        vars.currentLtv,
                        ,
                        vars.healthFactor,
                      ) = GenericLogic.calculateUserAccountData(
                        reservesData,
                        reservesList,
                        eModeCategories,
                        DataTypes.CalculateUserAccountDataParams({
                          userConfig: params.userConfig,
                          reservesCount: params.reservesCount,
                          user: params.userAddress,
                          oracle: params.oracle,
                          userEModeCategory: params.userEModeCategory
                        })
                      );
                      require(vars.userCollateralInBaseCurrency != 0, Errors.COLLATERAL_BALANCE_IS_ZERO);
                      require(vars.currentLtv != 0, Errors.LTV_VALIDATION_FAILED);
                      require(
                        vars.healthFactor > HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                        Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
                      );
                      vars.amountInBaseCurrency =
                        IPriceOracleGetter(params.oracle).getAssetPrice(params.asset) *
                        params.amount;
                      unchecked {
                        vars.amountInBaseCurrency /= vars.assetUnit;
                      }
                      //add the current already borrowed amount to the amount requested to calculate the total collateral needed.
                      vars.collateralNeededInBaseCurrency = (vars.userDebtInBaseCurrency + vars.amountInBaseCurrency)
                        .percentDiv(vars.currentLtv); //LTV is calculated in percentage
                      require(
                        vars.collateralNeededInBaseCurrency <= vars.userCollateralInBaseCurrency,
                        Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW
                      );
                      if (params.userConfig.isBorrowingAny()) {
                        (vars.siloedBorrowingEnabled, vars.siloedBorrowingAddress) = params
                          .userConfig
                          .getSiloedBorrowingState(reservesData, reservesList);
                        if (vars.siloedBorrowingEnabled) {
                          require(vars.siloedBorrowingAddress == params.asset, Errors.SILOED_BORROWING_VIOLATION);
                        } else {
                          require(
                            !params.reserveCache.reserveConfiguration.getSiloedBorrowing(),
                            Errors.SILOED_BORROWING_VIOLATION
                          );
                        }
                      }
                    }
                    /**
                     * @notice Validates a repay action.
                     * @param reserveCache The cached data of the reserve
                     * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1)
                     * @param onBehalfOf The address of the user msg.sender is repaying for
                     * @param debt The borrow balance of the user
                     */
                    function validateRepay(
                      DataTypes.ReserveCache memory reserveCache,
                      uint256 amountSent,
                      DataTypes.InterestRateMode interestRateMode,
                      address onBehalfOf,
                      uint256 debt
                    ) internal view {
                      require(amountSent != 0, Errors.INVALID_AMOUNT);
                      require(
                        interestRateMode == DataTypes.InterestRateMode.VARIABLE,
                        Errors.INVALID_INTEREST_RATE_MODE_SELECTED
                      );
                      require(
                        amountSent != type(uint256).max || msg.sender == onBehalfOf,
                        Errors.NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF
                      );
                      (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                      require(isActive, Errors.RESERVE_INACTIVE);
                      require(!isPaused, Errors.RESERVE_PAUSED);
                      require(debt != 0, Errors.NO_DEBT_OF_SELECTED_TYPE);
                    }
                    /**
                     * @notice Validates the action of setting an asset as collateral.
                     * @param reserveCache The cached data of the reserve
                     * @param userBalance The balance of the user
                     */
                    function validateSetUseReserveAsCollateral(
                      DataTypes.ReserveCache memory reserveCache,
                      uint256 userBalance
                    ) internal pure {
                      require(userBalance != 0, Errors.UNDERLYING_BALANCE_ZERO);
                      (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                      require(isActive, Errors.RESERVE_INACTIVE);
                      require(!isPaused, Errors.RESERVE_PAUSED);
                    }
                    /**
                     * @notice Validates a flashloan action.
                     * @param reservesData The state of all the reserves
                     * @param assets The assets being flash-borrowed
                     * @param amounts The amounts for each asset being borrowed
                     */
                    function validateFlashloan(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      address[] memory assets,
                      uint256[] memory amounts
                    ) internal view {
                      require(assets.length == amounts.length, Errors.INCONSISTENT_FLASHLOAN_PARAMS);
                      for (uint256 i = 0; i < assets.length; i++) {
                        for (uint256 j = i + 1; j < assets.length; j++) {
                          require(assets[i] != assets[j], Errors.INCONSISTENT_FLASHLOAN_PARAMS);
                        }
                        validateFlashloanSimple(reservesData[assets[i]], amounts[i]);
                      }
                    }
                    /**
                     * @notice Validates a flashloan action.
                     * @param reserve The state of the reserve
                     */
                    function validateFlashloanSimple(
                      DataTypes.ReserveData storage reserve,
                      uint256 amount
                    ) internal view {
                      DataTypes.ReserveConfigurationMap memory configuration = reserve.configuration;
                      require(!configuration.getPaused(), Errors.RESERVE_PAUSED);
                      require(configuration.getActive(), Errors.RESERVE_INACTIVE);
                      require(configuration.getFlashLoanEnabled(), Errors.FLASHLOAN_DISABLED);
                      require(
                        !configuration.getIsVirtualAccActive() ||
                          IERC20(reserve.aTokenAddress).totalSupply() >= amount,
                        Errors.INVALID_AMOUNT
                      );
                    }
                    struct ValidateLiquidationCallLocalVars {
                      bool collateralReserveActive;
                      bool collateralReservePaused;
                      bool principalReserveActive;
                      bool principalReservePaused;
                      bool isCollateralEnabled;
                    }
                    /**
                     * @notice Validates the liquidation action.
                     * @param userConfig The user configuration mapping
                     * @param collateralReserve The reserve data of the collateral
                     * @param debtReserve The reserve data of the debt
                     * @param params Additional parameters needed for the validation
                     */
                    function validateLiquidationCall(
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ReserveData storage collateralReserve,
                      DataTypes.ReserveData storage debtReserve,
                      DataTypes.ValidateLiquidationCallParams memory params
                    ) internal view {
                      ValidateLiquidationCallLocalVars memory vars;
                      (vars.collateralReserveActive, , , vars.collateralReservePaused) = collateralReserve
                        .configuration
                        .getFlags();
                      (vars.principalReserveActive, , , vars.principalReservePaused) = params
                        .debtReserveCache
                        .reserveConfiguration
                        .getFlags();
                      require(vars.collateralReserveActive && vars.principalReserveActive, Errors.RESERVE_INACTIVE);
                      require(!vars.collateralReservePaused && !vars.principalReservePaused, Errors.RESERVE_PAUSED);
                      require(
                        params.priceOracleSentinel == address(0) ||
                          params.healthFactor < MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD ||
                          IPriceOracleSentinel(params.priceOracleSentinel).isLiquidationAllowed(),
                        Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
                      );
                      require(
                        collateralReserve.liquidationGracePeriodUntil < uint40(block.timestamp) &&
                          debtReserve.liquidationGracePeriodUntil < uint40(block.timestamp),
                        Errors.LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED
                      );
                      require(
                        params.healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                        Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD
                      );
                      vars.isCollateralEnabled =
                        collateralReserve.configuration.getLiquidationThreshold() != 0 &&
                        userConfig.isUsingAsCollateral(collateralReserve.id);
                      //if collateral isn't enabled as collateral by user, it cannot be liquidated
                      require(vars.isCollateralEnabled, Errors.COLLATERAL_CANNOT_BE_LIQUIDATED);
                      require(params.totalDebt != 0, Errors.SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER);
                    }
                    /**
                     * @notice Validates the health factor of a user.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param userConfig The state of the user for the specific reserve
                     * @param user The user to validate health factor of
                     * @param userEModeCategory The users active efficiency mode category
                     * @param reservesCount The number of available reserves
                     * @param oracle The price oracle
                     */
                    function validateHealthFactor(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap memory userConfig,
                      address user,
                      uint8 userEModeCategory,
                      uint256 reservesCount,
                      address oracle
                    ) internal view returns (uint256, bool) {
                      (, , , , uint256 healthFactor, bool hasZeroLtvCollateral) = GenericLogic
                        .calculateUserAccountData(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          DataTypes.CalculateUserAccountDataParams({
                            userConfig: userConfig,
                            reservesCount: reservesCount,
                            user: user,
                            oracle: oracle,
                            userEModeCategory: userEModeCategory
                          })
                        );
                      require(
                        healthFactor >= HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                        Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
                      );
                      return (healthFactor, hasZeroLtvCollateral);
                    }
                    /**
                     * @notice Validates the health factor of a user and the ltv of the asset being withdrawn.
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param eModeCategories The configuration of all the efficiency mode categories
                     * @param userConfig The state of the user for the specific reserve
                     * @param asset The asset for which the ltv will be validated
                     * @param from The user from which the aTokens are being transferred
                     * @param reservesCount The number of available reserves
                     * @param oracle The price oracle
                     * @param userEModeCategory The users active efficiency mode category
                     */
                    function validateHFAndLtv(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap memory userConfig,
                      address asset,
                      address from,
                      uint256 reservesCount,
                      address oracle,
                      uint8 userEModeCategory
                    ) internal view {
                      DataTypes.ReserveData memory reserve = reservesData[asset];
                      (, bool hasZeroLtvCollateral) = validateHealthFactor(
                        reservesData,
                        reservesList,
                        eModeCategories,
                        userConfig,
                        from,
                        userEModeCategory,
                        reservesCount,
                        oracle
                      );
                      require(
                        !hasZeroLtvCollateral || reserve.configuration.getLtv() == 0,
                        Errors.LTV_VALIDATION_FAILED
                      );
                    }
                    /**
                     * @notice Validates a transfer action.
                     * @param reserve The reserve object
                     */
                    function validateTransfer(DataTypes.ReserveData storage reserve) internal view {
                      require(!reserve.configuration.getPaused(), Errors.RESERVE_PAUSED);
                    }
                    /**
                     * @notice Validates a drop reserve action.
                     * @param reservesList The addresses of all the active reserves
                     * @param reserve The reserve object
                     * @param asset The address of the reserve's underlying asset
                     */
                    function validateDropReserve(
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.ReserveData storage reserve,
                      address asset
                    ) internal view {
                      require(asset != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                      require(reserve.id != 0 || reservesList[0] == asset, Errors.ASSET_NOT_LISTED);
                      require(
                        IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0,
                        Errors.VARIABLE_DEBT_SUPPLY_NOT_ZERO
                      );
                      require(
                        IERC20(reserve.aTokenAddress).totalSupply() == 0 && reserve.accruedToTreasury == 0,
                        Errors.UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO
                      );
                    }
                    /**
                     * @notice Validates the action of setting efficiency mode.
                     * @param eModeCategories a mapping storing configurations for all efficiency mode categories
                     * @param userConfig the user configuration
                     * @param reservesCount The total number of valid reserves
                     * @param categoryId The id of the category
                     */
                    function validateSetUserEMode(
                      mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                      DataTypes.UserConfigurationMap memory userConfig,
                      uint256 reservesCount,
                      uint8 categoryId
                    ) internal view {
                      DataTypes.EModeCategory storage eModeCategory = eModeCategories[categoryId];
                      // category is invalid if the liq threshold is not set
                      require(
                        categoryId == 0 || eModeCategory.liquidationThreshold != 0,
                        Errors.INCONSISTENT_EMODE_CATEGORY
                      );
                      // eMode can always be enabled if the user hasn't supplied anything
                      if (userConfig.isEmpty()) {
                        return;
                      }
                      // if user is trying to set another category than default we require that
                      // either the user is not borrowing, or it's borrowing assets of categoryId
                      if (categoryId != 0) {
                        unchecked {
                          for (uint256 i = 0; i < reservesCount; i++) {
                            if (userConfig.isBorrowing(i)) {
                              require(
                                EModeConfiguration.isReserveEnabledOnBitmap(eModeCategory.borrowableBitmap, i),
                                Errors.NOT_BORROWABLE_IN_EMODE
                              );
                            }
                          }
                        }
                      }
                    }
                    /**
                     * @notice Validates the action of activating the asset as collateral.
                     * @dev Only possible if the asset has non-zero LTV and the user is not in isolation mode
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param userConfig the user configuration
                     * @param reserveConfig The reserve configuration
                     * @return True if the asset can be activated as collateral, false otherwise
                     */
                    function validateUseAsCollateral(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ReserveConfigurationMap memory reserveConfig
                    ) internal view returns (bool) {
                      if (reserveConfig.getLtv() == 0) {
                        return false;
                      }
                      if (!userConfig.isUsingAsCollateralAny()) {
                        return true;
                      }
                      (bool isolationModeActive, , ) = userConfig.getIsolationModeState(reservesData, reservesList);
                      return (!isolationModeActive && reserveConfig.getDebtCeiling() == 0);
                    }
                    /**
                     * @notice Validates if an asset should be automatically activated as collateral in the following actions: supply,
                     * transfer, mint unbacked, and liquidate
                     * @dev This is used to ensure that isolated assets are not enabled as collateral automatically
                     * @param reservesData The state of all the reserves
                     * @param reservesList The addresses of all the active reserves
                     * @param userConfig the user configuration
                     * @param reserveConfig The reserve configuration
                     * @return True if the asset can be activated as collateral, false otherwise
                     */
                    function validateAutomaticUseAsCollateral(
                      mapping(address => DataTypes.ReserveData) storage reservesData,
                      mapping(uint256 => address) storage reservesList,
                      DataTypes.UserConfigurationMap storage userConfig,
                      DataTypes.ReserveConfigurationMap memory reserveConfig,
                      address aTokenAddress
                    ) internal view returns (bool) {
                      if (reserveConfig.getDebtCeiling() != 0) {
                        // ensures only the ISOLATED_COLLATERAL_SUPPLIER_ROLE can enable collateral as side-effect of an action
                        IPoolAddressesProvider addressesProvider = IncentivizedERC20(aTokenAddress)
                          .POOL()
                          .ADDRESSES_PROVIDER();
                        if (
                          !IAccessControl(addressesProvider.getACLManager()).hasRole(
                            ISOLATED_COLLATERAL_SUPPLIER_ROLE,
                            msg.sender
                          )
                        ) return false;
                      }
                      return validateUseAsCollateral(reservesData, reservesList, userConfig, reserveConfig);
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  import {WadRayMath} from './WadRayMath.sol';
                  /**
                   * @title MathUtils library
                   * @author Aave
                   * @notice Provides functions to perform linear and compounded interest calculations
                   */
                  library MathUtils {
                    using WadRayMath for uint256;
                    /// @dev Ignoring leap years
                    uint256 internal constant SECONDS_PER_YEAR = 365 days;
                    /**
                     * @dev Function to calculate the interest accumulated using a linear interest rate formula
                     * @param rate The interest rate, in ray
                     * @param lastUpdateTimestamp The timestamp of the last update of the interest
                     * @return The interest rate linearly accumulated during the timeDelta, in ray
                     */
                    function calculateLinearInterest(
                      uint256 rate,
                      uint40 lastUpdateTimestamp
                    ) internal view returns (uint256) {
                      //solium-disable-next-line
                      uint256 result = rate * (block.timestamp - uint256(lastUpdateTimestamp));
                      unchecked {
                        result = result / SECONDS_PER_YEAR;
                      }
                      return WadRayMath.RAY + result;
                    }
                    /**
                     * @dev Function to calculate the interest using a compounded interest rate formula
                     * To avoid expensive exponentiation, the calculation is performed using a binomial approximation:
                     *
                     *  (1+x)^n = 1+n*x+[n/2*(n-1)]*x^2+[n/6*(n-1)*(n-2)*x^3...
                     *
                     * The approximation slightly underpays liquidity providers and undercharges borrowers, with the advantage of great
                     * gas cost reductions. The whitepaper contains reference to the approximation and a table showing the margin of
                     * error per different time periods
                     *
                     * @param rate The interest rate, in ray
                     * @param lastUpdateTimestamp The timestamp of the last update of the interest
                     * @return The interest rate compounded during the timeDelta, in ray
                     */
                    function calculateCompoundedInterest(
                      uint256 rate,
                      uint40 lastUpdateTimestamp,
                      uint256 currentTimestamp
                    ) internal pure returns (uint256) {
                      //solium-disable-next-line
                      uint256 exp = currentTimestamp - uint256(lastUpdateTimestamp);
                      if (exp == 0) {
                        return WadRayMath.RAY;
                      }
                      uint256 expMinusOne;
                      uint256 expMinusTwo;
                      uint256 basePowerTwo;
                      uint256 basePowerThree;
                      unchecked {
                        expMinusOne = exp - 1;
                        expMinusTwo = exp > 2 ? exp - 2 : 0;
                        basePowerTwo = rate.rayMul(rate) / (SECONDS_PER_YEAR * SECONDS_PER_YEAR);
                        basePowerThree = basePowerTwo.rayMul(rate) / SECONDS_PER_YEAR;
                      }
                      uint256 secondTerm = exp * expMinusOne * basePowerTwo;
                      unchecked {
                        secondTerm /= 2;
                      }
                      uint256 thirdTerm = exp * expMinusOne * expMinusTwo * basePowerThree;
                      unchecked {
                        thirdTerm /= 6;
                      }
                      return WadRayMath.RAY + (rate * exp) / SECONDS_PER_YEAR + secondTerm + thirdTerm;
                    }
                    /**
                     * @dev Calculates the compounded interest between the timestamp of the last update and the current block timestamp
                     * @param rate The interest rate (in ray)
                     * @param lastUpdateTimestamp The timestamp from which the interest accumulation needs to be calculated
                     * @return The interest rate compounded between lastUpdateTimestamp and current block timestamp, in ray
                     */
                    function calculateCompoundedInterest(
                      uint256 rate,
                      uint40 lastUpdateTimestamp
                    ) internal view returns (uint256) {
                      return calculateCompoundedInterest(rate, lastUpdateTimestamp, block.timestamp);
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  /**
                   * @title PercentageMath library
                   * @author Aave
                   * @notice Provides functions to perform percentage calculations
                   * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR
                   * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                   */
                  library PercentageMath {
                    // Maximum percentage factor (100.00%)
                    uint256 internal constant PERCENTAGE_FACTOR = 1e4;
                    // Half percentage factor (50.00%)
                    uint256 internal constant HALF_PERCENTAGE_FACTOR = 0.5e4;
                    /**
                     * @notice Executes a percentage multiplication
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param value The value of which the percentage needs to be calculated
                     * @param percentage The percentage of the value to be calculated
                     * @return result value percentmul percentage
                     */
                    function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                      // to avoid overflow, value <= (type(uint256).max - HALF_PERCENTAGE_FACTOR) / percentage
                      assembly {
                        if iszero(
                          or(
                            iszero(percentage),
                            iszero(gt(value, div(sub(not(0), HALF_PERCENTAGE_FACTOR), percentage)))
                          )
                        ) {
                          revert(0, 0)
                        }
                        result := div(add(mul(value, percentage), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
                      }
                    }
                    /**
                     * @notice Executes a percentage division
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param value The value of which the percentage needs to be calculated
                     * @param percentage The percentage of the value to be calculated
                     * @return result value percentdiv percentage
                     */
                    function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                      // to avoid overflow, value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR
                      assembly {
                        if or(
                          iszero(percentage),
                          iszero(iszero(gt(value, div(sub(not(0), div(percentage, 2)), PERCENTAGE_FACTOR))))
                        ) {
                          revert(0, 0)
                        }
                        result := div(add(mul(value, PERCENTAGE_FACTOR), div(percentage, 2)), percentage)
                      }
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  /**
                   * @title WadRayMath library
                   * @author Aave
                   * @notice Provides functions to perform calculations with Wad and Ray units
                   * @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers
                   * with 27 digits of precision)
                   * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                   */
                  library WadRayMath {
                    // HALF_WAD and HALF_RAY expressed with extended notation as constant with operations are not supported in Yul assembly
                    uint256 internal constant WAD = 1e18;
                    uint256 internal constant HALF_WAD = 0.5e18;
                    uint256 internal constant RAY = 1e27;
                    uint256 internal constant HALF_RAY = 0.5e27;
                    uint256 internal constant WAD_RAY_RATIO = 1e9;
                    /**
                     * @dev Multiplies two wad, rounding half up to the nearest wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @param b Wad
                     * @return c = a*b, in wad
                     */
                    function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b
                      assembly {
                        if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, b), HALF_WAD), WAD)
                      }
                    }
                    /**
                     * @dev Divides two wad, rounding half up to the nearest wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @param b Wad
                     * @return c = a/b, in wad
                     */
                    function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - halfB) / WAD
                      assembly {
                        if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, WAD), div(b, 2)), b)
                      }
                    }
                    /**
                     * @notice Multiplies two ray, rounding half up to the nearest ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @param b Ray
                     * @return c = a raymul b
                     */
                    function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - HALF_RAY) / b
                      assembly {
                        if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, b), HALF_RAY), RAY)
                      }
                    }
                    /**
                     * @notice Divides two ray, rounding half up to the nearest ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @param b Ray
                     * @return c = a raydiv b
                     */
                    function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - halfB) / RAY
                      assembly {
                        if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, RAY), div(b, 2)), b)
                      }
                    }
                    /**
                     * @dev Casts ray down to wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @return b = a converted to wad, rounded half up to the nearest wad
                     */
                    function rayToWad(uint256 a) internal pure returns (uint256 b) {
                      assembly {
                        b := div(a, WAD_RAY_RATIO)
                        let remainder := mod(a, WAD_RAY_RATIO)
                        if iszero(lt(remainder, div(WAD_RAY_RATIO, 2))) {
                          b := add(b, 1)
                        }
                      }
                    }
                    /**
                     * @dev Converts wad up to ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @return b = a converted in ray
                     */
                    function wadToRay(uint256 a) internal pure returns (uint256 b) {
                      // to avoid overflow, b/WAD_RAY_RATIO == a
                      assembly {
                        b := mul(a, WAD_RAY_RATIO)
                        if iszero(eq(div(b, WAD_RAY_RATIO), a)) {
                          revert(0, 0)
                        }
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  library DataTypes {
                    /**
                     * This exists specifically to maintain the `getReserveData()` interface, since the new, internal
                     * `ReserveData` struct includes the reserve's `virtualUnderlyingBalance`.
                     */
                    struct ReserveDataLegacy {
                      //stores the reserve configuration
                      ReserveConfigurationMap configuration;
                      //the liquidity index. Expressed in ray
                      uint128 liquidityIndex;
                      //the current supply rate. Expressed in ray
                      uint128 currentLiquidityRate;
                      //variable borrow index. Expressed in ray
                      uint128 variableBorrowIndex;
                      //the current variable borrow rate. Expressed in ray
                      uint128 currentVariableBorrowRate;
                      // DEPRECATED on v3.2.0
                      uint128 currentStableBorrowRate;
                      //timestamp of last update
                      uint40 lastUpdateTimestamp;
                      //the id of the reserve. Represents the position in the list of the active reserves
                      uint16 id;
                      //aToken address
                      address aTokenAddress;
                      // DEPRECATED on v3.2.0
                      address stableDebtTokenAddress;
                      //variableDebtToken address
                      address variableDebtTokenAddress;
                      //address of the interest rate strategy
                      address interestRateStrategyAddress;
                      //the current treasury balance, scaled
                      uint128 accruedToTreasury;
                      //the outstanding unbacked aTokens minted through the bridging feature
                      uint128 unbacked;
                      //the outstanding debt borrowed against this asset in isolation mode
                      uint128 isolationModeTotalDebt;
                    }
                    struct ReserveData {
                      //stores the reserve configuration
                      ReserveConfigurationMap configuration;
                      //the liquidity index. Expressed in ray
                      uint128 liquidityIndex;
                      //the current supply rate. Expressed in ray
                      uint128 currentLiquidityRate;
                      //variable borrow index. Expressed in ray
                      uint128 variableBorrowIndex;
                      //the current variable borrow rate. Expressed in ray
                      uint128 currentVariableBorrowRate;
                      /// @notice reused `__deprecatedStableBorrowRate` storage from pre 3.2
                      // the current accumulate deficit in underlying tokens
                      uint128 deficit;
                      //timestamp of last update
                      uint40 lastUpdateTimestamp;
                      //the id of the reserve. Represents the position in the list of the active reserves
                      uint16 id;
                      //timestamp until when liquidations are not allowed on the reserve, if set to past liquidations will be allowed
                      uint40 liquidationGracePeriodUntil;
                      //aToken address
                      address aTokenAddress;
                      // DEPRECATED on v3.2.0
                      address __deprecatedStableDebtTokenAddress;
                      //variableDebtToken address
                      address variableDebtTokenAddress;
                      //address of the interest rate strategy
                      address interestRateStrategyAddress;
                      //the current treasury balance, scaled
                      uint128 accruedToTreasury;
                      //the outstanding unbacked aTokens minted through the bridging feature
                      uint128 unbacked;
                      //the outstanding debt borrowed against this asset in isolation mode
                      uint128 isolationModeTotalDebt;
                      //the amount of underlying accounted for by the protocol
                      uint128 virtualUnderlyingBalance;
                    }
                    struct ReserveConfigurationMap {
                      //bit 0-15: LTV
                      //bit 16-31: Liq. threshold
                      //bit 32-47: Liq. bonus
                      //bit 48-55: Decimals
                      //bit 56: reserve is active
                      //bit 57: reserve is frozen
                      //bit 58: borrowing is enabled
                      //bit 59: DEPRECATED: stable rate borrowing enabled
                      //bit 60: asset is paused
                      //bit 61: borrowing in isolation mode is enabled
                      //bit 62: siloed borrowing enabled
                      //bit 63: flashloaning enabled
                      //bit 64-79: reserve factor
                      //bit 80-115: borrow cap in whole tokens, borrowCap == 0 => no cap
                      //bit 116-151: supply cap in whole tokens, supplyCap == 0 => no cap
                      //bit 152-167: liquidation protocol fee
                      //bit 168-175: DEPRECATED: eMode category
                      //bit 176-211: unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
                      //bit 212-251: debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
                      //bit 252: virtual accounting is enabled for the reserve
                      //bit 253-255 unused
                      uint256 data;
                    }
                    struct UserConfigurationMap {
                      /**
                       * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.
                       * The first bit indicates if an asset is used as collateral by the user, the second whether an
                       * asset is borrowed by the user.
                       */
                      uint256 data;
                    }
                    // DEPRECATED: kept for backwards compatibility, might be removed in a future version
                    struct EModeCategoryLegacy {
                      // each eMode category has a custom ltv and liquidation threshold
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                      // DEPRECATED
                      address priceSource;
                      string label;
                    }
                    struct CollateralConfig {
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                    }
                    struct EModeCategoryBaseConfiguration {
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                      string label;
                    }
                    struct EModeCategory {
                      // each eMode category has a custom ltv and liquidation threshold
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                      uint128 collateralBitmap;
                      string label;
                      uint128 borrowableBitmap;
                    }
                    enum InterestRateMode {
                      NONE,
                      __DEPRECATED,
                      VARIABLE
                    }
                    struct ReserveCache {
                      uint256 currScaledVariableDebt;
                      uint256 nextScaledVariableDebt;
                      uint256 currLiquidityIndex;
                      uint256 nextLiquidityIndex;
                      uint256 currVariableBorrowIndex;
                      uint256 nextVariableBorrowIndex;
                      uint256 currLiquidityRate;
                      uint256 currVariableBorrowRate;
                      uint256 reserveFactor;
                      ReserveConfigurationMap reserveConfiguration;
                      address aTokenAddress;
                      address variableDebtTokenAddress;
                      uint40 reserveLastUpdateTimestamp;
                    }
                    struct ExecuteLiquidationCallParams {
                      uint256 reservesCount;
                      uint256 debtToCover;
                      address collateralAsset;
                      address debtAsset;
                      address user;
                      bool receiveAToken;
                      address priceOracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                    }
                    struct ExecuteSupplyParams {
                      address asset;
                      uint256 amount;
                      address onBehalfOf;
                      uint16 referralCode;
                    }
                    struct ExecuteBorrowParams {
                      address asset;
                      address user;
                      address onBehalfOf;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      uint16 referralCode;
                      bool releaseUnderlying;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                    }
                    struct ExecuteRepayParams {
                      address asset;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      address onBehalfOf;
                      bool useATokens;
                    }
                    struct ExecuteWithdrawParams {
                      address asset;
                      uint256 amount;
                      address to;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                    }
                    struct ExecuteEliminateDeficitParams {
                      address asset;
                      uint256 amount;
                    }
                    struct ExecuteSetUserEModeParams {
                      uint256 reservesCount;
                      address oracle;
                      uint8 categoryId;
                    }
                    struct FinalizeTransferParams {
                      address asset;
                      address from;
                      address to;
                      uint256 amount;
                      uint256 balanceFromBefore;
                      uint256 balanceToBefore;
                      uint256 reservesCount;
                      address oracle;
                      uint8 fromEModeCategory;
                    }
                    struct FlashloanParams {
                      address receiverAddress;
                      address[] assets;
                      uint256[] amounts;
                      uint256[] interestRateModes;
                      address onBehalfOf;
                      bytes params;
                      uint16 referralCode;
                      uint256 flashLoanPremiumToProtocol;
                      uint256 flashLoanPremiumTotal;
                      uint256 reservesCount;
                      address addressesProvider;
                      address pool;
                      uint8 userEModeCategory;
                      bool isAuthorizedFlashBorrower;
                    }
                    struct FlashloanSimpleParams {
                      address receiverAddress;
                      address asset;
                      uint256 amount;
                      bytes params;
                      uint16 referralCode;
                      uint256 flashLoanPremiumToProtocol;
                      uint256 flashLoanPremiumTotal;
                    }
                    struct FlashLoanRepaymentParams {
                      uint256 amount;
                      uint256 totalPremium;
                      uint256 flashLoanPremiumToProtocol;
                      address asset;
                      address receiverAddress;
                      uint16 referralCode;
                    }
                    struct CalculateUserAccountDataParams {
                      UserConfigurationMap userConfig;
                      uint256 reservesCount;
                      address user;
                      address oracle;
                      uint8 userEModeCategory;
                    }
                    struct ValidateBorrowParams {
                      ReserveCache reserveCache;
                      UserConfigurationMap userConfig;
                      address asset;
                      address userAddress;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                      bool isolationModeActive;
                      address isolationModeCollateralAddress;
                      uint256 isolationModeDebtCeiling;
                    }
                    struct ValidateLiquidationCallParams {
                      ReserveCache debtReserveCache;
                      uint256 totalDebt;
                      uint256 healthFactor;
                      address priceOracleSentinel;
                    }
                    struct CalculateInterestRatesParams {
                      uint256 unbacked;
                      uint256 liquidityAdded;
                      uint256 liquidityTaken;
                      uint256 totalDebt;
                      uint256 reserveFactor;
                      address reserve;
                      bool usingVirtualBalance;
                      uint256 virtualUnderlyingBalance;
                    }
                    struct InitReserveParams {
                      address asset;
                      address aTokenAddress;
                      address variableDebtAddress;
                      address interestRateStrategyAddress;
                      uint16 reservesCount;
                      uint16 maxNumberReserves;
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.10;
                  import {Context} from '../../../dependencies/openzeppelin/contracts/Context.sol';
                  import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {IERC20Detailed} from '../../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
                  import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                  import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                  import {Errors} from '../../libraries/helpers/Errors.sol';
                  import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
                  import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                  import {IPool} from '../../../interfaces/IPool.sol';
                  import {IACLManager} from '../../../interfaces/IACLManager.sol';
                  /**
                   * @title IncentivizedERC20
                   * @author Aave, inspired by the Openzeppelin ERC20 implementation
                   * @notice Basic ERC20 implementation
                   */
                  abstract contract IncentivizedERC20 is Context, IERC20Detailed {
                    using WadRayMath for uint256;
                    using SafeCast for uint256;
                    /**
                     * @dev Only pool admin can call functions marked by this modifier.
                     */
                    modifier onlyPoolAdmin() {
                      IACLManager aclManager = IACLManager(_addressesProvider.getACLManager());
                      require(aclManager.isPoolAdmin(msg.sender), Errors.CALLER_NOT_POOL_ADMIN);
                      _;
                    }
                    /**
                     * @dev Only pool can call functions marked by this modifier.
                     */
                    modifier onlyPool() {
                      require(_msgSender() == address(POOL), Errors.CALLER_MUST_BE_POOL);
                      _;
                    }
                    /**
                     * @dev UserState - additionalData is a flexible field.
                     * ATokens and VariableDebtTokens use this field store the index of the
                     * user's last supply/withdrawal/borrow/repayment.
                     */
                    struct UserState {
                      uint128 balance;
                      uint128 additionalData;
                    }
                    // Map of users address and their state data (userAddress => userStateData)
                    mapping(address => UserState) internal _userState;
                    // Map of allowances (delegator => delegatee => allowanceAmount)
                    mapping(address => mapping(address => uint256)) private _allowances;
                    uint256 internal _totalSupply;
                    string private _name;
                    string private _symbol;
                    uint8 private _decimals;
                    IAaveIncentivesController internal _incentivesController;
                    IPoolAddressesProvider internal immutable _addressesProvider;
                    IPool public immutable POOL;
                    /**
                     * @dev Constructor.
                     * @param pool The reference to the main Pool contract
                     * @param name_ The name of the token
                     * @param symbol_ The symbol of the token
                     * @param decimals_ The number of decimals of the token
                     */
                    constructor(IPool pool, string memory name_, string memory symbol_, uint8 decimals_) {
                      _addressesProvider = pool.ADDRESSES_PROVIDER();
                      _name = name_;
                      _symbol = symbol_;
                      _decimals = decimals_;
                      POOL = pool;
                    }
                    /// @inheritdoc IERC20Detailed
                    function name() public view override returns (string memory) {
                      return _name;
                    }
                    /// @inheritdoc IERC20Detailed
                    function symbol() external view override returns (string memory) {
                      return _symbol;
                    }
                    /// @inheritdoc IERC20Detailed
                    function decimals() external view override returns (uint8) {
                      return _decimals;
                    }
                    /// @inheritdoc IERC20
                    function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                    }
                    /// @inheritdoc IERC20
                    function balanceOf(address account) public view virtual override returns (uint256) {
                      return _userState[account].balance;
                    }
                    /**
                     * @notice Returns the address of the Incentives Controller contract
                     * @return The address of the Incentives Controller
                     */
                    function getIncentivesController() external view virtual returns (IAaveIncentivesController) {
                      return _incentivesController;
                    }
                    /**
                     * @notice Sets a new Incentives Controller
                     * @param controller the new Incentives controller
                     */
                    function setIncentivesController(IAaveIncentivesController controller) external onlyPoolAdmin {
                      _incentivesController = controller;
                    }
                    /// @inheritdoc IERC20
                    function transfer(address recipient, uint256 amount) external virtual override returns (bool) {
                      uint128 castAmount = amount.toUint128();
                      _transfer(_msgSender(), recipient, castAmount);
                      return true;
                    }
                    /// @inheritdoc IERC20
                    function allowance(
                      address owner,
                      address spender
                    ) external view virtual override returns (uint256) {
                      return _allowances[owner][spender];
                    }
                    /// @inheritdoc IERC20
                    function approve(address spender, uint256 amount) external virtual override returns (bool) {
                      _approve(_msgSender(), spender, amount);
                      return true;
                    }
                    /// @inheritdoc IERC20
                    function transferFrom(
                      address sender,
                      address recipient,
                      uint256 amount
                    ) external virtual override returns (bool) {
                      uint128 castAmount = amount.toUint128();
                      _approve(sender, _msgSender(), _allowances[sender][_msgSender()] - castAmount);
                      _transfer(sender, recipient, castAmount);
                      return true;
                    }
                    /**
                     * @notice Increases the allowance of spender to spend _msgSender() tokens
                     * @param spender The user allowed to spend on behalf of _msgSender()
                     * @param addedValue The amount being added to the allowance
                     * @return `true`
                     */
                    function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool) {
                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
                      return true;
                    }
                    /**
                     * @notice Decreases the allowance of spender to spend _msgSender() tokens
                     * @param spender The user allowed to spend on behalf of _msgSender()
                     * @param subtractedValue The amount being subtracted to the allowance
                     * @return `true`
                     */
                    function decreaseAllowance(
                      address spender,
                      uint256 subtractedValue
                    ) external virtual returns (bool) {
                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue);
                      return true;
                    }
                    /**
                     * @notice Transfers tokens between two users and apply incentives if defined.
                     * @param sender The source address
                     * @param recipient The destination address
                     * @param amount The amount getting transferred
                     */
                    function _transfer(address sender, address recipient, uint128 amount) internal virtual {
                      uint128 oldSenderBalance = _userState[sender].balance;
                      _userState[sender].balance = oldSenderBalance - amount;
                      uint128 oldRecipientBalance = _userState[recipient].balance;
                      _userState[recipient].balance = oldRecipientBalance + amount;
                      IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                      if (address(incentivesControllerLocal) != address(0)) {
                        uint256 currentTotalSupply = _totalSupply;
                        incentivesControllerLocal.handleAction(sender, currentTotalSupply, oldSenderBalance);
                        if (sender != recipient) {
                          incentivesControllerLocal.handleAction(recipient, currentTotalSupply, oldRecipientBalance);
                        }
                      }
                    }
                    /**
                     * @notice Approve `spender` to use `amount` of `owner`s balance
                     * @param owner The address owning the tokens
                     * @param spender The address approved for spending
                     * @param amount The amount of tokens to approve spending of
                     */
                    function _approve(address owner, address spender, uint256 amount) internal virtual {
                      _allowances[owner][spender] = amount;
                      emit Approval(owner, spender, amount);
                    }
                    /**
                     * @notice Update the name of the token
                     * @param newName The new name for the token
                     */
                    function _setName(string memory newName) internal {
                      _name = newName;
                    }
                    /**
                     * @notice Update the symbol for the token
                     * @param newSymbol The new symbol for the token
                     */
                    function _setSymbol(string memory newSymbol) internal {
                      _symbol = newSymbol;
                    }
                    /**
                     * @notice Update the number of decimals for the token
                     * @param newDecimals The new number of decimals for the token
                     */
                    function _setDecimals(uint8 newDecimals) internal {
                      _decimals = newDecimals;
                    }
                  }
                  

                  File 9 of 10: InitializableImmutableAdminUpgradeabilityProxy
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                      // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                      // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                      // for accounts without code, i.e. `keccak256('')`
                      bytes32 codehash;
                      bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                        codehash := extcodehash(account)
                      }
                      return (codehash != accountHash && codehash != 0x0);
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, 'Address: insufficient balance');
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (bool success, ) = recipient.call{value: amount}('');
                      require(success, 'Address: unable to send value, recipient may have reverted');
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import './Proxy.sol';
                  import '../contracts/Address.sol';
                  /**
                   * @title BaseUpgradeabilityProxy
                   * @dev This contract implements a proxy that allows to change the
                   * implementation address to which it will delegate.
                   * Such a change is called an implementation upgrade.
                   */
                  contract BaseUpgradeabilityProxy is Proxy {
                    /**
                     * @dev Emitted when the implementation is upgraded.
                     * @param implementation Address of the new implementation.
                     */
                    event Upgraded(address indexed implementation);
                    /**
                     * @dev Storage slot with the address of the current implementation.
                     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                     * validated in the constructor.
                     */
                    bytes32 internal constant IMPLEMENTATION_SLOT =
                      0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                    /**
                     * @dev Returns the current implementation.
                     * @return impl Address of the current implementation
                     */
                    function _implementation() internal view override returns (address impl) {
                      bytes32 slot = IMPLEMENTATION_SLOT;
                      //solium-disable-next-line
                      assembly {
                        impl := sload(slot)
                      }
                    }
                    /**
                     * @dev Upgrades the proxy to a new implementation.
                     * @param newImplementation Address of the new implementation.
                     */
                    function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                    }
                    /**
                     * @dev Sets the implementation address of the proxy.
                     * @param newImplementation Address of the new implementation.
                     */
                    function _setImplementation(address newImplementation) internal {
                      require(
                        Address.isContract(newImplementation),
                        'Cannot set a proxy implementation to a non-contract address'
                      );
                      bytes32 slot = IMPLEMENTATION_SLOT;
                      //solium-disable-next-line
                      assembly {
                        sstore(slot, newImplementation)
                      }
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import './BaseUpgradeabilityProxy.sol';
                  /**
                   * @title InitializableUpgradeabilityProxy
                   * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
                   * implementation and init data.
                   */
                  contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
                    /**
                     * @dev Contract initializer.
                     * @param _logic Address of the initial implementation.
                     * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                     * It should include the signature and the parameters of the function to be called, as described in
                     * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                     * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                     */
                    function initialize(address _logic, bytes memory _data) public payable {
                      require(_implementation() == address(0));
                      assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                      _setImplementation(_logic);
                      if (_data.length > 0) {
                        (bool success, ) = _logic.delegatecall(_data);
                        require(success);
                      }
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  /**
                   * @title Proxy
                   * @dev Implements delegation of calls to other contracts, with proper
                   * forwarding of return values and bubbling of failures.
                   * It defines a fallback function that delegates all calls to the address
                   * returned by the abstract _implementation() internal function.
                   */
                  abstract contract Proxy {
                    /**
                     * @dev Fallback function.
                     * Will run if no other function in the contract matches the call data.
                     * Implemented entirely in `_fallback`.
                     */
                    fallback() external payable {
                      _fallback();
                    }
                    /**
                     * @return The Address of the implementation.
                     */
                    function _implementation() internal view virtual returns (address);
                    /**
                     * @dev Delegates execution to an implementation contract.
                     * This is a low level function that doesn't return to its internal call site.
                     * It will return to the external caller whatever the implementation returns.
                     * @param implementation Address to delegate.
                     */
                    function _delegate(address implementation) internal {
                      //solium-disable-next-line
                      assembly {
                        // Copy msg.data. We take full control of memory in this inline assembly
                        // block because it will not return to Solidity code. We overwrite the
                        // Solidity scratch pad at memory position 0.
                        calldatacopy(0, 0, calldatasize())
                        // Call the implementation.
                        // out and outsize are 0 because we don't know the size yet.
                        let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                        // Copy the returned data.
                        returndatacopy(0, 0, returndatasize())
                        switch result
                        // delegatecall returns 0 on error.
                        case 0 {
                          revert(0, returndatasize())
                        }
                        default {
                          return(0, returndatasize())
                        }
                      }
                    }
                    /**
                     * @dev Function that is run as the first thing in the fallback function.
                     * Can be redefined in derived contracts to add functionality.
                     * Redefinitions must call super._willFallback().
                     */
                    function _willFallback() internal virtual {}
                    /**
                     * @dev fallback implementation.
                     * Extracted to enable manual triggering.
                     */
                    function _fallback() internal {
                      _willFallback();
                      _delegate(_implementation());
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import {BaseUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
                  /**
                   * @title BaseImmutableAdminUpgradeabilityProxy
                   * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
                   * @notice This contract combines an upgradeability proxy with an authorization
                   * mechanism for administrative tasks.
                   * @dev The admin role is stored in an immutable, which helps saving transactions costs
                   * All external functions in this contract must be guarded by the
                   * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
                   * feature proposal that would enable this to be done automatically.
                   */
                  contract BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                    address internal immutable _admin;
                    /**
                     * @dev Constructor.
                     * @param admin The address of the admin
                     */
                    constructor(address admin) {
                      _admin = admin;
                    }
                    modifier ifAdmin() {
                      if (msg.sender == _admin) {
                        _;
                      } else {
                        _fallback();
                      }
                    }
                    /**
                     * @notice Return the admin address
                     * @return The address of the proxy admin.
                     */
                    function admin() external ifAdmin returns (address) {
                      return _admin;
                    }
                    /**
                     * @notice Return the implementation address
                     * @return The address of the implementation.
                     */
                    function implementation() external ifAdmin returns (address) {
                      return _implementation();
                    }
                    /**
                     * @notice Upgrade the backing implementation of the proxy.
                     * @dev Only the admin can call this function.
                     * @param newImplementation The address of the new implementation.
                     */
                    function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeTo(newImplementation);
                    }
                    /**
                     * @notice Upgrade the backing implementation of the proxy and call a function
                     * on the new implementation.
                     * @dev This is useful to initialize the proxied contract.
                     * @param newImplementation The address of the new implementation.
                     * @param data Data to send as msg.data in the low level call.
                     * It should include the signature and the parameters of the function to be called, as described in
                     * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                     */
                    function upgradeToAndCall(address newImplementation, bytes calldata data)
                      external
                      payable
                      ifAdmin
                    {
                      _upgradeTo(newImplementation);
                      (bool success, ) = newImplementation.delegatecall(data);
                      require(success);
                    }
                    /**
                     * @notice Only fall back when the sender is not the admin.
                     */
                    function _willFallback() internal virtual override {
                      require(msg.sender != _admin, 'Cannot call fallback function from the proxy admin');
                      super._willFallback();
                    }
                  }
                  // SPDX-License-Identifier: AGPL-3.0
                  pragma solidity 0.8.10;
                  import {InitializableUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
                  import {Proxy} from '../../../dependencies/openzeppelin/upgradeability/Proxy.sol';
                  import {BaseImmutableAdminUpgradeabilityProxy} from './BaseImmutableAdminUpgradeabilityProxy.sol';
                  /**
                   * @title InitializableAdminUpgradeabilityProxy
                   * @author Aave
                   * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
                   */
                  contract InitializableImmutableAdminUpgradeabilityProxy is
                    BaseImmutableAdminUpgradeabilityProxy,
                    InitializableUpgradeabilityProxy
                  {
                    /**
                     * @dev Constructor.
                     * @param admin The address of the admin
                     */
                    constructor(address admin) BaseImmutableAdminUpgradeabilityProxy(admin) {
                      // Intentionally left blank
                    }
                    /// @inheritdoc BaseImmutableAdminUpgradeabilityProxy
                    function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                      BaseImmutableAdminUpgradeabilityProxy._willFallback();
                    }
                  }
                  

                  File 10 of 10: DefaultReserveInterestRateStrategyV2
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
                  import {WadRayMath} from '../protocol/libraries/math/WadRayMath.sol';
                  import {PercentageMath} from '../protocol/libraries/math/PercentageMath.sol';
                  import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                  import {Errors} from '../protocol/libraries/helpers/Errors.sol';
                  import {IDefaultInterestRateStrategyV2} from '../interfaces/IDefaultInterestRateStrategyV2.sol';
                  import {IReserveInterestRateStrategy} from '../interfaces/IReserveInterestRateStrategy.sol';
                  import {IPoolAddressesProvider} from '../interfaces/IPoolAddressesProvider.sol';
                  /**
                   * @title DefaultReserveInterestRateStrategyV2 contract
                   * @author BGD Labs
                   * @notice Default interest rate strategy used by the Aave protocol
                   * @dev Strategies are pool-specific: each contract CAN'T be used across different Aave pools
                   *   due to the caching of the PoolAddressesProvider and the usage of underlying addresses as
                   *   index of the _interestRateData
                   */
                  contract DefaultReserveInterestRateStrategyV2 is IDefaultInterestRateStrategyV2 {
                    using WadRayMath for uint256;
                    using PercentageMath for uint256;
                    struct CalcInterestRatesLocalVars {
                      uint256 availableLiquidity;
                      uint256 currentVariableBorrowRate;
                      uint256 currentLiquidityRate;
                      uint256 borrowUsageRatio;
                      uint256 supplyUsageRatio;
                      uint256 availableLiquidityPlusDebt;
                    }
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    IPoolAddressesProvider public immutable ADDRESSES_PROVIDER;
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    uint256 public constant MAX_BORROW_RATE = 1000_00;
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    uint256 public constant MIN_OPTIMAL_POINT = 1_00;
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    uint256 public constant MAX_OPTIMAL_POINT = 99_00;
                    /// @dev Map of reserves address and their interest rate data (reserveAddress => interestRateData)
                    mapping(address => InterestRateData) internal _interestRateData;
                    modifier onlyPoolConfigurator() {
                      require(
                        msg.sender == ADDRESSES_PROVIDER.getPoolConfigurator(),
                        Errors.CALLER_NOT_POOL_CONFIGURATOR
                      );
                      _;
                    }
                    /**
                     * @dev Constructor.
                     * @param provider The address of the PoolAddressesProvider of the associated Aave pool
                     */
                    constructor(address provider) {
                      require(provider != address(0), Errors.INVALID_ADDRESSES_PROVIDER);
                      ADDRESSES_PROVIDER = IPoolAddressesProvider(provider);
                    }
                    /// @inheritdoc IReserveInterestRateStrategy
                    function setInterestRateParams(
                      address reserve,
                      bytes calldata rateData
                    ) external onlyPoolConfigurator {
                      _setInterestRateParams(reserve, abi.decode(rateData, (InterestRateData)));
                    }
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    function setInterestRateParams(
                      address reserve,
                      InterestRateData calldata rateData
                    ) external onlyPoolConfigurator {
                      _setInterestRateParams(reserve, rateData);
                    }
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    function getInterestRateData(address reserve) external view returns (InterestRateDataRay memory) {
                      return _rayifyRateData(_interestRateData[reserve]);
                    }
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    function getInterestRateDataBps(address reserve) external view returns (InterestRateData memory) {
                      return _interestRateData[reserve];
                    }
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    function getOptimalUsageRatio(address reserve) external view returns (uint256) {
                      return _bpsToRay(uint256(_interestRateData[reserve].optimalUsageRatio));
                    }
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    function getVariableRateSlope1(address reserve) external view returns (uint256) {
                      return _bpsToRay(uint256(_interestRateData[reserve].variableRateSlope1));
                    }
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    function getVariableRateSlope2(address reserve) external view returns (uint256) {
                      return _bpsToRay(uint256(_interestRateData[reserve].variableRateSlope2));
                    }
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    function getBaseVariableBorrowRate(address reserve) external view override returns (uint256) {
                      return _bpsToRay(uint256(_interestRateData[reserve].baseVariableBorrowRate));
                    }
                    /// @inheritdoc IDefaultInterestRateStrategyV2
                    function getMaxVariableBorrowRate(address reserve) external view override returns (uint256) {
                      return
                        _bpsToRay(
                          uint256(
                            _interestRateData[reserve].baseVariableBorrowRate +
                              _interestRateData[reserve].variableRateSlope1 +
                              _interestRateData[reserve].variableRateSlope2
                          )
                        );
                    }
                    /// @inheritdoc IReserveInterestRateStrategy
                    function calculateInterestRates(
                      DataTypes.CalculateInterestRatesParams memory params
                    ) external view virtual override returns (uint256, uint256) {
                      InterestRateDataRay memory rateData = _rayifyRateData(_interestRateData[params.reserve]);
                      // @note This is a short circuit to allow mintable assets (ex. GHO), which by definition cannot be supplied
                      // and thus do not use virtual underlying balances.
                      if (!params.usingVirtualBalance) {
                        return (0, rateData.baseVariableBorrowRate);
                      }
                      CalcInterestRatesLocalVars memory vars;
                      vars.currentLiquidityRate = 0;
                      vars.currentVariableBorrowRate = rateData.baseVariableBorrowRate;
                      if (params.totalDebt != 0) {
                        vars.availableLiquidity =
                          params.virtualUnderlyingBalance +
                          params.liquidityAdded -
                          params.liquidityTaken;
                        vars.availableLiquidityPlusDebt = vars.availableLiquidity + params.totalDebt;
                        vars.borrowUsageRatio = params.totalDebt.rayDiv(vars.availableLiquidityPlusDebt);
                        vars.supplyUsageRatio = params.totalDebt.rayDiv(
                          vars.availableLiquidityPlusDebt + params.unbacked
                        );
                      } else {
                        return (0, vars.currentVariableBorrowRate);
                      }
                      if (vars.borrowUsageRatio > rateData.optimalUsageRatio) {
                        uint256 excessBorrowUsageRatio = (vars.borrowUsageRatio - rateData.optimalUsageRatio).rayDiv(
                          WadRayMath.RAY - rateData.optimalUsageRatio
                        );
                        vars.currentVariableBorrowRate +=
                          rateData.variableRateSlope1 +
                          rateData.variableRateSlope2.rayMul(excessBorrowUsageRatio);
                      } else {
                        vars.currentVariableBorrowRate += rateData
                          .variableRateSlope1
                          .rayMul(vars.borrowUsageRatio)
                          .rayDiv(rateData.optimalUsageRatio);
                      }
                      vars.currentLiquidityRate = vars
                        .currentVariableBorrowRate
                        .rayMul(vars.supplyUsageRatio)
                        .percentMul(PercentageMath.PERCENTAGE_FACTOR - params.reserveFactor);
                      return (vars.currentLiquidityRate, vars.currentVariableBorrowRate);
                    }
                    /**
                     * @dev Doing validations and data update for an asset
                     * @param reserve address of the underlying asset of the reserve
                     * @param rateData Encoded reserve interest rate data to apply
                     */
                    function _setInterestRateParams(address reserve, InterestRateData memory rateData) internal {
                      require(reserve != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                      require(
                        rateData.optimalUsageRatio <= MAX_OPTIMAL_POINT &&
                          rateData.optimalUsageRatio >= MIN_OPTIMAL_POINT,
                        Errors.INVALID_OPTIMAL_USAGE_RATIO
                      );
                      require(
                        rateData.variableRateSlope1 <= rateData.variableRateSlope2,
                        Errors.SLOPE_2_MUST_BE_GTE_SLOPE_1
                      );
                      // The maximum rate should not be above certain threshold
                      require(
                        uint256(rateData.baseVariableBorrowRate) +
                          uint256(rateData.variableRateSlope1) +
                          uint256(rateData.variableRateSlope2) <=
                          MAX_BORROW_RATE,
                        Errors.INVALID_MAX_RATE
                      );
                      _interestRateData[reserve] = rateData;
                      emit RateDataUpdate(
                        reserve,
                        rateData.optimalUsageRatio,
                        rateData.baseVariableBorrowRate,
                        rateData.variableRateSlope1,
                        rateData.variableRateSlope2
                      );
                    }
                    /**
                     * @dev Transforms an InterestRateData struct to an InterestRateDataRay struct by multiplying all values
                     * by 1e23, turning them into ray values
                     *
                     * @param data The InterestRateData struct to transform
                     *
                     * @return The resulting InterestRateDataRay struct
                     */
                    function _rayifyRateData(
                      InterestRateData memory data
                    ) internal pure returns (InterestRateDataRay memory) {
                      return
                        InterestRateDataRay({
                          optimalUsageRatio: _bpsToRay(uint256(data.optimalUsageRatio)),
                          baseVariableBorrowRate: _bpsToRay(uint256(data.baseVariableBorrowRate)),
                          variableRateSlope1: _bpsToRay(uint256(data.variableRateSlope1)),
                          variableRateSlope2: _bpsToRay(uint256(data.variableRateSlope2))
                        });
                    }
                    // @dev helper function added here, as generally the protocol doesn't use bps
                    function _bpsToRay(uint256 n) internal pure returns (uint256) {
                      return n * 1e23;
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @dev Interface of the ERC20 standard as defined in the EIP.
                   */
                  interface IERC20 {
                    /**
                     * @dev Returns the amount of tokens in existence.
                     */
                    function totalSupply() external view returns (uint256);
                    /**
                     * @dev Returns the amount of tokens owned by `account`.
                     */
                    function balanceOf(address account) external view returns (uint256);
                    /**
                     * @dev Moves `amount` tokens from the caller's account to `recipient`.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transfer(address recipient, uint256 amount) external returns (bool);
                    /**
                     * @dev Returns the remaining number of tokens that `spender` will be
                     * allowed to spend on behalf of `owner` through {transferFrom}. This is
                     * zero by default.
                     *
                     * This value changes when {approve} or {transferFrom} are called.
                     */
                    function allowance(address owner, address spender) external view returns (uint256);
                    /**
                     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * IMPORTANT: Beware that changing an allowance with this method brings the risk
                     * that someone may use both the old and the new allowance by unfortunate
                     * transaction ordering. One possible solution to mitigate this race
                     * condition is to first reduce the spender's allowance to 0 and set the
                     * desired value afterwards:
                     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                     *
                     * Emits an {Approval} event.
                     */
                    function approve(address spender, uint256 amount) external returns (bool);
                    /**
                     * @dev Moves `amount` tokens from `sender` to `recipient` using the
                     * allowance mechanism. `amount` is then deducted from the caller's
                     * allowance.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                    /**
                     * @dev Emitted when `value` tokens are moved from one account (`from`) to
                     * another (`to`).
                     *
                     * Note that `value` may be zero.
                     */
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    /**
                     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                     * a call to {approve}. `value` is the new allowance.
                     */
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  /**
                   * @title WadRayMath library
                   * @author Aave
                   * @notice Provides functions to perform calculations with Wad and Ray units
                   * @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers
                   * with 27 digits of precision)
                   * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                   */
                  library WadRayMath {
                    // HALF_WAD and HALF_RAY expressed with extended notation as constant with operations are not supported in Yul assembly
                    uint256 internal constant WAD = 1e18;
                    uint256 internal constant HALF_WAD = 0.5e18;
                    uint256 internal constant RAY = 1e27;
                    uint256 internal constant HALF_RAY = 0.5e27;
                    uint256 internal constant WAD_RAY_RATIO = 1e9;
                    /**
                     * @dev Multiplies two wad, rounding half up to the nearest wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @param b Wad
                     * @return c = a*b, in wad
                     */
                    function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b
                      assembly {
                        if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, b), HALF_WAD), WAD)
                      }
                    }
                    /**
                     * @dev Divides two wad, rounding half up to the nearest wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @param b Wad
                     * @return c = a/b, in wad
                     */
                    function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - halfB) / WAD
                      assembly {
                        if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, WAD), div(b, 2)), b)
                      }
                    }
                    /**
                     * @notice Multiplies two ray, rounding half up to the nearest ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @param b Ray
                     * @return c = a raymul b
                     */
                    function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - HALF_RAY) / b
                      assembly {
                        if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, b), HALF_RAY), RAY)
                      }
                    }
                    /**
                     * @notice Divides two ray, rounding half up to the nearest ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @param b Ray
                     * @return c = a raydiv b
                     */
                    function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                      // to avoid overflow, a <= (type(uint256).max - halfB) / RAY
                      assembly {
                        if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
                          revert(0, 0)
                        }
                        c := div(add(mul(a, RAY), div(b, 2)), b)
                      }
                    }
                    /**
                     * @dev Casts ray down to wad
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Ray
                     * @return b = a converted to wad, rounded half up to the nearest wad
                     */
                    function rayToWad(uint256 a) internal pure returns (uint256 b) {
                      assembly {
                        b := div(a, WAD_RAY_RATIO)
                        let remainder := mod(a, WAD_RAY_RATIO)
                        if iszero(lt(remainder, div(WAD_RAY_RATIO, 2))) {
                          b := add(b, 1)
                        }
                      }
                    }
                    /**
                     * @dev Converts wad up to ray
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param a Wad
                     * @return b = a converted in ray
                     */
                    function wadToRay(uint256 a) internal pure returns (uint256 b) {
                      // to avoid overflow, b/WAD_RAY_RATIO == a
                      assembly {
                        b := mul(a, WAD_RAY_RATIO)
                        if iszero(eq(div(b, WAD_RAY_RATIO), a)) {
                          revert(0, 0)
                        }
                      }
                    }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  /**
                   * @title PercentageMath library
                   * @author Aave
                   * @notice Provides functions to perform percentage calculations
                   * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR
                   * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                   */
                  library PercentageMath {
                    // Maximum percentage factor (100.00%)
                    uint256 internal constant PERCENTAGE_FACTOR = 1e4;
                    // Half percentage factor (50.00%)
                    uint256 internal constant HALF_PERCENTAGE_FACTOR = 0.5e4;
                    /**
                     * @notice Executes a percentage multiplication
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param value The value of which the percentage needs to be calculated
                     * @param percentage The percentage of the value to be calculated
                     * @return result value percentmul percentage
                     */
                    function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                      // to avoid overflow, value <= (type(uint256).max - HALF_PERCENTAGE_FACTOR) / percentage
                      assembly {
                        if iszero(
                          or(
                            iszero(percentage),
                            iszero(gt(value, div(sub(not(0), HALF_PERCENTAGE_FACTOR), percentage)))
                          )
                        ) {
                          revert(0, 0)
                        }
                        result := div(add(mul(value, percentage), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
                      }
                    }
                    /**
                     * @notice Executes a percentage division
                     * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                     * @param value The value of which the percentage needs to be calculated
                     * @param percentage The percentage of the value to be calculated
                     * @return result value percentdiv percentage
                     */
                    function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                      // to avoid overflow, value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR
                      assembly {
                        if or(
                          iszero(percentage),
                          iszero(iszero(gt(value, div(sub(not(0), div(percentage, 2)), PERCENTAGE_FACTOR))))
                        ) {
                          revert(0, 0)
                        }
                        result := div(add(mul(value, PERCENTAGE_FACTOR), div(percentage, 2)), percentage)
                      }
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  library DataTypes {
                    /**
                     * This exists specifically to maintain the `getReserveData()` interface, since the new, internal
                     * `ReserveData` struct includes the reserve's `virtualUnderlyingBalance`.
                     */
                    struct ReserveDataLegacy {
                      //stores the reserve configuration
                      ReserveConfigurationMap configuration;
                      //the liquidity index. Expressed in ray
                      uint128 liquidityIndex;
                      //the current supply rate. Expressed in ray
                      uint128 currentLiquidityRate;
                      //variable borrow index. Expressed in ray
                      uint128 variableBorrowIndex;
                      //the current variable borrow rate. Expressed in ray
                      uint128 currentVariableBorrowRate;
                      // DEPRECATED on v3.2.0
                      uint128 currentStableBorrowRate;
                      //timestamp of last update
                      uint40 lastUpdateTimestamp;
                      //the id of the reserve. Represents the position in the list of the active reserves
                      uint16 id;
                      //aToken address
                      address aTokenAddress;
                      // DEPRECATED on v3.2.0
                      address stableDebtTokenAddress;
                      //variableDebtToken address
                      address variableDebtTokenAddress;
                      //address of the interest rate strategy
                      address interestRateStrategyAddress;
                      //the current treasury balance, scaled
                      uint128 accruedToTreasury;
                      //the outstanding unbacked aTokens minted through the bridging feature
                      uint128 unbacked;
                      //the outstanding debt borrowed against this asset in isolation mode
                      uint128 isolationModeTotalDebt;
                    }
                    struct ReserveData {
                      //stores the reserve configuration
                      ReserveConfigurationMap configuration;
                      //the liquidity index. Expressed in ray
                      uint128 liquidityIndex;
                      //the current supply rate. Expressed in ray
                      uint128 currentLiquidityRate;
                      //variable borrow index. Expressed in ray
                      uint128 variableBorrowIndex;
                      //the current variable borrow rate. Expressed in ray
                      uint128 currentVariableBorrowRate;
                      // DEPRECATED on v3.2.0
                      uint128 __deprecatedStableBorrowRate;
                      //timestamp of last update
                      uint40 lastUpdateTimestamp;
                      //the id of the reserve. Represents the position in the list of the active reserves
                      uint16 id;
                      //timestamp until when liquidations are not allowed on the reserve, if set to past liquidations will be allowed
                      uint40 liquidationGracePeriodUntil;
                      //aToken address
                      address aTokenAddress;
                      // DEPRECATED on v3.2.0
                      address __deprecatedStableDebtTokenAddress;
                      //variableDebtToken address
                      address variableDebtTokenAddress;
                      //address of the interest rate strategy
                      address interestRateStrategyAddress;
                      //the current treasury balance, scaled
                      uint128 accruedToTreasury;
                      //the outstanding unbacked aTokens minted through the bridging feature
                      uint128 unbacked;
                      //the outstanding debt borrowed against this asset in isolation mode
                      uint128 isolationModeTotalDebt;
                      //the amount of underlying accounted for by the protocol
                      uint128 virtualUnderlyingBalance;
                    }
                    struct ReserveConfigurationMap {
                      //bit 0-15: LTV
                      //bit 16-31: Liq. threshold
                      //bit 32-47: Liq. bonus
                      //bit 48-55: Decimals
                      //bit 56: reserve is active
                      //bit 57: reserve is frozen
                      //bit 58: borrowing is enabled
                      //bit 59: DEPRECATED: stable rate borrowing enabled
                      //bit 60: asset is paused
                      //bit 61: borrowing in isolation mode is enabled
                      //bit 62: siloed borrowing enabled
                      //bit 63: flashloaning enabled
                      //bit 64-79: reserve factor
                      //bit 80-115: borrow cap in whole tokens, borrowCap == 0 => no cap
                      //bit 116-151: supply cap in whole tokens, supplyCap == 0 => no cap
                      //bit 152-167: liquidation protocol fee
                      //bit 168-175: DEPRECATED: eMode category
                      //bit 176-211: unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
                      //bit 212-251: debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
                      //bit 252: virtual accounting is enabled for the reserve
                      //bit 253-255 unused
                      uint256 data;
                    }
                    struct UserConfigurationMap {
                      /**
                       * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.
                       * The first bit indicates if an asset is used as collateral by the user, the second whether an
                       * asset is borrowed by the user.
                       */
                      uint256 data;
                    }
                    // DEPRECATED: kept for backwards compatibility, might be removed in a future version
                    struct EModeCategoryLegacy {
                      // each eMode category has a custom ltv and liquidation threshold
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                      // DEPRECATED
                      address priceSource;
                      string label;
                    }
                    struct CollateralConfig {
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                    }
                    struct EModeCategoryBaseConfiguration {
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                      string label;
                    }
                    struct EModeCategory {
                      // each eMode category has a custom ltv and liquidation threshold
                      uint16 ltv;
                      uint16 liquidationThreshold;
                      uint16 liquidationBonus;
                      uint128 collateralBitmap;
                      string label;
                      uint128 borrowableBitmap;
                    }
                    enum InterestRateMode {
                      NONE,
                      __DEPRECATED,
                      VARIABLE
                    }
                    struct ReserveCache {
                      uint256 currScaledVariableDebt;
                      uint256 nextScaledVariableDebt;
                      uint256 currLiquidityIndex;
                      uint256 nextLiquidityIndex;
                      uint256 currVariableBorrowIndex;
                      uint256 nextVariableBorrowIndex;
                      uint256 currLiquidityRate;
                      uint256 currVariableBorrowRate;
                      uint256 reserveFactor;
                      ReserveConfigurationMap reserveConfiguration;
                      address aTokenAddress;
                      address variableDebtTokenAddress;
                      uint40 reserveLastUpdateTimestamp;
                    }
                    struct ExecuteLiquidationCallParams {
                      uint256 reservesCount;
                      uint256 debtToCover;
                      address collateralAsset;
                      address debtAsset;
                      address user;
                      bool receiveAToken;
                      address priceOracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                    }
                    struct ExecuteSupplyParams {
                      address asset;
                      uint256 amount;
                      address onBehalfOf;
                      uint16 referralCode;
                    }
                    struct ExecuteBorrowParams {
                      address asset;
                      address user;
                      address onBehalfOf;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      uint16 referralCode;
                      bool releaseUnderlying;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                    }
                    struct ExecuteRepayParams {
                      address asset;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      address onBehalfOf;
                      bool useATokens;
                    }
                    struct ExecuteWithdrawParams {
                      address asset;
                      uint256 amount;
                      address to;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                    }
                    struct ExecuteSetUserEModeParams {
                      uint256 reservesCount;
                      address oracle;
                      uint8 categoryId;
                    }
                    struct FinalizeTransferParams {
                      address asset;
                      address from;
                      address to;
                      uint256 amount;
                      uint256 balanceFromBefore;
                      uint256 balanceToBefore;
                      uint256 reservesCount;
                      address oracle;
                      uint8 fromEModeCategory;
                    }
                    struct FlashloanParams {
                      address receiverAddress;
                      address[] assets;
                      uint256[] amounts;
                      uint256[] interestRateModes;
                      address onBehalfOf;
                      bytes params;
                      uint16 referralCode;
                      uint256 flashLoanPremiumToProtocol;
                      uint256 flashLoanPremiumTotal;
                      uint256 reservesCount;
                      address addressesProvider;
                      address pool;
                      uint8 userEModeCategory;
                      bool isAuthorizedFlashBorrower;
                    }
                    struct FlashloanSimpleParams {
                      address receiverAddress;
                      address asset;
                      uint256 amount;
                      bytes params;
                      uint16 referralCode;
                      uint256 flashLoanPremiumToProtocol;
                      uint256 flashLoanPremiumTotal;
                    }
                    struct FlashLoanRepaymentParams {
                      uint256 amount;
                      uint256 totalPremium;
                      uint256 flashLoanPremiumToProtocol;
                      address asset;
                      address receiverAddress;
                      uint16 referralCode;
                    }
                    struct CalculateUserAccountDataParams {
                      UserConfigurationMap userConfig;
                      uint256 reservesCount;
                      address user;
                      address oracle;
                      uint8 userEModeCategory;
                    }
                    struct ValidateBorrowParams {
                      ReserveCache reserveCache;
                      UserConfigurationMap userConfig;
                      address asset;
                      address userAddress;
                      uint256 amount;
                      InterestRateMode interestRateMode;
                      uint256 reservesCount;
                      address oracle;
                      uint8 userEModeCategory;
                      address priceOracleSentinel;
                      bool isolationModeActive;
                      address isolationModeCollateralAddress;
                      uint256 isolationModeDebtCeiling;
                    }
                    struct ValidateLiquidationCallParams {
                      ReserveCache debtReserveCache;
                      uint256 totalDebt;
                      uint256 healthFactor;
                      address priceOracleSentinel;
                    }
                    struct CalculateInterestRatesParams {
                      uint256 unbacked;
                      uint256 liquidityAdded;
                      uint256 liquidityTaken;
                      uint256 totalDebt;
                      uint256 reserveFactor;
                      address reserve;
                      bool usingVirtualBalance;
                      uint256 virtualUnderlyingBalance;
                    }
                    struct InitReserveParams {
                      address asset;
                      address aTokenAddress;
                      address variableDebtAddress;
                      address interestRateStrategyAddress;
                      uint16 reservesCount;
                      uint16 maxNumberReserves;
                    }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title Errors library
                   * @author Aave
                   * @notice Defines the error messages emitted by the different contracts of the Aave protocol
                   */
                  library Errors {
                    string public constant CALLER_NOT_POOL_ADMIN = '1'; // 'The caller of the function is not a pool admin'
                    string public constant CALLER_NOT_EMERGENCY_ADMIN = '2'; // 'The caller of the function is not an emergency admin'
                    string public constant CALLER_NOT_POOL_OR_EMERGENCY_ADMIN = '3'; // 'The caller of the function is not a pool or emergency admin'
                    string public constant CALLER_NOT_RISK_OR_POOL_ADMIN = '4'; // 'The caller of the function is not a risk or pool admin'
                    string public constant CALLER_NOT_ASSET_LISTING_OR_POOL_ADMIN = '5'; // 'The caller of the function is not an asset listing or pool admin'
                    string public constant CALLER_NOT_BRIDGE = '6'; // 'The caller of the function is not a bridge'
                    string public constant ADDRESSES_PROVIDER_NOT_REGISTERED = '7'; // 'Pool addresses provider is not registered'
                    string public constant INVALID_ADDRESSES_PROVIDER_ID = '8'; // 'Invalid id for the pool addresses provider'
                    string public constant NOT_CONTRACT = '9'; // 'Address is not a contract'
                    string public constant CALLER_NOT_POOL_CONFIGURATOR = '10'; // 'The caller of the function is not the pool configurator'
                    string public constant CALLER_NOT_ATOKEN = '11'; // 'The caller of the function is not an AToken'
                    string public constant INVALID_ADDRESSES_PROVIDER = '12'; // 'The address of the pool addresses provider is invalid'
                    string public constant INVALID_FLASHLOAN_EXECUTOR_RETURN = '13'; // 'Invalid return value of the flashloan executor function'
                    string public constant RESERVE_ALREADY_ADDED = '14'; // 'Reserve has already been added to reserve list'
                    string public constant NO_MORE_RESERVES_ALLOWED = '15'; // 'Maximum amount of reserves in the pool reached'
                    string public constant EMODE_CATEGORY_RESERVED = '16'; // 'Zero eMode category is reserved for volatile heterogeneous assets'
                    string public constant INVALID_EMODE_CATEGORY_ASSIGNMENT = '17'; // 'Invalid eMode category assignment to asset'
                    string public constant RESERVE_LIQUIDITY_NOT_ZERO = '18'; // 'The liquidity of the reserve needs to be 0'
                    string public constant FLASHLOAN_PREMIUM_INVALID = '19'; // 'Invalid flashloan premium'
                    string public constant INVALID_RESERVE_PARAMS = '20'; // 'Invalid risk parameters for the reserve'
                    string public constant INVALID_EMODE_CATEGORY_PARAMS = '21'; // 'Invalid risk parameters for the eMode category'
                    string public constant BRIDGE_PROTOCOL_FEE_INVALID = '22'; // 'Invalid bridge protocol fee'
                    string public constant CALLER_MUST_BE_POOL = '23'; // 'The caller of this function must be a pool'
                    string public constant INVALID_MINT_AMOUNT = '24'; // 'Invalid amount to mint'
                    string public constant INVALID_BURN_AMOUNT = '25'; // 'Invalid amount to burn'
                    string public constant INVALID_AMOUNT = '26'; // 'Amount must be greater than 0'
                    string public constant RESERVE_INACTIVE = '27'; // 'Action requires an active reserve'
                    string public constant RESERVE_FROZEN = '28'; // 'Action cannot be performed because the reserve is frozen'
                    string public constant RESERVE_PAUSED = '29'; // 'Action cannot be performed because the reserve is paused'
                    string public constant BORROWING_NOT_ENABLED = '30'; // 'Borrowing is not enabled'
                    string public constant NOT_ENOUGH_AVAILABLE_USER_BALANCE = '32'; // 'User cannot withdraw more than the available balance'
                    string public constant INVALID_INTEREST_RATE_MODE_SELECTED = '33'; // 'Invalid interest rate mode selected'
                    string public constant COLLATERAL_BALANCE_IS_ZERO = '34'; // 'The collateral balance is 0'
                    string public constant HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '35'; // 'Health factor is lesser than the liquidation threshold'
                    string public constant COLLATERAL_CANNOT_COVER_NEW_BORROW = '36'; // 'There is not enough collateral to cover a new borrow'
                    string public constant COLLATERAL_SAME_AS_BORROWING_CURRENCY = '37'; // 'Collateral is (mostly) the same currency that is being borrowed'
                    string public constant NO_DEBT_OF_SELECTED_TYPE = '39'; // 'For repayment of a specific type of debt, the user needs to have debt that type'
                    string public constant NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '40'; // 'To repay on behalf of a user an explicit amount to repay is needed'
                    string public constant NO_OUTSTANDING_VARIABLE_DEBT = '42'; // 'User does not have outstanding variable rate debt on this reserve'
                    string public constant UNDERLYING_BALANCE_ZERO = '43'; // 'The underlying balance needs to be greater than 0'
                    string public constant INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '44'; // 'Interest rate rebalance conditions were not met'
                    string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45'; // 'Health factor is not below the threshold'
                    string public constant COLLATERAL_CANNOT_BE_LIQUIDATED = '46'; // 'The collateral chosen cannot be liquidated'
                    string public constant SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47'; // 'User did not borrow the specified currency'
                    string public constant INCONSISTENT_FLASHLOAN_PARAMS = '49'; // 'Inconsistent flashloan parameters'
                    string public constant BORROW_CAP_EXCEEDED = '50'; // 'Borrow cap is exceeded'
                    string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'
                    string public constant UNBACKED_MINT_CAP_EXCEEDED = '52'; // 'Unbacked mint cap is exceeded'
                    string public constant DEBT_CEILING_EXCEEDED = '53'; // 'Debt ceiling is exceeded'
                    string public constant UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54'; // 'Claimable rights over underlying not zero (aToken supply or accruedToTreasury)'
                    string public constant VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56'; // 'Variable debt supply is not zero'
                    string public constant LTV_VALIDATION_FAILED = '57'; // 'Ltv validation failed'
                    string public constant INCONSISTENT_EMODE_CATEGORY = '58'; // 'Inconsistent eMode category'
                    string public constant PRICE_ORACLE_SENTINEL_CHECK_FAILED = '59'; // 'Price oracle sentinel validation failed'
                    string public constant ASSET_NOT_BORROWABLE_IN_ISOLATION = '60'; // 'Asset is not borrowable in isolation mode'
                    string public constant RESERVE_ALREADY_INITIALIZED = '61'; // 'Reserve has already been initialized'
                    string public constant USER_IN_ISOLATION_MODE_OR_LTV_ZERO = '62'; // 'User is in isolation mode or ltv is zero'
                    string public constant INVALID_LTV = '63'; // 'Invalid ltv parameter for the reserve'
                    string public constant INVALID_LIQ_THRESHOLD = '64'; // 'Invalid liquidity threshold parameter for the reserve'
                    string public constant INVALID_LIQ_BONUS = '65'; // 'Invalid liquidity bonus parameter for the reserve'
                    string public constant INVALID_DECIMALS = '66'; // 'Invalid decimals parameter of the underlying asset of the reserve'
                    string public constant INVALID_RESERVE_FACTOR = '67'; // 'Invalid reserve factor parameter for the reserve'
                    string public constant INVALID_BORROW_CAP = '68'; // 'Invalid borrow cap for the reserve'
                    string public constant INVALID_SUPPLY_CAP = '69'; // 'Invalid supply cap for the reserve'
                    string public constant INVALID_LIQUIDATION_PROTOCOL_FEE = '70'; // 'Invalid liquidation protocol fee for the reserve'
                    string public constant INVALID_EMODE_CATEGORY = '71'; // 'Invalid eMode category for the reserve'
                    string public constant INVALID_UNBACKED_MINT_CAP = '72'; // 'Invalid unbacked mint cap for the reserve'
                    string public constant INVALID_DEBT_CEILING = '73'; // 'Invalid debt ceiling for the reserve
                    string public constant INVALID_RESERVE_INDEX = '74'; // 'Invalid reserve index'
                    string public constant ACL_ADMIN_CANNOT_BE_ZERO = '75'; // 'ACL admin cannot be set to the zero address'
                    string public constant INCONSISTENT_PARAMS_LENGTH = '76'; // 'Array parameters that should be equal length are not'
                    string public constant ZERO_ADDRESS_NOT_VALID = '77'; // 'Zero address not valid'
                    string public constant INVALID_EXPIRATION = '78'; // 'Invalid expiration'
                    string public constant INVALID_SIGNATURE = '79'; // 'Invalid signature'
                    string public constant OPERATION_NOT_SUPPORTED = '80'; // 'Operation not supported'
                    string public constant DEBT_CEILING_NOT_ZERO = '81'; // 'Debt ceiling is not zero'
                    string public constant ASSET_NOT_LISTED = '82'; // 'Asset is not listed'
                    string public constant INVALID_OPTIMAL_USAGE_RATIO = '83'; // 'Invalid optimal usage ratio'
                    string public constant UNDERLYING_CANNOT_BE_RESCUED = '85'; // 'The underlying asset cannot be rescued'
                    string public constant ADDRESSES_PROVIDER_ALREADY_ADDED = '86'; // 'Reserve has already been added to reserve list'
                    string public constant POOL_ADDRESSES_DO_NOT_MATCH = '87'; // 'The token implementation pool address and the pool address provided by the initializing pool do not match'
                    string public constant SILOED_BORROWING_VIOLATION = '89'; // 'User is trying to borrow multiple assets including a siloed one'
                    string public constant RESERVE_DEBT_NOT_ZERO = '90'; // the total debt of the reserve needs to be 0
                    string public constant FLASHLOAN_DISABLED = '91'; // FlashLoaning for this asset is disabled
                    string public constant INVALID_MAX_RATE = '92'; // The expect maximum borrow rate is invalid
                    string public constant WITHDRAW_TO_ATOKEN = '93'; // Withdrawing to the aToken is not allowed
                    string public constant SUPPLY_TO_ATOKEN = '94'; // Supplying to the aToken is not allowed
                    string public constant SLOPE_2_MUST_BE_GTE_SLOPE_1 = '95'; // Variable interest rate slope 2 can not be lower than slope 1
                    string public constant CALLER_NOT_RISK_OR_POOL_OR_EMERGENCY_ADMIN = '96'; // 'The caller of the function is not a risk, pool or emergency admin'
                    string public constant LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED = '97'; // 'Liquidation grace sentinel validation failed'
                    string public constant INVALID_GRACE_PERIOD = '98'; // Grace period above a valid range
                    string public constant INVALID_FREEZE_STATE = '99'; // Reserve is already in the passed freeze state
                    string public constant NOT_BORROWABLE_IN_EMODE = '100'; // Asset not borrowable in eMode
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {IReserveInterestRateStrategy} from './IReserveInterestRateStrategy.sol';
                  import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                  /**
                   * @title IDefaultInterestRateStrategyV2
                   * @author BGD Labs
                   * @notice Interface of the default interest rate strategy used by the Aave protocol
                   */
                  interface IDefaultInterestRateStrategyV2 is IReserveInterestRateStrategy {
                    /**
                     * @notice Holds the interest rate data for a given reserve
                     *
                     * @dev Since values are in bps, they are multiplied by 1e23 in order to become rays with 27 decimals. This
                     * in turn means that the maximum supported interest rate is 4294967295 (2**32-1) bps or 42949672.95%.
                     *
                     * @param optimalUsageRatio The optimal usage ratio, in bps
                     * @param baseVariableBorrowRate The base variable borrow rate, in bps
                     * @param variableRateSlope1 The slope of the variable interest curve, before hitting the optimal ratio, in bps
                     * @param variableRateSlope2 The slope of the variable interest curve, after hitting the optimal ratio, in bps
                     */
                    struct InterestRateData {
                      uint16 optimalUsageRatio;
                      uint32 baseVariableBorrowRate;
                      uint32 variableRateSlope1;
                      uint32 variableRateSlope2;
                    }
                    /**
                     * @notice The interest rate data, where all values are in ray (fixed-point 27 decimal numbers) for a given reserve,
                     * used in in-memory calculations.
                     *
                     * @param optimalUsageRatio The optimal usage ratio
                     * @param baseVariableBorrowRate The base variable borrow rate
                     * @param variableRateSlope1 The slope of the variable interest curve, before hitting the optimal ratio
                     * @param variableRateSlope2 The slope of the variable interest curve, after hitting the optimal ratio
                     */
                    struct InterestRateDataRay {
                      uint256 optimalUsageRatio;
                      uint256 baseVariableBorrowRate;
                      uint256 variableRateSlope1;
                      uint256 variableRateSlope2;
                    }
                    /**
                     * @notice emitted when new interest rate data is set in a reserve
                     *
                     * @param reserve address of the reserve that has new interest rate data set
                     * @param optimalUsageRatio The optimal usage ratio, in bps
                     * @param baseVariableBorrowRate The base variable borrow rate, in bps
                     * @param variableRateSlope1 The slope of the variable interest curve, before hitting the optimal ratio, in bps
                     * @param variableRateSlope2 The slope of the variable interest curve, after hitting the optimal ratio, in bps
                     */
                    event RateDataUpdate(
                      address indexed reserve,
                      uint256 optimalUsageRatio,
                      uint256 baseVariableBorrowRate,
                      uint256 variableRateSlope1,
                      uint256 variableRateSlope2
                    );
                    /**
                     * @notice Returns the address of the PoolAddressesProvider
                     * @return The address of the PoolAddressesProvider contract
                     */
                    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                    /**
                     * @notice Returns the maximum value achievable for variable borrow rate, in bps
                     * @return The maximum rate
                     */
                    function MAX_BORROW_RATE() external view returns (uint256);
                    /**
                     * @notice Returns the minimum optimal point, in bps
                     * @return The optimal point
                     */
                    function MIN_OPTIMAL_POINT() external view returns (uint256);
                    /**
                     * @notice Returns the maximum optimal point, in bps
                     * @return The optimal point
                     */
                    function MAX_OPTIMAL_POINT() external view returns (uint256);
                    /**
                     * notice Returns the full InterestRateData object for the given reserve, in ray
                     *
                     * @param reserve The reserve to get the data of
                     *
                     * @return The InterestRateDataRay object for the given reserve
                     */
                    function getInterestRateData(address reserve) external view returns (InterestRateDataRay memory);
                    /**
                     * notice Returns the full InterestRateDataRay object for the given reserve, in bps
                     *
                     * @param reserve The reserve to get the data of
                     *
                     * @return The InterestRateData object for the given reserve
                     */
                    function getInterestRateDataBps(address reserve) external view returns (InterestRateData memory);
                    /**
                     * @notice Returns the optimal usage rate for the given reserve in ray
                     *
                     * @param reserve The reserve to get the optimal usage rate of
                     *
                     * @return The optimal usage rate is the level of borrow / collateral at which the borrow rate
                     */
                    function getOptimalUsageRatio(address reserve) external view returns (uint256);
                    /**
                     * @notice Returns the variable rate slope below optimal usage ratio in ray
                     * @dev It's the variable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO
                     *
                     * @param reserve The reserve to get the variable rate slope 1 of
                     *
                     * @return The variable rate slope
                     */
                    function getVariableRateSlope1(address reserve) external view returns (uint256);
                    /**
                     * @notice Returns the variable rate slope above optimal usage ratio in ray
                     * @dev It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO
                     *
                     * @param reserve The reserve to get the variable rate slope 2 of
                     *
                     * @return The variable rate slope
                     */
                    function getVariableRateSlope2(address reserve) external view returns (uint256);
                    /**
                     * @notice Returns the base variable borrow rate, in ray
                     *
                     * @param reserve The reserve to get the base variable borrow rate of
                     *
                     * @return The base variable borrow rate
                     */
                    function getBaseVariableBorrowRate(address reserve) external view returns (uint256);
                    /**
                     * @notice Returns the maximum variable borrow rate, in ray
                     *
                     * @param reserve The reserve to get the maximum variable borrow rate of
                     *
                     * @return The maximum variable borrow rate
                     */
                    function getMaxVariableBorrowRate(address reserve) external view returns (uint256);
                    /**
                     * @notice Sets interest rate data for an Aave rate strategy
                     * @param reserve The reserve to update
                     * @param rateData The reserve interest rate data to apply to the given reserve
                     *   Being specific to this custom implementation, with custom struct type,
                     *   overloading the function on the generic interface
                     */
                    function setInterestRateParams(address reserve, InterestRateData calldata rateData) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                  /**
                   * @title IReserveInterestRateStrategy
                   * @author BGD Labs
                   * @notice Basic interface for any rate strategy used by the Aave protocol
                   */
                  interface IReserveInterestRateStrategy {
                    /**
                     * @notice Sets interest rate data for an Aave rate strategy
                     * @param reserve The reserve to update
                     * @param rateData The abi encoded reserve interest rate data to apply to the given reserve
                     *   Abstracted this way as rate strategies can be custom
                     */
                    function setInterestRateParams(address reserve, bytes calldata rateData) external;
                    /**
                     * @notice Calculates the interest rates depending on the reserve's state and configurations
                     * @param params The parameters needed to calculate interest rates
                     * @return liquidityRate The liquidity rate expressed in ray
                     * @return variableBorrowRate The variable borrow rate expressed in ray
                     */
                    function calculateInterestRates(
                      DataTypes.CalculateInterestRatesParams memory params
                    ) external view returns (uint256, uint256);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /**
                   * @title IPoolAddressesProvider
                   * @author Aave
                   * @notice Defines the basic interface for a Pool Addresses Provider.
                   */
                  interface IPoolAddressesProvider {
                    /**
                     * @dev Emitted when the market identifier is updated.
                     * @param oldMarketId The old id of the market
                     * @param newMarketId The new id of the market
                     */
                    event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);
                    /**
                     * @dev Emitted when the pool is updated.
                     * @param oldAddress The old address of the Pool
                     * @param newAddress The new address of the Pool
                     */
                    event PoolUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the pool configurator is updated.
                     * @param oldAddress The old address of the PoolConfigurator
                     * @param newAddress The new address of the PoolConfigurator
                     */
                    event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the price oracle is updated.
                     * @param oldAddress The old address of the PriceOracle
                     * @param newAddress The new address of the PriceOracle
                     */
                    event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the ACL manager is updated.
                     * @param oldAddress The old address of the ACLManager
                     * @param newAddress The new address of the ACLManager
                     */
                    event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the ACL admin is updated.
                     * @param oldAddress The old address of the ACLAdmin
                     * @param newAddress The new address of the ACLAdmin
                     */
                    event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the price oracle sentinel is updated.
                     * @param oldAddress The old address of the PriceOracleSentinel
                     * @param newAddress The new address of the PriceOracleSentinel
                     */
                    event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the pool data provider is updated.
                     * @param oldAddress The old address of the PoolDataProvider
                     * @param newAddress The new address of the PoolDataProvider
                     */
                    event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when a new proxy is created.
                     * @param id The identifier of the proxy
                     * @param proxyAddress The address of the created proxy contract
                     * @param implementationAddress The address of the implementation contract
                     */
                    event ProxyCreated(
                      bytes32 indexed id,
                      address indexed proxyAddress,
                      address indexed implementationAddress
                    );
                    /**
                     * @dev Emitted when a new non-proxied contract address is registered.
                     * @param id The identifier of the contract
                     * @param oldAddress The address of the old contract
                     * @param newAddress The address of the new contract
                     */
                    event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);
                    /**
                     * @dev Emitted when the implementation of the proxy registered with id is updated
                     * @param id The identifier of the contract
                     * @param proxyAddress The address of the proxy contract
                     * @param oldImplementationAddress The address of the old implementation contract
                     * @param newImplementationAddress The address of the new implementation contract
                     */
                    event AddressSetAsProxy(
                      bytes32 indexed id,
                      address indexed proxyAddress,
                      address oldImplementationAddress,
                      address indexed newImplementationAddress
                    );
                    /**
                     * @notice Returns the id of the Aave market to which this contract points to.
                     * @return The market id
                     */
                    function getMarketId() external view returns (string memory);
                    /**
                     * @notice Associates an id with a specific PoolAddressesProvider.
                     * @dev This can be used to create an onchain registry of PoolAddressesProviders to
                     * identify and validate multiple Aave markets.
                     * @param newMarketId The market id
                     */
                    function setMarketId(string calldata newMarketId) external;
                    /**
                     * @notice Returns an address by its identifier.
                     * @dev The returned address might be an EOA or a contract, potentially proxied
                     * @dev It returns ZERO if there is no registered address with the given id
                     * @param id The id
                     * @return The address of the registered for the specified id
                     */
                    function getAddress(bytes32 id) external view returns (address);
                    /**
                     * @notice General function to update the implementation of a proxy registered with
                     * certain `id`. If there is no proxy registered, it will instantiate one and
                     * set as implementation the `newImplementationAddress`.
                     * @dev IMPORTANT Use this function carefully, only for ids that don't have an explicit
                     * setter function, in order to avoid unexpected consequences
                     * @param id The id
                     * @param newImplementationAddress The address of the new implementation
                     */
                    function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;
                    /**
                     * @notice Sets an address for an id replacing the address saved in the addresses map.
                     * @dev IMPORTANT Use this function carefully, as it will do a hard replacement
                     * @param id The id
                     * @param newAddress The address to set
                     */
                    function setAddress(bytes32 id, address newAddress) external;
                    /**
                     * @notice Returns the address of the Pool proxy.
                     * @return The Pool proxy address
                     */
                    function getPool() external view returns (address);
                    /**
                     * @notice Updates the implementation of the Pool, or creates a proxy
                     * setting the new `pool` implementation when the function is called for the first time.
                     * @param newPoolImpl The new Pool implementation
                     */
                    function setPoolImpl(address newPoolImpl) external;
                    /**
                     * @notice Returns the address of the PoolConfigurator proxy.
                     * @return The PoolConfigurator proxy address
                     */
                    function getPoolConfigurator() external view returns (address);
                    /**
                     * @notice Updates the implementation of the PoolConfigurator, or creates a proxy
                     * setting the new `PoolConfigurator` implementation when the function is called for the first time.
                     * @param newPoolConfiguratorImpl The new PoolConfigurator implementation
                     */
                    function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;
                    /**
                     * @notice Returns the address of the price oracle.
                     * @return The address of the PriceOracle
                     */
                    function getPriceOracle() external view returns (address);
                    /**
                     * @notice Updates the address of the price oracle.
                     * @param newPriceOracle The address of the new PriceOracle
                     */
                    function setPriceOracle(address newPriceOracle) external;
                    /**
                     * @notice Returns the address of the ACL manager.
                     * @return The address of the ACLManager
                     */
                    function getACLManager() external view returns (address);
                    /**
                     * @notice Updates the address of the ACL manager.
                     * @param newAclManager The address of the new ACLManager
                     */
                    function setACLManager(address newAclManager) external;
                    /**
                     * @notice Returns the address of the ACL admin.
                     * @return The address of the ACL admin
                     */
                    function getACLAdmin() external view returns (address);
                    /**
                     * @notice Updates the address of the ACL admin.
                     * @param newAclAdmin The address of the new ACL admin
                     */
                    function setACLAdmin(address newAclAdmin) external;
                    /**
                     * @notice Returns the address of the price oracle sentinel.
                     * @return The address of the PriceOracleSentinel
                     */
                    function getPriceOracleSentinel() external view returns (address);
                    /**
                     * @notice Updates the address of the price oracle sentinel.
                     * @param newPriceOracleSentinel The address of the new PriceOracleSentinel
                     */
                    function setPriceOracleSentinel(address newPriceOracleSentinel) external;
                    /**
                     * @notice Returns the address of the data provider.
                     * @return The address of the DataProvider
                     */
                    function getPoolDataProvider() external view returns (address);
                    /**
                     * @notice Updates the address of the data provider.
                     * @param newDataProvider The address of the new DataProvider
                     */
                    function setPoolDataProvider(address newDataProvider) external;
                  }