ETH Price: $1,943.54 (-2.24%)

Transaction Decoder

Block:
24432384 at Feb-11-2026 08:38:11 AM +UTC
Transaction Fee:
0.000016356506065168 ETH $0.03
Gas Used:
261,763 Gas / 0.062485936 Gwei

Emitted Events:

882 TetherToken.Transfer( from=0x0Cb2ba2Fb76D91d5bc79Ab05B6a71a20b652Dba3, to=0x5B7cDf8B3530221cF929e4a928b69c5A63EEB285, value=50000000 )
883 TetherToken.Approval( owner=0x5B7cDf8B3530221cF929e4a928b69c5A63EEB285, spender=InitializableImmutableAdminUpgradeabilityProxy, value=50000000 )
884 InitializableImmutableAdminUpgradeabilityProxy.0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a( 0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a, 0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0000000000000000000000000000000000000000000f5811584109c18733539b, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000001baf48b8e316e8ccfe036c, 000000000000000000000000000000000000000003bb78a588072839dde7e587, 000000000000000000000000000000000000000003ed6a11e254ff6b2c138b51 )
885 TetherToken.Transfer( from=0x5B7cDf8B3530221cF929e4a928b69c5A63EEB285, to=InitializableImmutableAdminUpgradeabilityProxy, value=50000000 )
886 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000005b7cdf8b3530221cf929e4a928b69c5a63eeb285, 0000000000000000000000000000000000000000000000000000000002faff5f )
887 InitializableImmutableAdminUpgradeabilityProxy.0x458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b86196( 0x458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b86196, 0x0000000000000000000000005b7cdf8b3530221cf929e4a928b69c5a63eeb285, 0x0000000000000000000000005b7cdf8b3530221cf929e4a928b69c5a63eeb285, 0000000000000000000000000000000000000000000000000000000002faff5f, 0000000000000000000000000000000000000000000000000000000000000edf, 000000000000000000000000000000000000000003bb78a588072839dde7e587 )
888 InitializableImmutableAdminUpgradeabilityProxy.0x2b627736bca15cd5381dcf80b0bf11fd197d01a037c52b927a881a10fb73ba61( 0x2b627736bca15cd5381dcf80b0bf11fd197d01a037c52b927a881a10fb73ba61, 0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0x0000000000000000000000005b7cdf8b3530221cf929e4a928b69c5a63eeb285, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000005b7cdf8b3530221cf929e4a928b69c5a63eeb285, 0000000000000000000000000000000000000000000000000000000002faf080 )
889 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x00000000000000000000000037e7e93093ae3a8aaef4a0d41dbd9c037508eb60, 0000000000000000000000000000000000000000000000000000000000001595 )
890 InitializableImmutableAdminUpgradeabilityProxy.0x458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b86196( 0x458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b86196, 0x0000000000000000000000005b7cdf8b3530221cf929e4a928b69c5a63eeb285, 0x00000000000000000000000037e7e93093ae3a8aaef4a0d41dbd9c037508eb60, 0000000000000000000000000000000000000000000000000000000000001595, 0000000000000000000000000000000000000000000000000000000000001595, 000000000000000000000000000000000000000003bb78a588072839dde7e587 )
891 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000005b7cdf8b3530221cf929e4a928b69c5a63eeb285, 0x00000000000000000000000037e7e93093ae3a8aaef4a0d41dbd9c037508eb60, 000000000000000000000000000000000000000000000000000000000000a8b1 )
892 InitializableImmutableAdminUpgradeabilityProxy.0x4beccb90f994c31aced7a23b5611020728a23d8ec5cddd1a3e9d97b96fda8666( 0x4beccb90f994c31aced7a23b5611020728a23d8ec5cddd1a3e9d97b96fda8666, 0x0000000000000000000000005b7cdf8b3530221cf929e4a928b69c5a63eeb285, 0x00000000000000000000000037e7e93093ae3a8aaef4a0d41dbd9c037508eb60, 000000000000000000000000000000000000000000000000000000000000920b, 000000000000000000000000000000000000000003bb78a588072839dde7e587 )
893 0x5b7cdf8b3530221cf929e4a928b69c5a63eeb285.0x5c6331c8b8ede1c586e85939e6d88d39281ec06b1b10bac807d8de3cdb145d66( 0x5c6331c8b8ede1c586e85939e6d88d39281ec06b1b10bac807d8de3cdb145d66, 000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0000000000000000000000000000000000000000000000000000000059b7f4f9, 00000000000000000000000000000000000000000000000000000000000005dc )
894 0x5b7cdf8b3530221cf929e4a928b69c5a63eeb285.0xdc1ee6f03ac1d964463889d802070a9778a823a13fad3ef3fbd4f4cf13b514b1( 0xdc1ee6f03ac1d964463889d802070a9778a823a13fad3ef3fbd4f4cf13b514b1, 000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 000000000000000000000000000000000000000000000000000000000000a8b1, 00000000000000000000000037e7e93093ae3a8aaef4a0d41dbd9c037508eb60 )
895 0x5b7cdf8b3530221cf929e4a928b69c5a63eeb285.0x580a8baf7ef5aa26f7d45636a9af1b7b796b944999958f37298177a4f2bb6222( 0x580a8baf7ef5aa26f7d45636a9af1b7b796b944999958f37298177a4f2bb6222, 000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0000000000000000000000000000000000000000000000000000000002faf080, 000000000000000000000000000000000000000000000000000000000000a676 )
896 0x4ff6178b58a51cb74e50254ed1e9ebd4f28eb2c0.0x9d003a10bcec67c2990f8d5e14970aa1a179cccb69dc62e3c0a5908cf2b2089f( 0x9d003a10bcec67c2990f8d5e14970aa1a179cccb69dc62e3c0a5908cf2b2089f, 0000000000000000000000005b7cdf8b3530221cf929e4a928b69c5a63eeb285 )

Account State Difference:

  Address   Before After State Difference Code
0x23878914...A74D4086a
(Titan Builder)
20.395195221815292147 Eth20.395195579849488287 Eth0.00000035803419614
0x5B7cDf8B...A63EEB285
0x7F8f1810...637727Cc9
1.274549916683923662 Eth
Nonce: 8174
1.274533560177858494 Eth
Nonce: 8175
0.000016356506065168
0x87870Bca...50B4fA4E2
(Aave: Pool V3)
0xdAC17F95...13D831ec7

Execution Trace

0x4ff6178b58a51cb74e50254ed1e9ebd4f28eb2c0.b9de6a93( )
  • 0x5b7cdf8b3530221cf929e4a928b69c5a63eeb285.2a540772( )
    • 0x06c561600016398430cc48be17292b87b1c0db5f.2a540772( )
      • TetherToken.balanceOf( who=0x0Cb2ba2Fb76D91d5bc79Ab05B6a71a20b652Dba3 ) => ( 50000000 )
      • TetherToken.transferFrom( _from=0x0Cb2ba2Fb76D91d5bc79Ab05B6a71a20b652Dba3, _to=0x5B7cDf8B3530221cF929e4a928b69c5A63EEB285, _value=50000000 )
      • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
        • 0xadc45df3cf1584624c97338bef33363bf5b97ada.70a08231( )
          • InitializableImmutableAdminUpgradeabilityProxy.d15e0053( )
            • 0x8147b99df7672a21809c9093e6f6ce1a60f119bd.d15e0053( )
            • TetherToken.balanceOf( who=0x5B7cDf8B3530221cF929e4a928b69c5A63EEB285 ) => ( 50000000 )
            • TetherToken.approve( _spender=0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2, _value=50000000 )
            • InitializableImmutableAdminUpgradeabilityProxy.617ba037( )
              • 0x8147b99df7672a21809c9093e6f6ce1a60f119bd.617ba037( )
                • 0x33654b16a4de97bce05d7dd06803bf1066f3123c.73f23670( )
                  • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
                  • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
                  • DefaultReserveInterestRateStrategyV2.calculateInterestRates( params=[{name:unbacked, type:uint256, order:1, indexed:false, value:10153410274, valueString:10153410274}, {name:liquidityAdded, type:uint256, order:2, indexed:false, value:50000000, valueString:50000000}, {name:liquidityTaken, type:uint256, order:3, indexed:false, value:0, valueString:0}, {name:totalDebt, type:uint256, order:4, indexed:false, value:3714675025828982, valueString:3714675025828982}, {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:2317354673475536, valueString:2317354673475536}] ) => ( 18549775498736095087252379, 33468752752187794536661868 )
                  • TetherToken.transferFrom( _from=0x5B7cDf8B3530221cF929e4a928b69c5A63EEB285, _to=0x23878914EFE38d27C4D67Ab83ed1b93A74D4086a, _value=50000000 )
                  • InitializableImmutableAdminUpgradeabilityProxy.b3f1c93d( )
                  • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
                    • 0xadc45df3cf1584624c97338bef33363bf5b97ada.70a08231( )
                      • InitializableImmutableAdminUpgradeabilityProxy.d15e0053( )
                        • 0x8147b99df7672a21809c9093e6f6ce1a60f119bd.d15e0053( )
                        • 0x4ff6178b58a51cb74e50254ed1e9ebd4f28eb2c0.STATICCALL( )
                        • InitializableImmutableAdminUpgradeabilityProxy.a9059cbb( )
                          • 0xadc45df3cf1584624c97338bef33363bf5b97ada.a9059cbb( )
                            • InitializableImmutableAdminUpgradeabilityProxy.d15e0053( )
                              • 0x8147b99df7672a21809c9093e6f6ce1a60f119bd.d15e0053( )
                              • InitializableImmutableAdminUpgradeabilityProxy.31873e2e( )
                                • RewardsController.handleAction( user=0x5B7cDf8B3530221cF929e4a928b69c5A63EEB285, totalSupply=5222029886804964, userBalance=1303159178 )
                                • InitializableImmutableAdminUpgradeabilityProxy.31873e2e( )
                                  • RewardsController.handleAction( user=0x37E7e93093AE3A8AAEf4A0D41DBd9c037508eB60, totalSupply=5222029886804964, userBalance=10027043576 )
                                  • InitializableImmutableAdminUpgradeabilityProxy.12772993( )
                                    • 0x8147b99df7672a21809c9093e6f6ce1a60f119bd.12772993( )
                                    • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
                                      • 0xadc45df3cf1584624c97338bef33363bf5b97ada.70a08231( )
                                        • InitializableImmutableAdminUpgradeabilityProxy.d15e0053( )
                                          • 0x8147b99df7672a21809c9093e6f6ce1a60f119bd.d15e0053( )
                                          • 0x4ff6178b58a51cb74e50254ed1e9ebd4f28eb2c0.CALL( )
                                            File 1 of 7: 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 7: 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 3 of 7: 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 7: 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 7: 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;
                                            }
                                            

                                            File 6 of 7: 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 7 of 7: RewardsController
                                            // 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;
                                            /**
                                             * @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: AGPL-3.0
                                            pragma solidity ^0.8.10;
                                            interface IEACAggregatorProxy {
                                              function decimals() external view returns (uint8);
                                              function latestAnswer() external view returns (int256);
                                              function latestTimestamp() external view returns (uint256);
                                              function latestRound() external view returns (uint256);
                                              function getAnswer(uint256 roundId) external view returns (int256);
                                              function getTimestamp(uint256 roundId) external view returns (uint256);
                                              event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
                                              event NewRound(uint256 indexed roundId, address indexed startedBy);
                                            }
                                            // SPDX-License-Identifier: BUSL-1.1
                                            pragma solidity ^0.8.10;
                                            import {VersionedInitializable} from '@aave/core-v3/contracts/protocol/libraries/aave-upgradeability/VersionedInitializable.sol';
                                            import {SafeCast} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/SafeCast.sol';
                                            import {IScaledBalanceToken} from '@aave/core-v3/contracts/interfaces/IScaledBalanceToken.sol';
                                            import {RewardsDistributor} from './RewardsDistributor.sol';
                                            import {IRewardsController} from './interfaces/IRewardsController.sol';
                                            import {ITransferStrategyBase} from './interfaces/ITransferStrategyBase.sol';
                                            import {RewardsDataTypes} from './libraries/RewardsDataTypes.sol';
                                            import {IEACAggregatorProxy} from '../misc/interfaces/IEACAggregatorProxy.sol';
                                            /**
                                             * @title RewardsController
                                             * @notice Abstract contract template to build Distributors contracts for ERC20 rewards to protocol participants
                                             * @author Aave
                                             **/
                                            contract RewardsController is RewardsDistributor, VersionedInitializable, IRewardsController {
                                              using SafeCast for uint256;
                                              uint256 public constant REVISION = 1;
                                              // This mapping allows whitelisted addresses to claim on behalf of others
                                              // useful for contracts that hold tokens to be rewarded but don't have any native logic to claim Liquidity Mining rewards
                                              mapping(address => address) internal _authorizedClaimers;
                                              // reward => transfer strategy implementation contract
                                              // The TransferStrategy contract abstracts the logic regarding
                                              // the source of the reward and how to transfer it to the user.
                                              mapping(address => ITransferStrategyBase) internal _transferStrategy;
                                              // This mapping contains the price oracle per reward.
                                              // A price oracle is enforced for integrators to be able to show incentives at
                                              // the current Aave UI without the need to setup an external price registry
                                              // At the moment of reward configuration, the Incentives Controller performs
                                              // a check to see if the provided reward oracle contains `latestAnswer`.
                                              mapping(address => IEACAggregatorProxy) internal _rewardOracle;
                                              modifier onlyAuthorizedClaimers(address claimer, address user) {
                                                require(_authorizedClaimers[user] == claimer, 'CLAIMER_UNAUTHORIZED');
                                                _;
                                              }
                                              constructor(address emissionManager) RewardsDistributor(emissionManager) {}
                                              /**
                                               * @dev Initialize for RewardsController
                                               * @dev It expects an address as argument since its initialized via PoolAddressesProvider._updateImpl()
                                               **/
                                              function initialize(address) external initializer {}
                                              /// @inheritdoc IRewardsController
                                              function getClaimer(address user) external view override returns (address) {
                                                return _authorizedClaimers[user];
                                              }
                                              /**
                                               * @dev Returns the revision of the implementation contract
                                               * @return uint256, current revision version
                                               */
                                              function getRevision() internal pure override returns (uint256) {
                                                return REVISION;
                                              }
                                              /// @inheritdoc IRewardsController
                                              function getRewardOracle(address reward) external view override returns (address) {
                                                return address(_rewardOracle[reward]);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function getTransferStrategy(address reward) external view override returns (address) {
                                                return address(_transferStrategy[reward]);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function configureAssets(RewardsDataTypes.RewardsConfigInput[] memory config)
                                                external
                                                override
                                                onlyEmissionManager
                                              {
                                                for (uint256 i = 0; i < config.length; i++) {
                                                  // Get the current Scaled Total Supply of AToken or Debt token
                                                  config[i].totalSupply = IScaledBalanceToken(config[i].asset).scaledTotalSupply();
                                                  // Install TransferStrategy logic at IncentivesController
                                                  _installTransferStrategy(config[i].reward, config[i].transferStrategy);
                                                  // Set reward oracle, enforces input oracle to have latestPrice function
                                                  _setRewardOracle(config[i].reward, config[i].rewardOracle);
                                                }
                                                _configureAssets(config);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function setTransferStrategy(address reward, ITransferStrategyBase transferStrategy)
                                                external
                                                onlyEmissionManager
                                              {
                                                _installTransferStrategy(reward, transferStrategy);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function setRewardOracle(address reward, IEACAggregatorProxy rewardOracle)
                                                external
                                                onlyEmissionManager
                                              {
                                                _setRewardOracle(reward, rewardOracle);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function handleAction(
                                                address user,
                                                uint256 totalSupply,
                                                uint256 userBalance
                                              ) external override {
                                                _updateData(msg.sender, user, userBalance, totalSupply);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function claimRewards(
                                                address[] calldata assets,
                                                uint256 amount,
                                                address to,
                                                address reward
                                              ) external override returns (uint256) {
                                                require(to != address(0), 'INVALID_TO_ADDRESS');
                                                return _claimRewards(assets, amount, msg.sender, msg.sender, to, reward);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function claimRewardsOnBehalf(
                                                address[] calldata assets,
                                                uint256 amount,
                                                address user,
                                                address to,
                                                address reward
                                              ) external override onlyAuthorizedClaimers(msg.sender, user) returns (uint256) {
                                                require(user != address(0), 'INVALID_USER_ADDRESS');
                                                require(to != address(0), 'INVALID_TO_ADDRESS');
                                                return _claimRewards(assets, amount, msg.sender, user, to, reward);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function claimRewardsToSelf(
                                                address[] calldata assets,
                                                uint256 amount,
                                                address reward
                                              ) external override returns (uint256) {
                                                return _claimRewards(assets, amount, msg.sender, msg.sender, msg.sender, reward);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function claimAllRewards(address[] calldata assets, address to)
                                                external
                                                override
                                                returns (address[] memory rewardsList, uint256[] memory claimedAmounts)
                                              {
                                                require(to != address(0), 'INVALID_TO_ADDRESS');
                                                return _claimAllRewards(assets, msg.sender, msg.sender, to);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function claimAllRewardsOnBehalf(
                                                address[] calldata assets,
                                                address user,
                                                address to
                                              )
                                                external
                                                override
                                                onlyAuthorizedClaimers(msg.sender, user)
                                                returns (address[] memory rewardsList, uint256[] memory claimedAmounts)
                                              {
                                                require(user != address(0), 'INVALID_USER_ADDRESS');
                                                require(to != address(0), 'INVALID_TO_ADDRESS');
                                                return _claimAllRewards(assets, msg.sender, user, to);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function claimAllRewardsToSelf(address[] calldata assets)
                                                external
                                                override
                                                returns (address[] memory rewardsList, uint256[] memory claimedAmounts)
                                              {
                                                return _claimAllRewards(assets, msg.sender, msg.sender, msg.sender);
                                              }
                                              /// @inheritdoc IRewardsController
                                              function setClaimer(address user, address caller) external override onlyEmissionManager {
                                                _authorizedClaimers[user] = caller;
                                                emit ClaimerSet(user, caller);
                                              }
                                              /**
                                               * @dev Get user balances and total supply of all the assets specified by the assets parameter
                                               * @param assets List of assets to retrieve user balance and total supply
                                               * @param user Address of the user
                                               * @return userAssetBalances contains a list of structs with user balance and total supply of the given assets
                                               */
                                              function _getUserAssetBalances(address[] calldata assets, address user)
                                                internal
                                                view
                                                override
                                                returns (RewardsDataTypes.UserAssetBalance[] memory userAssetBalances)
                                              {
                                                userAssetBalances = new RewardsDataTypes.UserAssetBalance[](assets.length);
                                                for (uint256 i = 0; i < assets.length; i++) {
                                                  userAssetBalances[i].asset = assets[i];
                                                  (userAssetBalances[i].userBalance, userAssetBalances[i].totalSupply) = IScaledBalanceToken(
                                                    assets[i]
                                                  ).getScaledUserBalanceAndSupply(user);
                                                }
                                                return userAssetBalances;
                                              }
                                              /**
                                               * @dev Claims one type of reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards.
                                               * @param assets List of assets to check eligible distributions before claiming rewards
                                               * @param amount Amount of rewards to claim
                                               * @param claimer Address of the claimer who claims rewards on behalf of user
                                               * @param user Address to check and claim rewards
                                               * @param to Address that will be receiving the rewards
                                               * @param reward Address of the reward token
                                               * @return Rewards claimed
                                               **/
                                              function _claimRewards(
                                                address[] calldata assets,
                                                uint256 amount,
                                                address claimer,
                                                address user,
                                                address to,
                                                address reward
                                              ) internal returns (uint256) {
                                                if (amount == 0) {
                                                  return 0;
                                                }
                                                uint256 totalRewards;
                                                _updateDataMultiple(user, _getUserAssetBalances(assets, user));
                                                for (uint256 i = 0; i < assets.length; i++) {
                                                  address asset = assets[i];
                                                  totalRewards += _assets[asset].rewards[reward].usersData[user].accrued;
                                                  if (totalRewards <= amount) {
                                                    _assets[asset].rewards[reward].usersData[user].accrued = 0;
                                                  } else {
                                                    uint256 difference = totalRewards - amount;
                                                    totalRewards -= difference;
                                                    _assets[asset].rewards[reward].usersData[user].accrued = difference.toUint128();
                                                    break;
                                                  }
                                                }
                                                if (totalRewards == 0) {
                                                  return 0;
                                                }
                                                _transferRewards(to, reward, totalRewards);
                                                emit RewardsClaimed(user, reward, to, claimer, totalRewards);
                                                return totalRewards;
                                              }
                                              /**
                                               * @dev Claims one type of reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards.
                                               * @param assets List of assets to check eligible distributions before claiming rewards
                                               * @param claimer Address of the claimer on behalf of user
                                               * @param user Address to check and claim rewards
                                               * @param to Address that will be receiving the rewards
                                               * @return
                                               *   rewardsList List of reward addresses
                                               *   claimedAmount List of claimed amounts, follows "rewardsList" items order
                                               **/
                                              function _claimAllRewards(
                                                address[] calldata assets,
                                                address claimer,
                                                address user,
                                                address to
                                              ) internal returns (address[] memory rewardsList, uint256[] memory claimedAmounts) {
                                                uint256 rewardsListLength = _rewardsList.length;
                                                rewardsList = new address[](rewardsListLength);
                                                claimedAmounts = new uint256[](rewardsListLength);
                                                _updateDataMultiple(user, _getUserAssetBalances(assets, user));
                                                for (uint256 i = 0; i < assets.length; i++) {
                                                  address asset = assets[i];
                                                  for (uint256 j = 0; j < rewardsListLength; j++) {
                                                    if (rewardsList[j] == address(0)) {
                                                      rewardsList[j] = _rewardsList[j];
                                                    }
                                                    uint256 rewardAmount = _assets[asset].rewards[rewardsList[j]].usersData[user].accrued;
                                                    if (rewardAmount != 0) {
                                                      claimedAmounts[j] += rewardAmount;
                                                      _assets[asset].rewards[rewardsList[j]].usersData[user].accrued = 0;
                                                    }
                                                  }
                                                }
                                                for (uint256 i = 0; i < rewardsListLength; i++) {
                                                  _transferRewards(to, rewardsList[i], claimedAmounts[i]);
                                                  emit RewardsClaimed(user, rewardsList[i], to, claimer, claimedAmounts[i]);
                                                }
                                                return (rewardsList, claimedAmounts);
                                              }
                                              /**
                                               * @dev Function to transfer rewards to the desired account using delegatecall and
                                               * @param to Account address to send the rewards
                                               * @param reward Address of the reward token
                                               * @param amount Amount of rewards to transfer
                                               */
                                              function _transferRewards(
                                                address to,
                                                address reward,
                                                uint256 amount
                                              ) internal {
                                                ITransferStrategyBase transferStrategy = _transferStrategy[reward];
                                                bool success = transferStrategy.performTransfer(to, reward, amount);
                                                require(success == true, 'TRANSFER_ERROR');
                                              }
                                              /**
                                               * @dev Returns true if `account` is a contract.
                                               * @param account The address of the account
                                               * @return bool, true if contract, false otherwise
                                               */
                                              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;
                                                // solhint-disable-next-line no-inline-assembly
                                                assembly {
                                                  size := extcodesize(account)
                                                }
                                                return size > 0;
                                              }
                                              /**
                                               * @dev Internal function to call the optional install hook at the TransferStrategy
                                               * @param reward The address of the reward token
                                               * @param transferStrategy The address of the reward TransferStrategy
                                               */
                                              function _installTransferStrategy(address reward, ITransferStrategyBase transferStrategy)
                                                internal
                                              {
                                                require(address(transferStrategy) != address(0), 'STRATEGY_CAN_NOT_BE_ZERO');
                                                require(_isContract(address(transferStrategy)) == true, 'STRATEGY_MUST_BE_CONTRACT');
                                                _transferStrategy[reward] = transferStrategy;
                                                emit TransferStrategyInstalled(reward, address(transferStrategy));
                                              }
                                              /**
                                               * @dev Update the Price Oracle of a reward token. The Price Oracle must follow Chainlink IEACAggregatorProxy interface.
                                               * @notice The Price Oracle of a reward is used for displaying correct data about the incentives at the UI frontend.
                                               * @param reward The address of the reward token
                                               * @param rewardOracle The address of the price oracle
                                               */
                                              function _setRewardOracle(address reward, IEACAggregatorProxy rewardOracle) internal {
                                                require(rewardOracle.latestAnswer() > 0, 'ORACLE_MUST_RETURN_PRICE');
                                                _rewardOracle[reward] = rewardOracle;
                                                emit RewardOracleUpdated(reward, address(rewardOracle));
                                              }
                                            }
                                            // SPDX-License-Identifier: BUSL-1.1
                                            pragma solidity ^0.8.10;
                                            import {IScaledBalanceToken} from '@aave/core-v3/contracts/interfaces/IScaledBalanceToken.sol';
                                            import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol';
                                            import {SafeCast} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/SafeCast.sol';
                                            import {IRewardsDistributor} from './interfaces/IRewardsDistributor.sol';
                                            import {RewardsDataTypes} from './libraries/RewardsDataTypes.sol';
                                            /**
                                             * @title RewardsDistributor
                                             * @notice Accounting contract to manage multiple staking distributions with multiple rewards
                                             * @author Aave
                                             **/
                                            abstract contract RewardsDistributor is IRewardsDistributor {
                                              using SafeCast for uint256;
                                              // Manager of incentives
                                              address public immutable EMISSION_MANAGER;
                                              // Deprecated: This storage slot is kept for backwards compatibility purposes.
                                              address internal _emissionManager;
                                              // Map of rewarded asset addresses and their data (assetAddress => assetData)
                                              mapping(address => RewardsDataTypes.AssetData) internal _assets;
                                              // Map of reward assets (rewardAddress => enabled)
                                              mapping(address => bool) internal _isRewardEnabled;
                                              // Rewards list
                                              address[] internal _rewardsList;
                                              // Assets list
                                              address[] internal _assetsList;
                                              modifier onlyEmissionManager() {
                                                require(msg.sender == EMISSION_MANAGER, 'ONLY_EMISSION_MANAGER');
                                                _;
                                              }
                                              constructor(address emissionManager) {
                                                EMISSION_MANAGER = emissionManager;
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function getRewardsData(address asset, address reward)
                                                public
                                                view
                                                override
                                                returns (
                                                  uint256,
                                                  uint256,
                                                  uint256,
                                                  uint256
                                                )
                                              {
                                                return (
                                                  _assets[asset].rewards[reward].index,
                                                  _assets[asset].rewards[reward].emissionPerSecond,
                                                  _assets[asset].rewards[reward].lastUpdateTimestamp,
                                                  _assets[asset].rewards[reward].distributionEnd
                                                );
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function getAssetIndex(address asset, address reward)
                                                external
                                                view
                                                override
                                                returns (uint256, uint256)
                                              {
                                                RewardsDataTypes.RewardData storage rewardData = _assets[asset].rewards[reward];
                                                return
                                                  _getAssetIndex(
                                                    rewardData,
                                                    IScaledBalanceToken(asset).scaledTotalSupply(),
                                                    10**_assets[asset].decimals
                                                  );
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function getDistributionEnd(address asset, address reward)
                                                external
                                                view
                                                override
                                                returns (uint256)
                                              {
                                                return _assets[asset].rewards[reward].distributionEnd;
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function getRewardsByAsset(address asset) external view override returns (address[] memory) {
                                                uint128 rewardsCount = _assets[asset].availableRewardsCount;
                                                address[] memory availableRewards = new address[](rewardsCount);
                                                for (uint128 i = 0; i < rewardsCount; i++) {
                                                  availableRewards[i] = _assets[asset].availableRewards[i];
                                                }
                                                return availableRewards;
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function getRewardsList() external view override returns (address[] memory) {
                                                return _rewardsList;
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function getUserAssetIndex(
                                                address user,
                                                address asset,
                                                address reward
                                              ) public view override returns (uint256) {
                                                return _assets[asset].rewards[reward].usersData[user].index;
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function getUserAccruedRewards(address user, address reward)
                                                external
                                                view
                                                override
                                                returns (uint256)
                                              {
                                                uint256 totalAccrued;
                                                for (uint256 i = 0; i < _assetsList.length; i++) {
                                                  totalAccrued += _assets[_assetsList[i]].rewards[reward].usersData[user].accrued;
                                                }
                                                return totalAccrued;
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function getUserRewards(
                                                address[] calldata assets,
                                                address user,
                                                address reward
                                              ) external view override returns (uint256) {
                                                return _getUserReward(user, reward, _getUserAssetBalances(assets, user));
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function getAllUserRewards(address[] calldata assets, address user)
                                                external
                                                view
                                                override
                                                returns (address[] memory rewardsList, uint256[] memory unclaimedAmounts)
                                              {
                                                RewardsDataTypes.UserAssetBalance[] memory userAssetBalances = _getUserAssetBalances(
                                                  assets,
                                                  user
                                                );
                                                rewardsList = new address[](_rewardsList.length);
                                                unclaimedAmounts = new uint256[](rewardsList.length);
                                                // Add unrealized rewards from user to unclaimedRewards
                                                for (uint256 i = 0; i < userAssetBalances.length; i++) {
                                                  for (uint256 r = 0; r < rewardsList.length; r++) {
                                                    rewardsList[r] = _rewardsList[r];
                                                    unclaimedAmounts[r] += _assets[userAssetBalances[i].asset]
                                                      .rewards[rewardsList[r]]
                                                      .usersData[user]
                                                      .accrued;
                                                    if (userAssetBalances[i].userBalance == 0) {
                                                      continue;
                                                    }
                                                    unclaimedAmounts[r] += _getPendingRewards(user, rewardsList[r], userAssetBalances[i]);
                                                  }
                                                }
                                                return (rewardsList, unclaimedAmounts);
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function setDistributionEnd(
                                                address asset,
                                                address reward,
                                                uint32 newDistributionEnd
                                              ) external override onlyEmissionManager {
                                                uint256 oldDistributionEnd = _assets[asset].rewards[reward].distributionEnd;
                                                _assets[asset].rewards[reward].distributionEnd = newDistributionEnd;
                                                emit AssetConfigUpdated(
                                                  asset,
                                                  reward,
                                                  _assets[asset].rewards[reward].emissionPerSecond,
                                                  _assets[asset].rewards[reward].emissionPerSecond,
                                                  oldDistributionEnd,
                                                  newDistributionEnd,
                                                  _assets[asset].rewards[reward].index
                                                );
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function setEmissionPerSecond(
                                                address asset,
                                                address[] calldata rewards,
                                                uint88[] calldata newEmissionsPerSecond
                                              ) external override onlyEmissionManager {
                                                require(rewards.length == newEmissionsPerSecond.length, 'INVALID_INPUT');
                                                for (uint256 i = 0; i < rewards.length; i++) {
                                                  RewardsDataTypes.AssetData storage assetConfig = _assets[asset];
                                                  RewardsDataTypes.RewardData storage rewardConfig = _assets[asset].rewards[rewards[i]];
                                                  uint256 decimals = assetConfig.decimals;
                                                  require(
                                                    decimals != 0 && rewardConfig.lastUpdateTimestamp != 0,
                                                    'DISTRIBUTION_DOES_NOT_EXIST'
                                                  );
                                                  (uint256 newIndex, ) = _updateRewardData(
                                                    rewardConfig,
                                                    IScaledBalanceToken(asset).scaledTotalSupply(),
                                                    10**decimals
                                                  );
                                                  uint256 oldEmissionPerSecond = rewardConfig.emissionPerSecond;
                                                  rewardConfig.emissionPerSecond = newEmissionsPerSecond[i];
                                                  emit AssetConfigUpdated(
                                                    asset,
                                                    rewards[i],
                                                    oldEmissionPerSecond,
                                                    newEmissionsPerSecond[i],
                                                    rewardConfig.distributionEnd,
                                                    rewardConfig.distributionEnd,
                                                    newIndex
                                                  );
                                                }
                                              }
                                              /**
                                               * @dev Configure the _assets for a specific emission
                                               * @param rewardsInput The array of each asset configuration
                                               **/
                                              function _configureAssets(RewardsDataTypes.RewardsConfigInput[] memory rewardsInput) internal {
                                                for (uint256 i = 0; i < rewardsInput.length; i++) {
                                                  if (_assets[rewardsInput[i].asset].decimals == 0) {
                                                    //never initialized before, adding to the list of assets
                                                    _assetsList.push(rewardsInput[i].asset);
                                                  }
                                                  uint256 decimals = _assets[rewardsInput[i].asset].decimals = IERC20Detailed(
                                                    rewardsInput[i].asset
                                                  ).decimals();
                                                  RewardsDataTypes.RewardData storage rewardConfig = _assets[rewardsInput[i].asset].rewards[
                                                    rewardsInput[i].reward
                                                  ];
                                                  // Add reward address to asset available rewards if latestUpdateTimestamp is zero
                                                  if (rewardConfig.lastUpdateTimestamp == 0) {
                                                    _assets[rewardsInput[i].asset].availableRewards[
                                                      _assets[rewardsInput[i].asset].availableRewardsCount
                                                    ] = rewardsInput[i].reward;
                                                    _assets[rewardsInput[i].asset].availableRewardsCount++;
                                                  }
                                                  // Add reward address to global rewards list if still not enabled
                                                  if (_isRewardEnabled[rewardsInput[i].reward] == false) {
                                                    _isRewardEnabled[rewardsInput[i].reward] = true;
                                                    _rewardsList.push(rewardsInput[i].reward);
                                                  }
                                                  // Due emissions is still zero, updates only latestUpdateTimestamp
                                                  (uint256 newIndex, ) = _updateRewardData(
                                                    rewardConfig,
                                                    rewardsInput[i].totalSupply,
                                                    10**decimals
                                                  );
                                                  // Configure emission and distribution end of the reward per asset
                                                  uint88 oldEmissionsPerSecond = rewardConfig.emissionPerSecond;
                                                  uint32 oldDistributionEnd = rewardConfig.distributionEnd;
                                                  rewardConfig.emissionPerSecond = rewardsInput[i].emissionPerSecond;
                                                  rewardConfig.distributionEnd = rewardsInput[i].distributionEnd;
                                                  emit AssetConfigUpdated(
                                                    rewardsInput[i].asset,
                                                    rewardsInput[i].reward,
                                                    oldEmissionsPerSecond,
                                                    rewardsInput[i].emissionPerSecond,
                                                    oldDistributionEnd,
                                                    rewardsInput[i].distributionEnd,
                                                    newIndex
                                                  );
                                                }
                                              }
                                              /**
                                               * @dev Updates the state of the distribution for the specified reward
                                               * @param rewardData Storage pointer to the distribution reward config
                                               * @param totalSupply Current total of underlying assets for this distribution
                                               * @param assetUnit One unit of asset (10**decimals)
                                               * @return The new distribution index
                                               * @return True if the index was updated, false otherwise
                                               **/
                                              function _updateRewardData(
                                                RewardsDataTypes.RewardData storage rewardData,
                                                uint256 totalSupply,
                                                uint256 assetUnit
                                              ) internal returns (uint256, bool) {
                                                (uint256 oldIndex, uint256 newIndex) = _getAssetIndex(rewardData, totalSupply, assetUnit);
                                                bool indexUpdated;
                                                if (newIndex != oldIndex) {
                                                  require(newIndex <= type(uint104).max, 'INDEX_OVERFLOW');
                                                  indexUpdated = true;
                                                  //optimization: storing one after another saves one SSTORE
                                                  rewardData.index = uint104(newIndex);
                                                  rewardData.lastUpdateTimestamp = block.timestamp.toUint32();
                                                } else {
                                                  rewardData.lastUpdateTimestamp = block.timestamp.toUint32();
                                                }
                                                return (newIndex, indexUpdated);
                                              }
                                              /**
                                               * @dev Updates the state of the distribution for the specific user
                                               * @param rewardData Storage pointer to the distribution reward config
                                               * @param user The address of the user
                                               * @param userBalance The user balance of the asset
                                               * @param newAssetIndex The new index of the asset distribution
                                               * @param assetUnit One unit of asset (10**decimals)
                                               * @return The rewards accrued since the last update
                                               **/
                                              function _updateUserData(
                                                RewardsDataTypes.RewardData storage rewardData,
                                                address user,
                                                uint256 userBalance,
                                                uint256 newAssetIndex,
                                                uint256 assetUnit
                                              ) internal returns (uint256, bool) {
                                                uint256 userIndex = rewardData.usersData[user].index;
                                                uint256 rewardsAccrued;
                                                bool dataUpdated;
                                                if ((dataUpdated = userIndex != newAssetIndex)) {
                                                  // already checked for overflow in _updateRewardData
                                                  rewardData.usersData[user].index = uint104(newAssetIndex);
                                                  if (userBalance != 0) {
                                                    rewardsAccrued = _getRewards(userBalance, newAssetIndex, userIndex, assetUnit);
                                                    rewardData.usersData[user].accrued += rewardsAccrued.toUint128();
                                                  }
                                                }
                                                return (rewardsAccrued, dataUpdated);
                                              }
                                              /**
                                               * @dev Iterates and accrues all the rewards for asset of the specific user
                                               * @param asset The address of the reference asset of the distribution
                                               * @param user The user address
                                               * @param userBalance The current user asset balance
                                               * @param totalSupply Total supply of the asset
                                               **/
                                              function _updateData(
                                                address asset,
                                                address user,
                                                uint256 userBalance,
                                                uint256 totalSupply
                                              ) internal {
                                                uint256 assetUnit;
                                                uint256 numAvailableRewards = _assets[asset].availableRewardsCount;
                                                unchecked {
                                                  assetUnit = 10**_assets[asset].decimals;
                                                }
                                                if (numAvailableRewards == 0) {
                                                  return;
                                                }
                                                unchecked {
                                                  for (uint128 r = 0; r < numAvailableRewards; r++) {
                                                    address reward = _assets[asset].availableRewards[r];
                                                    RewardsDataTypes.RewardData storage rewardData = _assets[asset].rewards[reward];
                                                    (uint256 newAssetIndex, bool rewardDataUpdated) = _updateRewardData(
                                                      rewardData,
                                                      totalSupply,
                                                      assetUnit
                                                    );
                                                    (uint256 rewardsAccrued, bool userDataUpdated) = _updateUserData(
                                                      rewardData,
                                                      user,
                                                      userBalance,
                                                      newAssetIndex,
                                                      assetUnit
                                                    );
                                                    if (rewardDataUpdated || userDataUpdated) {
                                                      emit Accrued(asset, reward, user, newAssetIndex, newAssetIndex, rewardsAccrued);
                                                    }
                                                  }
                                                }
                                              }
                                              /**
                                               * @dev Accrues all the rewards of the assets specified in the userAssetBalances list
                                               * @param user The address of the user
                                               * @param userAssetBalances List of structs with the user balance and total supply of a set of assets
                                               **/
                                              function _updateDataMultiple(
                                                address user,
                                                RewardsDataTypes.UserAssetBalance[] memory userAssetBalances
                                              ) internal {
                                                for (uint256 i = 0; i < userAssetBalances.length; i++) {
                                                  _updateData(
                                                    userAssetBalances[i].asset,
                                                    user,
                                                    userAssetBalances[i].userBalance,
                                                    userAssetBalances[i].totalSupply
                                                  );
                                                }
                                              }
                                              /**
                                               * @dev Return the accrued unclaimed amount of a reward from a user over a list of distribution
                                               * @param user The address of the user
                                               * @param reward The address of the reward token
                                               * @param userAssetBalances List of structs with the user balance and total supply of a set of assets
                                               * @return unclaimedRewards The accrued rewards for the user until the moment
                                               **/
                                              function _getUserReward(
                                                address user,
                                                address reward,
                                                RewardsDataTypes.UserAssetBalance[] memory userAssetBalances
                                              ) internal view returns (uint256 unclaimedRewards) {
                                                // Add unrealized rewards
                                                for (uint256 i = 0; i < userAssetBalances.length; i++) {
                                                  if (userAssetBalances[i].userBalance == 0) {
                                                    unclaimedRewards += _assets[userAssetBalances[i].asset]
                                                      .rewards[reward]
                                                      .usersData[user]
                                                      .accrued;
                                                  } else {
                                                    unclaimedRewards +=
                                                      _getPendingRewards(user, reward, userAssetBalances[i]) +
                                                      _assets[userAssetBalances[i].asset].rewards[reward].usersData[user].accrued;
                                                  }
                                                }
                                                return unclaimedRewards;
                                              }
                                              /**
                                               * @dev Calculates the pending (not yet accrued) rewards since the last user action
                                               * @param user The address of the user
                                               * @param reward The address of the reward token
                                               * @param userAssetBalance struct with the user balance and total supply of the incentivized asset
                                               * @return The pending rewards for the user since the last user action
                                               **/
                                              function _getPendingRewards(
                                                address user,
                                                address reward,
                                                RewardsDataTypes.UserAssetBalance memory userAssetBalance
                                              ) internal view returns (uint256) {
                                                RewardsDataTypes.RewardData storage rewardData = _assets[userAssetBalance.asset].rewards[
                                                  reward
                                                ];
                                                uint256 assetUnit = 10**_assets[userAssetBalance.asset].decimals;
                                                (, uint256 nextIndex) = _getAssetIndex(rewardData, userAssetBalance.totalSupply, assetUnit);
                                                return
                                                  _getRewards(
                                                    userAssetBalance.userBalance,
                                                    nextIndex,
                                                    rewardData.usersData[user].index,
                                                    assetUnit
                                                  );
                                              }
                                              /**
                                               * @dev Internal function for the calculation of user's rewards on a distribution
                                               * @param userBalance Balance of the user asset on a distribution
                                               * @param reserveIndex Current index of the distribution
                                               * @param userIndex Index stored for the user, representation his staking moment
                                               * @param assetUnit One unit of asset (10**decimals)
                                               * @return The rewards
                                               **/
                                              function _getRewards(
                                                uint256 userBalance,
                                                uint256 reserveIndex,
                                                uint256 userIndex,
                                                uint256 assetUnit
                                              ) internal pure returns (uint256) {
                                                uint256 result = userBalance * (reserveIndex - userIndex);
                                                assembly {
                                                  result := div(result, assetUnit)
                                                }
                                                return result;
                                              }
                                              /**
                                               * @dev Calculates the next value of an specific distribution index, with validations
                                               * @param rewardData Storage pointer to the distribution reward config
                                               * @param totalSupply of the asset being rewarded
                                               * @param assetUnit One unit of asset (10**decimals)
                                               * @return The new index.
                                               **/
                                              function _getAssetIndex(
                                                RewardsDataTypes.RewardData storage rewardData,
                                                uint256 totalSupply,
                                                uint256 assetUnit
                                              ) internal view returns (uint256, uint256) {
                                                uint256 oldIndex = rewardData.index;
                                                uint256 distributionEnd = rewardData.distributionEnd;
                                                uint256 emissionPerSecond = rewardData.emissionPerSecond;
                                                uint256 lastUpdateTimestamp = rewardData.lastUpdateTimestamp;
                                                if (
                                                  emissionPerSecond == 0 ||
                                                  totalSupply == 0 ||
                                                  lastUpdateTimestamp == block.timestamp ||
                                                  lastUpdateTimestamp >= distributionEnd
                                                ) {
                                                  return (oldIndex, oldIndex);
                                                }
                                                uint256 currentTimestamp = block.timestamp > distributionEnd
                                                  ? distributionEnd
                                                  : block.timestamp;
                                                uint256 timeDelta = currentTimestamp - lastUpdateTimestamp;
                                                uint256 firstTerm = emissionPerSecond * timeDelta * assetUnit;
                                                assembly {
                                                  firstTerm := div(firstTerm, totalSupply)
                                                }
                                                return (oldIndex, (firstTerm + oldIndex));
                                              }
                                              /**
                                               * @dev Get user balances and total supply of all the assets specified by the assets parameter
                                               * @param assets List of assets to retrieve user balance and total supply
                                               * @param user Address of the user
                                               * @return userAssetBalances contains a list of structs with user balance and total supply of the given assets
                                               */
                                              function _getUserAssetBalances(address[] calldata assets, address user)
                                                internal
                                                view
                                                virtual
                                                returns (RewardsDataTypes.UserAssetBalance[] memory userAssetBalances);
                                              /// @inheritdoc IRewardsDistributor
                                              function getAssetDecimals(address asset) external view returns (uint8) {
                                                return _assets[asset].decimals;
                                              }
                                              /// @inheritdoc IRewardsDistributor
                                              function getEmissionManager() external view returns (address) {
                                                return EMISSION_MANAGER;
                                              }
                                            }
                                            // SPDX-License-Identifier: AGPL-3.0
                                            pragma solidity ^0.8.10;
                                            import {IRewardsDistributor} from './IRewardsDistributor.sol';
                                            import {ITransferStrategyBase} from './ITransferStrategyBase.sol';
                                            import {IEACAggregatorProxy} from '../../misc/interfaces/IEACAggregatorProxy.sol';
                                            import {RewardsDataTypes} from '../libraries/RewardsDataTypes.sol';
                                            /**
                                             * @title IRewardsController
                                             * @author Aave
                                             * @notice Defines the basic interface for a Rewards Controller.
                                             */
                                            interface IRewardsController is IRewardsDistributor {
                                              /**
                                               * @dev Emitted when a new address is whitelisted as claimer of rewards on behalf of a user
                                               * @param user The address of the user
                                               * @param claimer The address of the claimer
                                               */
                                              event ClaimerSet(address indexed user, address indexed claimer);
                                              /**
                                               * @dev Emitted when rewards are claimed
                                               * @param user The address of the user rewards has been claimed on behalf of
                                               * @param reward The address of the token reward is claimed
                                               * @param to The address of the receiver of the rewards
                                               * @param claimer The address of the claimer
                                               * @param amount The amount of rewards claimed
                                               */
                                              event RewardsClaimed(
                                                address indexed user,
                                                address indexed reward,
                                                address indexed to,
                                                address claimer,
                                                uint256 amount
                                              );
                                              /**
                                               * @dev Emitted when a transfer strategy is installed for the reward distribution
                                               * @param reward The address of the token reward
                                               * @param transferStrategy The address of TransferStrategy contract
                                               */
                                              event TransferStrategyInstalled(address indexed reward, address indexed transferStrategy);
                                              /**
                                               * @dev Emitted when the reward oracle is updated
                                               * @param reward The address of the token reward
                                               * @param rewardOracle The address of oracle
                                               */
                                              event RewardOracleUpdated(address indexed reward, address indexed rewardOracle);
                                              /**
                                               * @dev Whitelists an address to claim the rewards on behalf of another address
                                               * @param user The address of the user
                                               * @param claimer The address of the claimer
                                               */
                                              function setClaimer(address user, address claimer) external;
                                              /**
                                               * @dev Sets a TransferStrategy logic contract that determines the logic of the rewards transfer
                                               * @param reward The address of the reward token
                                               * @param transferStrategy The address of the TransferStrategy logic contract
                                               */
                                              function setTransferStrategy(address reward, ITransferStrategyBase transferStrategy) external;
                                              /**
                                               * @dev Sets an Aave Oracle contract to enforce rewards with a source of value.
                                               * @notice At the moment of reward configuration, the Incentives Controller performs
                                               * a check to see if the reward asset oracle is compatible with IEACAggregator proxy.
                                               * This check is enforced for integrators to be able to show incentives at
                                               * the current Aave UI without the need to setup an external price registry
                                               * @param reward The address of the reward to set the price aggregator
                                               * @param rewardOracle The address of price aggregator that follows IEACAggregatorProxy interface
                                               */
                                              function setRewardOracle(address reward, IEACAggregatorProxy rewardOracle) external;
                                              /**
                                               * @dev Get the price aggregator oracle address
                                               * @param reward The address of the reward
                                               * @return The price oracle of the reward
                                               */
                                              function getRewardOracle(address reward) external view returns (address);
                                              /**
                                               * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)
                                               * @param user The address of the user
                                               * @return The claimer address
                                               */
                                              function getClaimer(address user) external view returns (address);
                                              /**
                                               * @dev Returns the Transfer Strategy implementation contract address being used for a reward address
                                               * @param reward The address of the reward
                                               * @return The address of the TransferStrategy contract
                                               */
                                              function getTransferStrategy(address reward) external view returns (address);
                                              /**
                                               * @dev Configure assets to incentivize with an emission of rewards per second until the end of distribution.
                                               * @param config The assets configuration input, the list of structs contains the following fields:
                                               *   uint104 emissionPerSecond: The emission per second following rewards unit decimals.
                                               *   uint256 totalSupply: The total supply of the asset to incentivize
                                               *   uint40 distributionEnd: The end of the distribution of the incentives for an asset
                                               *   address asset: The asset address to incentivize
                                               *   address reward: The reward token address
                                               *   ITransferStrategy transferStrategy: The TransferStrategy address with the install hook and claim logic.
                                               *   IEACAggregatorProxy rewardOracle: The Price Oracle of a reward to visualize the incentives at the UI Frontend.
                                               *                                     Must follow Chainlink Aggregator IEACAggregatorProxy interface to be compatible.
                                               */
                                              function configureAssets(RewardsDataTypes.RewardsConfigInput[] memory config) external;
                                              /**
                                               * @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;
                                              /**
                                               * @dev Claims reward for a user to the desired address, on all the assets of the pool, accumulating the pending rewards
                                               * @param assets List of assets to check eligible distributions before claiming rewards
                                               * @param amount The amount of rewards to claim
                                               * @param to The address that will be receiving the rewards
                                               * @param reward The address of the reward token
                                               * @return The amount of rewards claimed
                                               **/
                                              function claimRewards(
                                                address[] calldata assets,
                                                uint256 amount,
                                                address to,
                                                address reward
                                              ) external returns (uint256);
                                              /**
                                               * @dev Claims reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards. The
                                               * caller must be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager
                                               * @param assets The list of assets to check eligible distributions before claiming rewards
                                               * @param amount The amount of rewards to claim
                                               * @param user The address to check and claim rewards
                                               * @param to The address that will be receiving the rewards
                                               * @param reward The address of the reward token
                                               * @return The amount of rewards claimed
                                               **/
                                              function claimRewardsOnBehalf(
                                                address[] calldata assets,
                                                uint256 amount,
                                                address user,
                                                address to,
                                                address reward
                                              ) external returns (uint256);
                                              /**
                                               * @dev Claims reward for msg.sender, on all the assets of the pool, accumulating the pending rewards
                                               * @param assets The list of assets to check eligible distributions before claiming rewards
                                               * @param amount The amount of rewards to claim
                                               * @param reward The address of the reward token
                                               * @return The amount of rewards claimed
                                               **/
                                              function claimRewardsToSelf(
                                                address[] calldata assets,
                                                uint256 amount,
                                                address reward
                                              ) external returns (uint256);
                                              /**
                                               * @dev Claims all rewards for a user to the desired address, on all the assets of the pool, accumulating the pending rewards
                                               * @param assets The list of assets to check eligible distributions before claiming rewards
                                               * @param to The address that will be receiving the rewards
                                               * @return rewardsList List of addresses of the reward tokens
                                               * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardList"
                                               **/
                                              function claimAllRewards(address[] calldata assets, address to)
                                                external
                                                returns (address[] memory rewardsList, uint256[] memory claimedAmounts);
                                              /**
                                               * @dev Claims all rewards for a user on behalf, on all the assets of the pool, accumulating the pending rewards. The caller must
                                               * be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager
                                               * @param assets The list of assets to check eligible distributions before claiming rewards
                                               * @param user The address to check and claim rewards
                                               * @param to The address that will be receiving the rewards
                                               * @return rewardsList List of addresses of the reward tokens
                                               * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardsList"
                                               **/
                                              function claimAllRewardsOnBehalf(
                                                address[] calldata assets,
                                                address user,
                                                address to
                                              ) external returns (address[] memory rewardsList, uint256[] memory claimedAmounts);
                                              /**
                                               * @dev Claims all reward for msg.sender, on all the assets of the pool, accumulating the pending rewards
                                               * @param assets The list of assets to check eligible distributions before claiming rewards
                                               * @return rewardsList List of addresses of the reward tokens
                                               * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardsList"
                                               **/
                                              function claimAllRewardsToSelf(address[] calldata assets)
                                                external
                                                returns (address[] memory rewardsList, uint256[] memory claimedAmounts);
                                            }
                                            // SPDX-License-Identifier: AGPL-3.0
                                            pragma solidity ^0.8.10;
                                            /**
                                             * @title IRewardsDistributor
                                             * @author Aave
                                             * @notice Defines the basic interface for a Rewards Distributor.
                                             */
                                            interface IRewardsDistributor {
                                              /**
                                               * @dev Emitted when the configuration of the rewards of an asset is updated.
                                               * @param asset The address of the incentivized asset
                                               * @param reward The address of the reward token
                                               * @param oldEmission The old emissions per second value of the reward distribution
                                               * @param newEmission The new emissions per second value of the reward distribution
                                               * @param oldDistributionEnd The old end timestamp of the reward distribution
                                               * @param newDistributionEnd The new end timestamp of the reward distribution
                                               * @param assetIndex The index of the asset distribution
                                               */
                                              event AssetConfigUpdated(
                                                address indexed asset,
                                                address indexed reward,
                                                uint256 oldEmission,
                                                uint256 newEmission,
                                                uint256 oldDistributionEnd,
                                                uint256 newDistributionEnd,
                                                uint256 assetIndex
                                              );
                                              /**
                                               * @dev Emitted when rewards of an asset are accrued on behalf of a user.
                                               * @param asset The address of the incentivized asset
                                               * @param reward The address of the reward token
                                               * @param user The address of the user that rewards are accrued on behalf of
                                               * @param assetIndex The index of the asset distribution
                                               * @param userIndex The index of the asset distribution on behalf of the user
                                               * @param rewardsAccrued The amount of rewards accrued
                                               */
                                              event Accrued(
                                                address indexed asset,
                                                address indexed reward,
                                                address indexed user,
                                                uint256 assetIndex,
                                                uint256 userIndex,
                                                uint256 rewardsAccrued
                                              );
                                              /**
                                               * @dev Sets the end date for the distribution
                                               * @param asset The asset to incentivize
                                               * @param reward The reward token that incentives the asset
                                               * @param newDistributionEnd The end date of the incentivization, in unix time format
                                               **/
                                              function setDistributionEnd(
                                                address asset,
                                                address reward,
                                                uint32 newDistributionEnd
                                              ) external;
                                              /**
                                               * @dev Sets the emission per second of a set of reward distributions
                                               * @param asset The asset is being incentivized
                                               * @param rewards List of reward addresses are being distributed
                                               * @param newEmissionsPerSecond List of new reward emissions per second
                                               */
                                              function setEmissionPerSecond(
                                                address asset,
                                                address[] calldata rewards,
                                                uint88[] calldata newEmissionsPerSecond
                                              ) external;
                                              /**
                                               * @dev Gets the end date for the distribution
                                               * @param asset The incentivized asset
                                               * @param reward The reward token of the incentivized asset
                                               * @return The timestamp with the end of the distribution, in unix time format
                                               **/
                                              function getDistributionEnd(address asset, address reward) external view returns (uint256);
                                              /**
                                               * @dev Returns the index of a user on a reward distribution
                                               * @param user Address of the user
                                               * @param asset The incentivized asset
                                               * @param reward The reward token of the incentivized asset
                                               * @return The current user asset index, not including new distributions
                                               **/
                                              function getUserAssetIndex(
                                                address user,
                                                address asset,
                                                address reward
                                              ) external view returns (uint256);
                                              /**
                                               * @dev Returns the configuration of the distribution reward for a certain asset
                                               * @param asset The incentivized asset
                                               * @param reward The reward token of the incentivized asset
                                               * @return The index of the asset distribution
                                               * @return The emission per second of the reward distribution
                                               * @return The timestamp of the last update of the index
                                               * @return The timestamp of the distribution end
                                               **/
                                              function getRewardsData(address asset, address reward)
                                                external
                                                view
                                                returns (
                                                  uint256,
                                                  uint256,
                                                  uint256,
                                                  uint256
                                                );
                                              /**
                                               * @dev Calculates the next value of an specific distribution index, with validations.
                                               * @param asset The incentivized asset
                                               * @param reward The reward token of the incentivized asset
                                               * @return The old index of the asset distribution
                                               * @return The new index of the asset distribution
                                               **/
                                              function getAssetIndex(address asset, address reward) external view returns (uint256, uint256);
                                              /**
                                               * @dev Returns the list of available reward token addresses of an incentivized asset
                                               * @param asset The incentivized asset
                                               * @return List of rewards addresses of the input asset
                                               **/
                                              function getRewardsByAsset(address asset) external view returns (address[] memory);
                                              /**
                                               * @dev Returns the list of available reward addresses
                                               * @return List of rewards supported in this contract
                                               **/
                                              function getRewardsList() external view returns (address[] memory);
                                              /**
                                               * @dev Returns the accrued rewards balance of a user, not including virtually accrued rewards since last distribution.
                                               * @param user The address of the user
                                               * @param reward The address of the reward token
                                               * @return Unclaimed rewards, not including new distributions
                                               **/
                                              function getUserAccruedRewards(address user, address reward) external view returns (uint256);
                                              /**
                                               * @dev Returns a single rewards balance of a user, including virtually accrued and unrealized claimable rewards.
                                               * @param assets List of incentivized assets to check eligible distributions
                                               * @param user The address of the user
                                               * @param reward The address of the reward token
                                               * @return The rewards amount
                                               **/
                                              function getUserRewards(
                                                address[] calldata assets,
                                                address user,
                                                address reward
                                              ) external view returns (uint256);
                                              /**
                                               * @dev Returns a list all rewards of a user, including already accrued and unrealized claimable rewards
                                               * @param assets List of incentivized assets to check eligible distributions
                                               * @param user The address of the user
                                               * @return The list of reward addresses
                                               * @return The list of unclaimed amount of rewards
                                               **/
                                              function getAllUserRewards(address[] calldata assets, address user)
                                                external
                                                view
                                                returns (address[] memory, uint256[] memory);
                                              /**
                                               * @dev Returns the decimals of an asset to calculate the distribution delta
                                               * @param asset The address to retrieve decimals
                                               * @return The decimals of an underlying asset
                                               */
                                              function getAssetDecimals(address asset) external view returns (uint8);
                                              /**
                                               * @dev Returns the address of the emission manager
                                               * @return The address of the EmissionManager
                                               */
                                              function EMISSION_MANAGER() external view returns (address);
                                              /**
                                               * @dev Returns the address of the emission manager.
                                               * Deprecated: This getter is maintained for compatibility purposes. Use the `EMISSION_MANAGER()` function instead.
                                               * @return The address of the EmissionManager
                                               */
                                              function getEmissionManager() external view returns (address);
                                            }
                                            // SPDX-License-Identifier: AGPL-3.0
                                            pragma solidity ^0.8.10;
                                            interface ITransferStrategyBase {
                                              event EmergencyWithdrawal(
                                                address indexed caller,
                                                address indexed token,
                                                address indexed to,
                                                uint256 amount
                                              );
                                              /**
                                               * @dev Perform custom transfer logic via delegate call from source contract to a TransferStrategy implementation
                                               * @param to Account to transfer rewards
                                               * @param reward Address of the reward token
                                               * @param amount Amount to transfer to the "to" address parameter
                                               * @return Returns true bool if transfer logic succeeds
                                               */
                                              function performTransfer(
                                                address to,
                                                address reward,
                                                uint256 amount
                                              ) external returns (bool);
                                              /**
                                               * @return Returns the address of the Incentives Controller
                                               */
                                              function getIncentivesController() external view returns (address);
                                              /**
                                               * @return Returns the address of the Rewards admin
                                               */
                                              function getRewardsAdmin() external view returns (address);
                                              /**
                                               * @dev Perform an emergency token withdrawal only callable by the Rewards admin
                                               * @param token Address of the token to withdraw funds from this contract
                                               * @param to Address of the recipient of the withdrawal
                                               * @param amount Amount of the withdrawal
                                               */
                                              function emergencyWithdrawal(
                                                address token,
                                                address to,
                                                uint256 amount
                                              ) external;
                                            }
                                            // SPDX-License-Identifier: AGPL-3.0
                                            pragma solidity ^0.8.10;
                                            import {ITransferStrategyBase} from '../interfaces/ITransferStrategyBase.sol';
                                            import {IEACAggregatorProxy} from '../../misc/interfaces/IEACAggregatorProxy.sol';
                                            library RewardsDataTypes {
                                              struct RewardsConfigInput {
                                                uint88 emissionPerSecond;
                                                uint256 totalSupply;
                                                uint32 distributionEnd;
                                                address asset;
                                                address reward;
                                                ITransferStrategyBase transferStrategy;
                                                IEACAggregatorProxy rewardOracle;
                                              }
                                              struct UserAssetBalance {
                                                address asset;
                                                uint256 userBalance;
                                                uint256 totalSupply;
                                              }
                                              struct UserData {
                                                // Liquidity index of the reward distribution for the user
                                                uint104 index;
                                                // Amount of accrued rewards for the user since last user index update
                                                uint128 accrued;
                                              }
                                              struct RewardData {
                                                // Liquidity index of the reward distribution
                                                uint104 index;
                                                // Amount of reward tokens distributed per second
                                                uint88 emissionPerSecond;
                                                // Timestamp of the last reward index update
                                                uint32 lastUpdateTimestamp;
                                                // The end of the distribution of rewards (in seconds)
                                                uint32 distributionEnd;
                                                // Map of user addresses and their rewards data (userAddress => userData)
                                                mapping(address => UserData) usersData;
                                              }
                                              struct AssetData {
                                                // Map of reward token addresses and their data (rewardTokenAddress => rewardData)
                                                mapping(address => RewardData) rewards;
                                                // List of reward token addresses for the asset
                                                mapping(uint128 => address) availableRewards;
                                                // Count of reward tokens for the asset
                                                uint128 availableRewardsCount;
                                                // Number of decimals of the asset
                                                uint8 decimals;
                                              }
                                            }