ETH Price: $2,050.10 (-0.01%)

Transaction Decoder

Block:
13276028 at Sep-22-2021 02:12:55 PM +UTC
Transaction Fee:
0.00498059297881236 ETH $10.21
Gas Used:
96,040 Gas / 51.859568709 Gwei

Emitted Events:

318 AaveGovernanceV2.VoteEmitted( id=38, voter=[Sender] 0x7f18ca114c9d162b7078791582885f41caa4caed, support=True, votingPower=75130601529822042107 )

Account State Difference:

  Address   Before After State Difference Code
(Spark Pool)
26.250390706427273847 Eth26.250534766427273847 Eth0.00014406
0x7F18CA11...1caa4cAEd
0.448471747573998602 Eth
Nonce: 133
0.443491154595186242 Eth
Nonce: 134
0.00498059297881236
0xEC568fff...4DFE2252c
(Aave: Governance V2)

Execution Trace

AaveGovernanceV2.submitVote( proposalId=38, support=True )
  • GovernanceStrategy.getVotingPowerAt( user=0x7F18CA114C9D162B7078791582885F41caa4cAEd, blockNumber=13264819 ) => ( 75130601529822042107 )
    • InitializableAdminUpgradeabilityProxy.c2ffbb91( )
      • StakedTokenV2Rev3.getPowerAtBlock( user=0x7F18CA114C9D162B7078791582885F41caa4cAEd, blockNumber=13264819, delegationType=0 ) => ( 75130601529822042107 )
      • InitializableAdminUpgradeabilityProxy.c2ffbb91( )
        • AaveTokenV2.getPowerAtBlock( user=0x7F18CA114C9D162B7078791582885F41caa4cAEd, blockNumber=13264819, delegationType=0 ) => ( 0 )
          File 1 of 6: AaveGovernanceV2
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.7.5;
          pragma abicoder v2;
          
          function getChainId() pure returns (uint256) {
            uint256 chainId;
            assembly {
              chainId := chainid()
            }
            return chainId;
          }
          
          function isContract(address account) 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 Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with GSN meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
            function _msgSender() internal view virtual returns (address payable) {
              return msg.sender;
            }
          
            function _msgData() internal view virtual returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
            }
          }
          
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * `onlyOwner`, which can be applied to your functions to restrict their use to
           * the owner.
           */
          contract Ownable is Context {
            address private _owner;
          
            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          
            /**
             * @dev Initializes the contract setting the deployer as the initial owner.
             */
            constructor() {
              address msgSender = _msgSender();
              _owner = msgSender;
              emit OwnershipTransferred(address(0), msgSender);
            }
          
            /**
             * @dev Returns the address of the current owner.
             */
            function owner() public view returns (address) {
              return _owner;
            }
          
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
              require(_owner == _msgSender(), 'Ownable: caller is not the owner');
              _;
            }
          
            /**
             * @dev Leaves the contract without owner. It will not be possible to call
             * `onlyOwner` functions anymore. Can only be called by the current owner.
             *
             * NOTE: Renouncing ownership will leave the contract without an owner,
             * thereby removing any functionality that is only available to the owner.
             */
            function renounceOwnership() public virtual onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = address(0);
            }
          
            /**
             * @dev Transfers ownership of the contract to a new account (`newOwner`).
             * Can only be called by the current owner.
             */
            function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), 'Ownable: new owner is the zero address');
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
            }
          }
          
          /**
           * @dev Wrappers over Solidity's arithmetic operations with added overflow
           * checks.
           *
           * Arithmetic operations in Solidity wrap on overflow. This can easily result
           * in bugs, because programmers usually assume that an overflow raises an
           * error, which is the standard behavior in high level programming languages.
           * `SafeMath` restores this intuition by reverting the transaction when 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.
           */
          library SafeMath {
            /**
             * @dev Returns the addition of two unsigned integers, reverting on
             * overflow.
             *
             * Counterpart to Solidity's `+` operator.
             *
             * Requirements:
             * - Addition cannot overflow.
             */
            function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, 'SafeMath: addition overflow');
          
              return c;
            }
          
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             * - Subtraction cannot overflow.
             */
            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, 'SafeMath: subtraction overflow');
            }
          
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             * - Subtraction cannot overflow.
             */
            function sub(
              uint256 a,
              uint256 b,
              string memory errorMessage
            ) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
          
              return c;
            }
          
            /**
             * @dev Returns the multiplication of two unsigned integers, reverting on
             * overflow.
             *
             * Counterpart to Solidity's `*` operator.
             *
             * Requirements:
             * - Multiplication cannot overflow.
             */
            function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
              if (a == 0) {
                return 0;
              }
          
              uint256 c = a * b;
              require(c / a == b, 'SafeMath: multiplication overflow');
          
              return c;
            }
          
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts on
             * division by zero. The result is rounded towards zero.
             *
             * Counterpart to Solidity's `/` operator. Note: this function uses a
             * `revert` opcode (which leaves remaining gas untouched) while Solidity
             * uses an invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, 'SafeMath: division by zero');
            }
          
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
             * division by zero. The result is rounded towards zero.
             *
             * Counterpart to Solidity's `/` operator. Note: this function uses a
             * `revert` opcode (which leaves remaining gas untouched) while Solidity
             * uses an invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function div(
              uint256 a,
              uint256 b,
              string memory errorMessage
            ) internal pure returns (uint256) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
          
              return c;
            }
          
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts when dividing by zero.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, 'SafeMath: modulo by zero');
            }
          
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts with custom message when dividing by zero.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function mod(
              uint256 a,
              uint256 b,
              string memory errorMessage
            ) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
            }
          }
          
          interface IVotingStrategy {
            function getVotingPowerAt(address user, uint256 blockNumber) external view returns (uint256);
          }
          
          interface IProposalValidator {
            /**
             * @dev Called to validate a proposal (e.g when creating new proposal in Governance)
             * @param governance Governance Contract
             * @param user Address of the proposal creator
             * @param blockNumber Block Number against which to make the test (e.g proposal creation block -1).
             * @return boolean, true if can be created
             **/
            function validateCreatorOfProposal(
              IAaveGovernanceV2 governance,
              address user,
              uint256 blockNumber
            ) external view returns (bool);
          
            /**
             * @dev Called to validate the cancellation of a proposal
             * @param governance Governance Contract
             * @param user Address of the proposal creator
             * @param blockNumber Block Number against which to make the test (e.g proposal creation block -1).
             * @return boolean, true if can be cancelled
             **/
            function validateProposalCancellation(
              IAaveGovernanceV2 governance,
              address user,
              uint256 blockNumber
            ) external view returns (bool);
          
            /**
             * @dev Returns whether a user has enough Proposition Power to make a proposal.
             * @param governance Governance Contract
             * @param user Address of the user to be challenged.
             * @param blockNumber Block Number against which to make the challenge.
             * @return true if user has enough power
             **/
            function isPropositionPowerEnough(
              IAaveGovernanceV2 governance,
              address user,
              uint256 blockNumber
            ) external view returns (bool);
          
            /**
             * @dev Returns the minimum Proposition Power needed to create a proposition.
             * @param governance Governance Contract
             * @param blockNumber Blocknumber at which to evaluate
             * @return minimum Proposition Power needed
             **/
            function getMinimumPropositionPowerNeeded(IAaveGovernanceV2 governance, uint256 blockNumber)
              external
              view
              returns (uint256);
          
            /**
             * @dev Returns whether a proposal passed or not
             * @param governance Governance Contract
             * @param proposalId Id of the proposal to set
             * @return true if proposal passed
             **/
            function isProposalPassed(IAaveGovernanceV2 governance, uint256 proposalId)
              external
              view
              returns (bool);
          
            /**
             * @dev Check whether a proposal has reached quorum, ie has enough FOR-voting-power
             * Here quorum is not to understand as number of votes reached, but number of for-votes reached
             * @param governance Governance Contract
             * @param proposalId Id of the proposal to verify
             * @return voting power needed for a proposal to pass
             **/
            function isQuorumValid(IAaveGovernanceV2 governance, uint256 proposalId)
              external
              view
              returns (bool);
          
            /**
             * @dev Check whether a proposal has enough extra FOR-votes than AGAINST-votes
             * FOR VOTES - AGAINST VOTES > VOTE_DIFFERENTIAL * voting supply
             * @param governance Governance Contract
             * @param proposalId Id of the proposal to verify
             * @return true if enough For-Votes
             **/
            function isVoteDifferentialValid(IAaveGovernanceV2 governance, uint256 proposalId)
              external
              view
              returns (bool);
          
            /**
             * @dev Calculates the minimum amount of Voting Power needed for a proposal to Pass
             * @param votingSupply Total number of oustanding voting tokens
             * @return voting power needed for a proposal to pass
             **/
            function getMinimumVotingPowerNeeded(uint256 votingSupply) external view returns (uint256);
          
            /**
             * @dev Get proposition threshold constant value
             * @return the proposition threshold value (100 <=> 1%)
             **/
            function PROPOSITION_THRESHOLD() external view returns (uint256);
          
            /**
             * @dev Get voting duration constant value
             * @return the voting duration value in seconds
             **/
            function VOTING_DURATION() external view returns (uint256);
          
            /**
             * @dev Get the vote differential threshold constant value
             * to compare with % of for votes/total supply - % of against votes/total supply
             * @return the vote differential threshold value (100 <=> 1%)
             **/
            function VOTE_DIFFERENTIAL() external view returns (uint256);
          
            /**
             * @dev Get quorum threshold constant value
             * to compare with % of for votes/total supply
             * @return the quorum threshold value (100 <=> 1%)
             **/
            function MINIMUM_QUORUM() external view returns (uint256);
          
            /**
             * @dev precision helper: 100% = 10000
             * @return one hundred percents with our chosen precision
             **/
            function ONE_HUNDRED_WITH_PRECISION() external view returns (uint256);
          }
          
          interface IGovernanceStrategy {
            /**
             * @dev Returns the Proposition Power of a user at a specific block number.
             * @param user Address of the user.
             * @param blockNumber Blocknumber at which to fetch Proposition Power
             * @return Power number
             **/
            function getPropositionPowerAt(address user, uint256 blockNumber) external view returns (uint256);
          
            /**
             * @dev Returns the total supply of Outstanding Proposition Tokens
             * @param blockNumber Blocknumber at which to evaluate
             * @return total supply at blockNumber
             **/
            function getTotalPropositionSupplyAt(uint256 blockNumber) external view returns (uint256);
          
            /**
             * @dev Returns the total supply of Outstanding Voting Tokens
             * @param blockNumber Blocknumber at which to evaluate
             * @return total supply at blockNumber
             **/
            function getTotalVotingSupplyAt(uint256 blockNumber) external view returns (uint256);
          
            /**
             * @dev Returns the Vote Power of a user at a specific block number.
             * @param user Address of the user.
             * @param blockNumber Blocknumber at which to fetch Vote Power
             * @return Vote number
             **/
            function getVotingPowerAt(address user, uint256 blockNumber) external view returns (uint256);
          }
          
          interface IExecutorWithTimelock {
            /**
             * @dev emitted when a new pending admin is set
             * @param newPendingAdmin address of the new pending admin
             **/
            event NewPendingAdmin(address newPendingAdmin);
          
            /**
             * @dev emitted when a new admin is set
             * @param newAdmin address of the new admin
             **/
            event NewAdmin(address newAdmin);
          
            /**
             * @dev emitted when a new delay (between queueing and execution) is set
             * @param delay new delay
             **/
            event NewDelay(uint256 delay);
          
            /**
             * @dev emitted when a new (trans)action is Queued.
             * @param actionHash hash of the action
             * @param target address of the targeted contract
             * @param value wei value of the transaction
             * @param signature function signature of the transaction
             * @param data function arguments of the transaction or callData if signature empty
             * @param executionTime time at which to execute the transaction
             * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
             **/
            event QueuedAction(
              bytes32 actionHash,
              address indexed target,
              uint256 value,
              string signature,
              bytes data,
              uint256 executionTime,
              bool withDelegatecall
            );
          
            /**
             * @dev emitted when an action is Cancelled
             * @param actionHash hash of the action
             * @param target address of the targeted contract
             * @param value wei value of the transaction
             * @param signature function signature of the transaction
             * @param data function arguments of the transaction or callData if signature empty
             * @param executionTime time at which to execute the transaction
             * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
             **/
            event CancelledAction(
              bytes32 actionHash,
              address indexed target,
              uint256 value,
              string signature,
              bytes data,
              uint256 executionTime,
              bool withDelegatecall
            );
          
            /**
             * @dev emitted when an action is Cancelled
             * @param actionHash hash of the action
             * @param target address of the targeted contract
             * @param value wei value of the transaction
             * @param signature function signature of the transaction
             * @param data function arguments of the transaction or callData if signature empty
             * @param executionTime time at which to execute the transaction
             * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
             * @param resultData the actual callData used on the target
             **/
            event ExecutedAction(
              bytes32 actionHash,
              address indexed target,
              uint256 value,
              string signature,
              bytes data,
              uint256 executionTime,
              bool withDelegatecall,
              bytes resultData
            );
          
            /**
             * @dev Getter of the current admin address (should be governance)
             * @return The address of the current admin
             **/
            function getAdmin() external view returns (address);
          
            /**
             * @dev Getter of the current pending admin address
             * @return The address of the pending admin
             **/
            function getPendingAdmin() external view returns (address);
          
            /**
             * @dev Getter of the delay between queuing and execution
             * @return The delay in seconds
             **/
            function getDelay() external view returns (uint256);
          
            /**
             * @dev Returns whether an action (via actionHash) is queued
             * @param actionHash hash of the action to be checked
             * keccak256(abi.encode(target, value, signature, data, executionTime, withDelegatecall))
             * @return true if underlying action of actionHash is queued
             **/
            function isActionQueued(bytes32 actionHash) external view returns (bool);
          
            /**
             * @dev Checks whether a proposal is over its grace period
             * @param governance Governance contract
             * @param proposalId Id of the proposal against which to test
             * @return true of proposal is over grace period
             **/
            function isProposalOverGracePeriod(IAaveGovernanceV2 governance, uint256 proposalId)
              external
              view
              returns (bool);
          
            /**
             * @dev Getter of grace period constant
             * @return grace period in seconds
             **/
            function GRACE_PERIOD() external view returns (uint256);
          
            /**
             * @dev Getter of minimum delay constant
             * @return minimum delay in seconds
             **/
            function MINIMUM_DELAY() external view returns (uint256);
          
            /**
             * @dev Getter of maximum delay constant
             * @return maximum delay in seconds
             **/
            function MAXIMUM_DELAY() external view returns (uint256);
          
            /**
             * @dev Function, called by Governance, that queue a transaction, returns action hash
             * @param target smart contract target
             * @param value wei value of the transaction
             * @param signature function signature of the transaction
             * @param data function arguments of the transaction or callData if signature empty
             * @param executionTime time at which to execute the transaction
             * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
             **/
            function queueTransaction(
              address target,
              uint256 value,
              string memory signature,
              bytes memory data,
              uint256 executionTime,
              bool withDelegatecall
            ) external returns (bytes32);
          
            /**
             * @dev Function, called by Governance, that cancels a transaction, returns the callData executed
             * @param target smart contract target
             * @param value wei value of the transaction
             * @param signature function signature of the transaction
             * @param data function arguments of the transaction or callData if signature empty
             * @param executionTime time at which to execute the transaction
             * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
             **/
            function executeTransaction(
              address target,
              uint256 value,
              string memory signature,
              bytes memory data,
              uint256 executionTime,
              bool withDelegatecall
            ) external payable returns (bytes memory);
          
            /**
             * @dev Function, called by Governance, that cancels a transaction, returns action hash
             * @param target smart contract target
             * @param value wei value of the transaction
             * @param signature function signature of the transaction
             * @param data function arguments of the transaction or callData if signature empty
             * @param executionTime time at which to execute the transaction
             * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
             **/
            function cancelTransaction(
              address target,
              uint256 value,
              string memory signature,
              bytes memory data,
              uint256 executionTime,
              bool withDelegatecall
            ) external returns (bytes32);
          }
          
          interface IAaveGovernanceV2 {
            enum ProposalState {Pending, Canceled, Active, Failed, Succeeded, Queued, Expired, Executed}
          
            struct Vote {
              bool support;
              uint248 votingPower;
            }
          
            struct Proposal {
              uint256 id;
              address creator;
              IExecutorWithTimelock executor;
              address[] targets;
              uint256[] values;
              string[] signatures;
              bytes[] calldatas;
              bool[] withDelegatecalls;
              uint256 startBlock;
              uint256 endBlock;
              uint256 executionTime;
              uint256 forVotes;
              uint256 againstVotes;
              bool executed;
              bool canceled;
              address strategy;
              bytes32 ipfsHash;
              mapping(address => Vote) votes;
            }
          
            struct ProposalWithoutVotes {
              uint256 id;
              address creator;
              IExecutorWithTimelock executor;
              address[] targets;
              uint256[] values;
              string[] signatures;
              bytes[] calldatas;
              bool[] withDelegatecalls;
              uint256 startBlock;
              uint256 endBlock;
              uint256 executionTime;
              uint256 forVotes;
              uint256 againstVotes;
              bool executed;
              bool canceled;
              address strategy;
              bytes32 ipfsHash;
            }
          
            /**
             * @dev emitted when a new proposal is created
             * @param id Id of the proposal
             * @param creator address of the creator
             * @param executor The ExecutorWithTimelock contract that will execute the proposal
             * @param targets list of contracts called by proposal's associated transactions
             * @param values list of value in wei for each propoposal's associated transaction
             * @param signatures list of function signatures (can be empty) to be used when created the callData
             * @param calldatas list of calldatas: if associated signature empty, calldata ready, else calldata is arguments
             * @param withDelegatecalls boolean, true = transaction delegatecalls the taget, else calls the target
             * @param startBlock block number when vote starts
             * @param endBlock block number when vote ends
             * @param strategy address of the governanceStrategy contract
             * @param ipfsHash IPFS hash of the proposal
             **/
            event ProposalCreated(
              uint256 id,
              address indexed creator,
              IExecutorWithTimelock indexed executor,
              address[] targets,
              uint256[] values,
              string[] signatures,
              bytes[] calldatas,
              bool[] withDelegatecalls,
              uint256 startBlock,
              uint256 endBlock,
              address strategy,
              bytes32 ipfsHash
            );
          
            /**
             * @dev emitted when a proposal is canceled
             * @param id Id of the proposal
             **/
            event ProposalCanceled(uint256 id);
          
            /**
             * @dev emitted when a proposal is queued
             * @param id Id of the proposal
             * @param executionTime time when proposal underlying transactions can be executed
             * @param initiatorQueueing address of the initiator of the queuing transaction
             **/
            event ProposalQueued(uint256 id, uint256 executionTime, address indexed initiatorQueueing);
            /**
             * @dev emitted when a proposal is executed
             * @param id Id of the proposal
             * @param initiatorExecution address of the initiator of the execution transaction
             **/
            event ProposalExecuted(uint256 id, address indexed initiatorExecution);
            /**
             * @dev emitted when a vote is registered
             * @param id Id of the proposal
             * @param voter address of the voter
             * @param support boolean, true = vote for, false = vote against
             * @param votingPower Power of the voter/vote
             **/
            event VoteEmitted(uint256 id, address indexed voter, bool support, uint256 votingPower);
          
            event GovernanceStrategyChanged(address indexed newStrategy, address indexed initiatorChange);
          
            event VotingDelayChanged(uint256 newVotingDelay, address indexed initiatorChange);
          
            event ExecutorAuthorized(address executor);
          
            event ExecutorUnauthorized(address executor);
          
            /**
             * @dev Creates a Proposal (needs Proposition Power of creator > Threshold)
             * @param executor The ExecutorWithTimelock contract that will execute the proposal
             * @param targets list of contracts called by proposal's associated transactions
             * @param values list of value in wei for each propoposal's associated transaction
             * @param signatures list of function signatures (can be empty) to be used when created the callData
             * @param calldatas list of calldatas: if associated signature empty, calldata ready, else calldata is arguments
             * @param withDelegatecalls if true, transaction delegatecalls the taget, else calls the target
             * @param ipfsHash IPFS hash of the proposal
             **/
            function create(
              IExecutorWithTimelock executor,
              address[] memory targets,
              uint256[] memory values,
              string[] memory signatures,
              bytes[] memory calldatas,
              bool[] memory withDelegatecalls,
              bytes32 ipfsHash
            ) external returns (uint256);
          
            /**
             * @dev Cancels a Proposal,
             * either at anytime by guardian
             * or when proposal is Pending/Active and threshold no longer reached
             * @param proposalId id of the proposal
             **/
            function cancel(uint256 proposalId) external;
          
            /**
             * @dev Queue the proposal (If Proposal Succeeded)
             * @param proposalId id of the proposal to queue
             **/
            function queue(uint256 proposalId) external;
          
            /**
             * @dev Execute the proposal (If Proposal Queued)
             * @param proposalId id of the proposal to execute
             **/
            function execute(uint256 proposalId) external payable;
          
            /**
             * @dev Function allowing msg.sender to vote for/against a proposal
             * @param proposalId id of the proposal
             * @param support boolean, true = vote for, false = vote against
             **/
            function submitVote(uint256 proposalId, bool support) external;
          
            /**
             * @dev Function to register the vote of user that has voted offchain via signature
             * @param proposalId id of the proposal
             * @param support boolean, true = vote for, false = vote against
             * @param v v part of the voter signature
             * @param r r part of the voter signature
             * @param s s part of the voter signature
             **/
            function submitVoteBySignature(
              uint256 proposalId,
              bool support,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external;
          
            /**
             * @dev Set new GovernanceStrategy
             * Note: owner should be a timelocked executor, so needs to make a proposal
             * @param governanceStrategy new Address of the GovernanceStrategy contract
             **/
            function setGovernanceStrategy(address governanceStrategy) external;
          
            /**
             * @dev Set new Voting Delay (delay before a newly created proposal can be voted on)
             * Note: owner should be a timelocked executor, so needs to make a proposal
             * @param votingDelay new voting delay in seconds
             **/
            function setVotingDelay(uint256 votingDelay) external;
          
            /**
             * @dev Add new addresses to the list of authorized executors
             * @param executors list of new addresses to be authorized executors
             **/
            function authorizeExecutors(address[] memory executors) external;
          
            /**
             * @dev Remove addresses to the list of authorized executors
             * @param executors list of addresses to be removed as authorized executors
             **/
            function unauthorizeExecutors(address[] memory executors) external;
          
            /**
             * @dev Let the guardian abdicate from its priviledged rights
             **/
            function __abdicate() external;
          
            /**
             * @dev Getter of the current GovernanceStrategy address
             * @return The address of the current GovernanceStrategy contracts
             **/
            function getGovernanceStrategy() external view returns (address);
          
            /**
             * @dev Getter of the current Voting Delay (delay before a created proposal can be voted on)
             * Different from the voting duration
             * @return The voting delay in seconds
             **/
            function getVotingDelay() external view returns (uint256);
          
            /**
             * @dev Returns whether an address is an authorized executor
             * @param executor address to evaluate as authorized executor
             * @return true if authorized
             **/
            function isExecutorAuthorized(address executor) external view returns (bool);
          
            /**
             * @dev Getter the address of the guardian, that can mainly cancel proposals
             * @return The address of the guardian
             **/
            function getGuardian() external view returns (address);
          
            /**
             * @dev Getter of the proposal count (the current number of proposals ever created)
             * @return the proposal count
             **/
            function getProposalsCount() external view returns (uint256);
          
            /**
             * @dev Getter of a proposal by id
             * @param proposalId id of the proposal to get
             * @return the proposal as ProposalWithoutVotes memory object
             **/
            function getProposalById(uint256 proposalId) external view returns (ProposalWithoutVotes memory);
          
            /**
             * @dev Getter of the Vote of a voter about a proposal
             * Note: Vote is a struct: ({bool support, uint248 votingPower})
             * @param proposalId id of the proposal
             * @param voter address of the voter
             * @return The associated Vote memory object
             **/
            function getVoteOnProposal(uint256 proposalId, address voter) external view returns (Vote memory);
          
            /**
             * @dev Get the current state of a proposal
             * @param proposalId id of the proposal
             * @return The current state if the proposal
             **/
            function getProposalState(uint256 proposalId) external view returns (ProposalState);
          }
          
          /**
           * @title Governance V2 contract
           * @dev Main point of interaction with Aave protocol's governance
           * - Create a Proposal
           * - Cancel a Proposal
           * - Queue a Proposal
           * - Execute a Proposal
           * - Submit Vote to a Proposal
           * Proposal States : Pending => Active => Succeeded(/Failed) => Queued => Executed(/Expired)
           *                   The transition to "Canceled" can appear in multiple states
           * @author Aave
           **/
          contract AaveGovernanceV2 is Ownable, IAaveGovernanceV2 {
            using SafeMath for uint256;
          
            address private _governanceStrategy;
            uint256 private _votingDelay;
          
            uint256 private _proposalsCount;
            mapping(uint256 => Proposal) private _proposals;
            mapping(address => bool) private _authorizedExecutors;
          
            address private _guardian;
          
            bytes32 public constant DOMAIN_TYPEHASH = keccak256(
              'EIP712Domain(string name,uint256 chainId,address verifyingContract)'
            );
            bytes32 public constant VOTE_EMITTED_TYPEHASH = keccak256('VoteEmitted(uint256 id,bool support)');
            string public constant NAME = 'Aave Governance v2';
          
            modifier onlyGuardian() {
              require(msg.sender == _guardian, 'ONLY_BY_GUARDIAN');
              _;
            }
          
            constructor(
              address governanceStrategy,
              uint256 votingDelay,
              address guardian,
              address[] memory executors
            ) {
              _setGovernanceStrategy(governanceStrategy);
              _setVotingDelay(votingDelay);
              _guardian = guardian;
          
              authorizeExecutors(executors);
            }
          
            struct CreateVars {
              uint256 startBlock;
              uint256 endBlock;
              uint256 previousProposalsCount;
            }
          
            /**
             * @dev Creates a Proposal (needs to be validated by the Proposal Validator)
             * @param executor The ExecutorWithTimelock contract that will execute the proposal
             * @param targets list of contracts called by proposal's associated transactions
             * @param values list of value in wei for each propoposal's associated transaction
             * @param signatures list of function signatures (can be empty) to be used when created the callData
             * @param calldatas list of calldatas: if associated signature empty, calldata ready, else calldata is arguments
             * @param withDelegatecalls boolean, true = transaction delegatecalls the taget, else calls the target
             * @param ipfsHash IPFS hash of the proposal
             **/
            function create(
              IExecutorWithTimelock executor,
              address[] memory targets,
              uint256[] memory values,
              string[] memory signatures,
              bytes[] memory calldatas,
              bool[] memory withDelegatecalls,
              bytes32 ipfsHash
            ) external override returns (uint256) {
              require(targets.length != 0, 'INVALID_EMPTY_TARGETS');
              require(
                targets.length == values.length &&
                  targets.length == signatures.length &&
                  targets.length == calldatas.length &&
                  targets.length == withDelegatecalls.length,
                'INCONSISTENT_PARAMS_LENGTH'
              );
          
              require(isExecutorAuthorized(address(executor)), 'EXECUTOR_NOT_AUTHORIZED');
          
              require(
                IProposalValidator(address(executor)).validateCreatorOfProposal(
                  this,
                  msg.sender,
                  block.number - 1
                ),
                'PROPOSITION_CREATION_INVALID'
              );
          
              CreateVars memory vars;
          
              vars.startBlock = block.number.add(_votingDelay);
              vars.endBlock = vars.startBlock.add(IProposalValidator(address(executor)).VOTING_DURATION());
          
              vars.previousProposalsCount = _proposalsCount;
          
              Proposal storage newProposal = _proposals[vars.previousProposalsCount];
              newProposal.id = vars.previousProposalsCount;
              newProposal.creator = msg.sender;
              newProposal.executor = executor;
              newProposal.targets = targets;
              newProposal.values = values;
              newProposal.signatures = signatures;
              newProposal.calldatas = calldatas;
              newProposal.withDelegatecalls = withDelegatecalls;
              newProposal.startBlock = vars.startBlock;
              newProposal.endBlock = vars.endBlock;
              newProposal.strategy = _governanceStrategy;
              newProposal.ipfsHash = ipfsHash;
              _proposalsCount++;
          
              emit ProposalCreated(
                vars.previousProposalsCount,
                msg.sender,
                executor,
                targets,
                values,
                signatures,
                calldatas,
                withDelegatecalls,
                vars.startBlock,
                vars.endBlock,
                _governanceStrategy,
                ipfsHash
              );
          
              return newProposal.id;
            }
          
            /**
             * @dev Cancels a Proposal.
             * - Callable by the _guardian with relaxed conditions, or by anybody if the conditions of
             *   cancellation on the executor are fulfilled
             * @param proposalId id of the proposal
             **/
            function cancel(uint256 proposalId) external override {
              ProposalState state = getProposalState(proposalId);
              require(
                state != ProposalState.Executed &&
                  state != ProposalState.Canceled &&
                  state != ProposalState.Expired,
                'ONLY_BEFORE_EXECUTED'
              );
          
              Proposal storage proposal = _proposals[proposalId];
              require(
                msg.sender == _guardian ||
                  IProposalValidator(address(proposal.executor)).validateProposalCancellation(
                    this,
                    proposal.creator,
                    block.number - 1
                  ),
                'PROPOSITION_CANCELLATION_INVALID'
              );
              proposal.canceled = true;
              for (uint256 i = 0; i < proposal.targets.length; i++) {
                proposal.executor.cancelTransaction(
                  proposal.targets[i],
                  proposal.values[i],
                  proposal.signatures[i],
                  proposal.calldatas[i],
                  proposal.executionTime,
                  proposal.withDelegatecalls[i]
                );
              }
          
              emit ProposalCanceled(proposalId);
            }
          
            /**
             * @dev Queue the proposal (If Proposal Succeeded)
             * @param proposalId id of the proposal to queue
             **/
            function queue(uint256 proposalId) external override {
              require(getProposalState(proposalId) == ProposalState.Succeeded, 'INVALID_STATE_FOR_QUEUE');
              Proposal storage proposal = _proposals[proposalId];
              uint256 executionTime = block.timestamp.add(proposal.executor.getDelay());
              for (uint256 i = 0; i < proposal.targets.length; i++) {
                _queueOrRevert(
                  proposal.executor,
                  proposal.targets[i],
                  proposal.values[i],
                  proposal.signatures[i],
                  proposal.calldatas[i],
                  executionTime,
                  proposal.withDelegatecalls[i]
                );
              }
              proposal.executionTime = executionTime;
          
              emit ProposalQueued(proposalId, executionTime, msg.sender);
            }
          
            /**
             * @dev Execute the proposal (If Proposal Queued)
             * @param proposalId id of the proposal to execute
             **/
            function execute(uint256 proposalId) external payable override {
              require(getProposalState(proposalId) == ProposalState.Queued, 'ONLY_QUEUED_PROPOSALS');
              Proposal storage proposal = _proposals[proposalId];
              proposal.executed = true;
              for (uint256 i = 0; i < proposal.targets.length; i++) {
                proposal.executor.executeTransaction{value: proposal.values[i]}(
                  proposal.targets[i],
                  proposal.values[i],
                  proposal.signatures[i],
                  proposal.calldatas[i],
                  proposal.executionTime,
                  proposal.withDelegatecalls[i]
                );
              }
              emit ProposalExecuted(proposalId, msg.sender);
            }
          
            /**
             * @dev Function allowing msg.sender to vote for/against a proposal
             * @param proposalId id of the proposal
             * @param support boolean, true = vote for, false = vote against
             **/
            function submitVote(uint256 proposalId, bool support) external override {
              return _submitVote(msg.sender, proposalId, support);
            }
          
            /**
             * @dev Function to register the vote of user that has voted offchain via signature
             * @param proposalId id of the proposal
             * @param support boolean, true = vote for, false = vote against
             * @param v v part of the voter signature
             * @param r r part of the voter signature
             * @param s s part of the voter signature
             **/
            function submitVoteBySignature(
              uint256 proposalId,
              bool support,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external override {
              bytes32 digest = keccak256(
                abi.encodePacked(
                  '\x19\x01',
                  keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(NAME)), getChainId(), address(this))),
                  keccak256(abi.encode(VOTE_EMITTED_TYPEHASH, proposalId, support))
                )
              );
              address signer = ecrecover(digest, v, r, s);
              require(signer != address(0), 'INVALID_SIGNATURE');
              return _submitVote(signer, proposalId, support);
            }
          
            /**
             * @dev Set new GovernanceStrategy
             * Note: owner should be a timelocked executor, so needs to make a proposal
             * @param governanceStrategy new Address of the GovernanceStrategy contract
             **/
            function setGovernanceStrategy(address governanceStrategy) external override onlyOwner {
              _setGovernanceStrategy(governanceStrategy);
            }
          
            /**
             * @dev Set new Voting Delay (delay before a newly created proposal can be voted on)
             * Note: owner should be a timelocked executor, so needs to make a proposal
             * @param votingDelay new voting delay in terms of blocks
             **/
            function setVotingDelay(uint256 votingDelay) external override onlyOwner {
              _setVotingDelay(votingDelay);
            }
          
            /**
             * @dev Add new addresses to the list of authorized executors
             * @param executors list of new addresses to be authorized executors
             **/
            function authorizeExecutors(address[] memory executors) public override onlyOwner {
              for (uint256 i = 0; i < executors.length; i++) {
                _authorizeExecutor(executors[i]);
              }
            }
          
            /**
             * @dev Remove addresses to the list of authorized executors
             * @param executors list of addresses to be removed as authorized executors
             **/
            function unauthorizeExecutors(address[] memory executors) public override onlyOwner {
              for (uint256 i = 0; i < executors.length; i++) {
                _unauthorizeExecutor(executors[i]);
              }
            }
          
            /**
             * @dev Let the guardian abdicate from its priviledged rights
             **/
            function __abdicate() external override onlyGuardian {
              _guardian = address(0);
            }
          
            /**
             * @dev Getter of the current GovernanceStrategy address
             * @return The address of the current GovernanceStrategy contracts
             **/
            function getGovernanceStrategy() external view override returns (address) {
              return _governanceStrategy;
            }
          
            /**
             * @dev Getter of the current Voting Delay (delay before a created proposal can be voted on)
             * Different from the voting duration
             * @return The voting delay in number of blocks
             **/
            function getVotingDelay() external view override returns (uint256) {
              return _votingDelay;
            }
          
            /**
             * @dev Returns whether an address is an authorized executor
             * @param executor address to evaluate as authorized executor
             * @return true if authorized
             **/
            function isExecutorAuthorized(address executor) public view override returns (bool) {
              return _authorizedExecutors[executor];
            }
          
            /**
             * @dev Getter the address of the guardian, that can mainly cancel proposals
             * @return The address of the guardian
             **/
            function getGuardian() external view override returns (address) {
              return _guardian;
            }
          
            /**
             * @dev Getter of the proposal count (the current number of proposals ever created)
             * @return the proposal count
             **/
            function getProposalsCount() external view override returns (uint256) {
              return _proposalsCount;
            }
          
            /**
             * @dev Getter of a proposal by id
             * @param proposalId id of the proposal to get
             * @return the proposal as ProposalWithoutVotes memory object
             **/
            function getProposalById(uint256 proposalId)
              external
              view
              override
              returns (ProposalWithoutVotes memory)
            {
              Proposal storage proposal = _proposals[proposalId];
              ProposalWithoutVotes memory proposalWithoutVotes = ProposalWithoutVotes({
                id: proposal.id,
                creator: proposal.creator,
                executor: proposal.executor,
                targets: proposal.targets,
                values: proposal.values,
                signatures: proposal.signatures,
                calldatas: proposal.calldatas,
                withDelegatecalls: proposal.withDelegatecalls,
                startBlock: proposal.startBlock,
                endBlock: proposal.endBlock,
                executionTime: proposal.executionTime,
                forVotes: proposal.forVotes,
                againstVotes: proposal.againstVotes,
                executed: proposal.executed,
                canceled: proposal.canceled,
                strategy: proposal.strategy,
                ipfsHash: proposal.ipfsHash
              });
          
              return proposalWithoutVotes;
            }
          
            /**
             * @dev Getter of the Vote of a voter about a proposal
             * Note: Vote is a struct: ({bool support, uint248 votingPower})
             * @param proposalId id of the proposal
             * @param voter address of the voter
             * @return The associated Vote memory object
             **/
            function getVoteOnProposal(uint256 proposalId, address voter)
              external
              view
              override
              returns (Vote memory)
            {
              return _proposals[proposalId].votes[voter];
            }
          
            /**
             * @dev Get the current state of a proposal
             * @param proposalId id of the proposal
             * @return The current state if the proposal
             **/
            function getProposalState(uint256 proposalId) public view override returns (ProposalState) {
              require(_proposalsCount >= proposalId, 'INVALID_PROPOSAL_ID');
              Proposal storage proposal = _proposals[proposalId];
              if (proposal.canceled) {
                return ProposalState.Canceled;
              } else if (block.number <= proposal.startBlock) {
                return ProposalState.Pending;
              } else if (block.number <= proposal.endBlock) {
                return ProposalState.Active;
              } else if (!IProposalValidator(address(proposal.executor)).isProposalPassed(this, proposalId)) {
                return ProposalState.Failed;
              } else if (proposal.executionTime == 0) {
                return ProposalState.Succeeded;
              } else if (proposal.executed) {
                return ProposalState.Executed;
              } else if (proposal.executor.isProposalOverGracePeriod(this, proposalId)) {
                return ProposalState.Expired;
              } else {
                return ProposalState.Queued;
              }
            }
          
            function _queueOrRevert(
              IExecutorWithTimelock executor,
              address target,
              uint256 value,
              string memory signature,
              bytes memory callData,
              uint256 executionTime,
              bool withDelegatecall
            ) internal {
              require(
                !executor.isActionQueued(
                  keccak256(abi.encode(target, value, signature, callData, executionTime, withDelegatecall))
                ),
                'DUPLICATED_ACTION'
              );
              executor.queueTransaction(target, value, signature, callData, executionTime, withDelegatecall);
            }
          
            function _submitVote(
              address voter,
              uint256 proposalId,
              bool support
            ) internal {
              require(getProposalState(proposalId) == ProposalState.Active, 'VOTING_CLOSED');
              Proposal storage proposal = _proposals[proposalId];
              Vote storage vote = proposal.votes[voter];
          
              require(vote.votingPower == 0, 'VOTE_ALREADY_SUBMITTED');
          
              uint256 votingPower = IVotingStrategy(proposal.strategy).getVotingPowerAt(
                voter,
                proposal.startBlock
              );
          
              if (support) {
                proposal.forVotes = proposal.forVotes.add(votingPower);
              } else {
                proposal.againstVotes = proposal.againstVotes.add(votingPower);
              }
          
              vote.support = support;
              vote.votingPower = uint248(votingPower);
          
              emit VoteEmitted(proposalId, voter, support, votingPower);
            }
          
            function _setGovernanceStrategy(address governanceStrategy) internal {
              _governanceStrategy = governanceStrategy;
          
              emit GovernanceStrategyChanged(governanceStrategy, msg.sender);
            }
          
            function _setVotingDelay(uint256 votingDelay) internal {
              _votingDelay = votingDelay;
          
              emit VotingDelayChanged(votingDelay, msg.sender);
            }
          
            function _authorizeExecutor(address executor) internal {
              _authorizedExecutors[executor] = true;
              emit ExecutorAuthorized(executor);
            }
          
            function _unauthorizeExecutor(address executor) internal {
              _authorizedExecutors[executor] = false;
              emit ExecutorUnauthorized(executor);
            }
          }

          File 2 of 6: GovernanceStrategy
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.7.5;
          pragma abicoder v2;
          
          interface IERC20 {
            function totalSupplyAt(uint256 blockNumber) external view returns (uint256);
          
            function balanceOf(address account) external view returns (uint256);
          }
          
          interface IGovernancePowerDelegationToken {
            enum DelegationType {VOTING_POWER, PROPOSITION_POWER}
          
            /**
             * @dev get the power of a user at a specified block
             * @param user address of the user
             * @param blockNumber block number at which to get power
             * @param delegationType delegation type (propose/vote)
             **/
            function getPowerAtBlock(
              address user,
              uint256 blockNumber,
              DelegationType delegationType
            ) external view returns (uint256);
          }
          
          interface IGovernanceStrategy {
            /**
             * @dev Returns the Proposition Power of a user at a specific block number.
             * @param user Address of the user.
             * @param blockNumber Blocknumber at which to fetch Proposition Power
             * @return Power number
             **/
            function getPropositionPowerAt(address user, uint256 blockNumber) external view returns (uint256);
          
            /**
             * @dev Returns the total supply of Outstanding Proposition Tokens
             * @param blockNumber Blocknumber at which to evaluate
             * @return total supply at blockNumber
             **/
            function getTotalPropositionSupplyAt(uint256 blockNumber) external view returns (uint256);
          
            /**
             * @dev Returns the total supply of Outstanding Voting Tokens
             * @param blockNumber Blocknumber at which to evaluate
             * @return total supply at blockNumber
             **/
            function getTotalVotingSupplyAt(uint256 blockNumber) external view returns (uint256);
          
            /**
             * @dev Returns the Vote Power of a user at a specific block number.
             * @param user Address of the user.
             * @param blockNumber Blocknumber at which to fetch Vote Power
             * @return Vote number
             **/
            function getVotingPowerAt(address user, uint256 blockNumber) external view returns (uint256);
          }
          
          /**
           * @title Governance Strategy contract
           * @dev Smart contract containing logic to measure users' relative power to propose and vote.
           * User Power = User Power from Aave Token + User Power from stkAave Token.
           * User Power from Token = Token Power + Token Power as Delegatee [- Token Power if user has delegated]
           * Two wrapper functions linked to Aave Tokens's GovernancePowerDelegationERC20.sol implementation
           * - getPropositionPowerAt: fetching a user Proposition Power at a specified block
           * - getVotingPowerAt: fetching a user Voting Power at a specified block
           * @author Aave
           **/
          contract GovernanceStrategy is IGovernanceStrategy {
            address public immutable AAVE;
            address public immutable STK_AAVE;
          
            /**
             * @dev Constructor, register tokens used for Voting and Proposition Powers.
             * @param aave The address of the AAVE Token contract.
             * @param stkAave The address of the stkAAVE Token Contract
             **/
            constructor(address aave, address stkAave) {
              AAVE = aave;
              STK_AAVE = stkAave;
            }
          
            /**
             * @dev Returns the total supply of Proposition Tokens Available for Governance
             * = AAVE Available for governance      + stkAAVE available
             * The supply of AAVE staked in stkAAVE are not taken into account so:
             * = (Supply of AAVE - AAVE in stkAAVE) + (Supply of stkAAVE)
             * = Supply of AAVE, Since the supply of stkAAVE is equal to the number of AAVE staked
             * @param blockNumber Blocknumber at which to evaluate
             * @return total supply at blockNumber
             **/
            function getTotalPropositionSupplyAt(uint256 blockNumber) public view override returns (uint256) {
              return IERC20(AAVE).totalSupplyAt(blockNumber);
            }
          
            /**
             * @dev Returns the total supply of Outstanding Voting Tokens
             * @param blockNumber Blocknumber at which to evaluate
             * @return total supply at blockNumber
             **/
            function getTotalVotingSupplyAt(uint256 blockNumber) public view override returns (uint256) {
              return getTotalPropositionSupplyAt(blockNumber);
            }
          
            /**
             * @dev Returns the Proposition Power of a user at a specific block number.
             * @param user Address of the user.
             * @param blockNumber Blocknumber at which to fetch Proposition Power
             * @return Power number
             **/
            function getPropositionPowerAt(address user, uint256 blockNumber)
              public
              view
              override
              returns (uint256)
            {
              return
                _getPowerByTypeAt(
                  user,
                  blockNumber,
                  IGovernancePowerDelegationToken.DelegationType.PROPOSITION_POWER
                );
            }
          
            /**
             * @dev Returns the Vote Power of a user at a specific block number.
             * @param user Address of the user.
             * @param blockNumber Blocknumber at which to fetch Vote Power
             * @return Vote number
             **/
            function getVotingPowerAt(address user, uint256 blockNumber)
              public
              view
              override
              returns (uint256)
            {
              return
                _getPowerByTypeAt(
                  user,
                  blockNumber,
                  IGovernancePowerDelegationToken.DelegationType.VOTING_POWER
                );
            }
          
            function _getPowerByTypeAt(
              address user,
              uint256 blockNumber,
              IGovernancePowerDelegationToken.DelegationType powerType
            ) internal view returns (uint256) {
              return
                IGovernancePowerDelegationToken(AAVE).getPowerAtBlock(user, blockNumber, powerType) +
                IGovernancePowerDelegationToken(STK_AAVE).getPowerAtBlock(user, blockNumber, powerType);
            }
          }

          File 3 of 6: InitializableAdminUpgradeabilityProxy
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          pragma experimental ABIEncoderV2;
          import {DistributionTypes} from '../lib/DistributionTypes.sol';
          interface IAaveDistributionManager {
            function configureAssets(DistributionTypes.AssetConfigInput[] calldata assetsConfigInput) external;
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          pragma experimental ABIEncoderV2;
          library DistributionTypes {
            struct AssetConfigInput {
              uint128 emissionPerSecond;
              uint256 totalStaked;
              address underlyingAsset;
            }
            struct UserStakeInput {
              address underlyingAsset;
              uint256 stakedByUser;
              uint256 totalStaked;
            }
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          pragma experimental ABIEncoderV2;
          interface IAaveIncentivesController {
            function handleAction(
              address asset,
              uint256 userBalance,
              uint256 totalSupply
            ) external;
            function getRewardsBalance(address[] calldata assets, address user)
              external
              view
              returns (uint256);
            function claimRewards(
              address[] calldata assets,
              uint256 amount,
              address to,
              bool stake
            ) external returns (uint256);
          }
          pragma solidity ^0.6.12;
          interface IAToken {
            function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.6.12;
          /**
           * @dev Interface of the ERC20 standard as defined in the EIP.
           * From https://github.com/OpenZeppelin/openzeppelin-contracts
           */
          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.6.12;
          import {IERC20} from './IERC20.sol';
          /**
           * @dev Interface for ERC20 including metadata
           **/
          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: agpl-3.0
          pragma solidity 0.6.12;
          interface IStakedAave {
            function stake(address to, uint256 amount) external;
            function redeem(address to, uint256 amount) external;
            function cooldown() external;
            function claimRewards(address to, uint256 amount) external;
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          interface ITransferHook {
              function onTransfer(address from, address to, uint256 amount) external;
          }// SPDX-License-Identifier: MIT
          pragma solidity 0.6.12;
          /**
           * @dev Collection of functions related to the address type
           * From https://github.com/OpenZeppelin/openzeppelin-contracts
           */
          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: MIT
          pragma solidity 0.6.12;
          import './UpgradeabilityProxy.sol';
          /**
           * @title BaseAdminUpgradeabilityProxy
           * @dev From https://github.com/OpenZeppelin/openzeppelin-sdk/tree/solc-0.6/packages/lib/contracts/upgradeability 
           * This contract combines an upgradeability proxy with an authorization
           * mechanism for administrative tasks.
           * 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 BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
              /**
               * @dev Emitted when the administration has been transferred.
               * @param previousAdmin Address of the previous admin.
               * @param newAdmin Address of the new admin.
               */
              event AdminChanged(address previousAdmin, address newAdmin);
              /**
               * @dev Storage slot with the admin of the contract.
               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
               * validated in the constructor.
               */
              bytes32
                  internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
              /**
               * @dev Modifier to check whether the `msg.sender` is the admin.
               * If it is, it will run the function. Otherwise, it will delegate the call
               * to the implementation.
               */
              modifier ifAdmin() {
                  if (msg.sender == _admin()) {
                      _;
                  } else {
                      _fallback();
                  }
              }
              /**
               * @return The address of the proxy admin.
               */
              function admin() external ifAdmin returns (address) {
                  return _admin();
              }
              /**
               * @return The address of the implementation.
               */
              function implementation() external ifAdmin returns (address) {
                  return _implementation();
              }
              /**
               * @dev Changes the admin of the proxy.
               * Only the current admin can call this function.
               * @param newAdmin Address to transfer proxy administration to.
               */
              function changeAdmin(address newAdmin) external ifAdmin {
                  require(newAdmin != address(0), 'Cannot change the admin of a proxy to the zero address');
                  emit AdminChanged(_admin(), newAdmin);
                  _setAdmin(newAdmin);
              }
              /**
               * @dev Upgrade the backing implementation of the proxy.
               * Only the admin can call this function.
               * @param newImplementation Address of the new implementation.
               */
              function upgradeTo(address newImplementation) external ifAdmin {
                  _upgradeTo(newImplementation);
              }
              /**
               * @dev Upgrade the backing implementation of the proxy and call a function
               * on the new implementation.
               * This is useful to initialize the proxied contract.
               * @param newImplementation 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);
              }
              /**
               * @return adm The admin slot.
               */
              function _admin() internal view returns (address adm) {
                  bytes32 slot = ADMIN_SLOT;
                  assembly {
                      adm := sload(slot)
                  }
              }
              /**
               * @dev Sets the address of the proxy admin.
               * @param newAdmin Address of the new proxy admin.
               */
              function _setAdmin(address newAdmin) internal {
                  bytes32 slot = ADMIN_SLOT;
                  assembly {
                      sstore(slot, newAdmin)
                  }
              }
              /**
               * @dev 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.6.12;
          import './BaseUpgradeabilityProxy.sol';
          /**
           * @title UpgradeabilityProxy
           * @dev From https://github.com/OpenZeppelin/openzeppelin-sdk/tree/solc-0.6/packages/lib/contracts/upgradeability
           * Extends BaseUpgradeabilityProxy with a constructor for initializing
           * implementation and init data.
           */
          contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
              /**
               * @dev Contract constructor.
               * @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.
               */
              constructor(address _logic, bytes memory _data) public payable {
                  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: MIT
          pragma solidity 0.6.12;
          import './Proxy.sol';
          import './Address.sol';
          /**
           * @title BaseUpgradeabilityProxy
           * @dev From https://github.com/OpenZeppelin/openzeppelin-sdk/tree/solc-0.6/packages/lib/contracts/upgradeability
           * 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 override view returns (address impl) {
                  bytes32 slot = IMPLEMENTATION_SLOT;
                  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;
                  assembly {
                      sstore(slot, newImplementation)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.6.12;
          /**
           * @title Proxy
           * @dev From https://github.com/OpenZeppelin/openzeppelin-sdk/tree/solc-0.6/packages/lib/contracts/upgradeability
           * 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.
               * Implemented entirely in `_fallback`.
               */
              fallback() external payable {
                  _fallback();
              }
              /**
               * @return The Address of the implementation.
               */
              function _implementation() internal virtual view 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 {
                  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: MIT
          pragma solidity 0.6.12;
          /**
           * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
           * Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with GSN meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal virtual view returns (address payable) {
                  return msg.sender;
              }
              function _msgData() internal virtual view returns (bytes memory) {
                  this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                  return msg.data;
              }
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          import {Context} from './Context.sol';
          import {IERC20} from '../interfaces/IERC20.sol';
          import {IERC20Detailed} from '../interfaces/IERC20Detailed.sol';
          import {SafeMath} from './SafeMath.sol';
          /**
           * @title ERC20
           * @notice Basic ERC20 implementation
           * @author Aave
           **/
          contract ERC20 is Context, IERC20, IERC20Detailed {
            using SafeMath for uint256;
            mapping(address => uint256) private _balances;
            mapping(address => mapping(address => uint256)) private _allowances;
            uint256 private _totalSupply;
            string private _name;
            string private _symbol;
            uint8 private _decimals;
            constructor(
              string memory name,
              string memory symbol,
              uint8 decimals
            ) public {
              _name = name;
              _symbol = symbol;
              _decimals = decimals;
            }
            /**
             * @return the name of the token
             **/
            function name() public override view returns (string memory) {
              return _name;
            }
            /**
             * @return the symbol of the token
             **/
            function symbol() public override view returns (string memory) {
              return _symbol;
            }
            /**
             * @return the decimals of the token
             **/
            function decimals() public override view returns (uint8) {
              return _decimals;
            }
            /**
             * @return the total supply of the token
             **/
            function totalSupply() public override view returns (uint256) {
              return _totalSupply;
            }
            /**
             * @return the balance of the token
             **/
            function balanceOf(address account) public override view returns (uint256) {
              return _balances[account];
            }
            /**
             * @dev executes a transfer of tokens from msg.sender to recipient
             * @param recipient the recipient of the tokens
             * @param amount the amount of tokens being transferred
             * @return true if the transfer succeeds, false otherwise
             **/
            function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(_msgSender(), recipient, amount);
              return true;
            }
            /**
             * @dev returns the allowance of spender on the tokens owned by owner
             * @param owner the owner of the tokens
             * @param spender the user allowed to spend the owner's tokens
             * @return the amount of owner's tokens spender is allowed to spend
             **/
            function allowance(address owner, address spender)
              public
              virtual
              override
              view
              returns (uint256)
            {
              return _allowances[owner][spender];
            }
            /**
             * @dev allows spender to spend the tokens owned by msg.sender
             * @param spender the user allowed to spend msg.sender tokens
             * @return true
             **/
            function approve(address spender, uint256 amount) public virtual override returns (bool) {
              _approve(_msgSender(), spender, amount);
              return true;
            }
            /**
             * @dev executes a transfer of token from sender to recipient, if msg.sender is allowed to do so
             * @param sender the owner of the tokens
             * @param recipient the recipient of the tokens
             * @param amount the amount of tokens being transferred
             * @return true if the transfer succeeds, false otherwise
             **/
            function transferFrom(
              address sender,
              address recipient,
              uint256 amount
            ) public virtual override returns (bool) {
              _transfer(sender, recipient, amount);
              _approve(
                sender,
                _msgSender(),
                _allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance')
              );
              return true;
            }
            /**
             * @dev increases the allowance of spender to spend msg.sender tokens
             * @param spender the user allowed to spend on behalf of msg.sender
             * @param addedValue the amount being added to the allowance
             * @return true
             **/
            function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
              return true;
            }
            /**
             * @dev decreases the allowance of spender to spend msg.sender tokens
             * @param spender the user allowed to spend on behalf of msg.sender
             * @param subtractedValue the amount being subtracted to the allowance
             * @return true
             **/
            function decreaseAllowance(address spender, uint256 subtractedValue)
              public
              virtual
              returns (bool)
            {
              _approve(
                _msgSender(),
                spender,
                _allowances[_msgSender()][spender].sub(
                  subtractedValue,
                  'ERC20: decreased allowance below zero'
                )
              );
              return true;
            }
            function _transfer(
              address sender,
              address recipient,
              uint256 amount
            ) internal virtual {
              require(sender != address(0), 'ERC20: transfer from the zero address');
              require(recipient != address(0), 'ERC20: transfer to the zero address');
              _beforeTokenTransfer(sender, recipient, amount);
              _balances[sender] = _balances[sender].sub(amount, 'ERC20: transfer amount exceeds balance');
              _balances[recipient] = _balances[recipient].add(amount);
              emit Transfer(sender, recipient, amount);
            }
            function _mint(address account, uint256 amount) internal virtual {
              require(account != address(0), 'ERC20: mint to the zero address');
              _beforeTokenTransfer(address(0), account, amount);
              _totalSupply = _totalSupply.add(amount);
              _balances[account] = _balances[account].add(amount);
              emit Transfer(address(0), account, amount);
            }
            function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), 'ERC20: burn from the zero address');
              _beforeTokenTransfer(account, address(0), amount);
              _balances[account] = _balances[account].sub(amount, 'ERC20: burn amount exceeds balance');
              _totalSupply = _totalSupply.sub(amount);
              emit Transfer(account, address(0), amount);
            }
            function _approve(
              address owner,
              address spender,
              uint256 amount
            ) internal virtual {
              require(owner != address(0), 'ERC20: approve from the zero address');
              require(spender != address(0), 'ERC20: approve to the zero address');
              _allowances[owner][spender] = amount;
              emit Approval(owner, spender, amount);
            }
            function _setName(string memory newName) internal {
              _name = newName;
            }
            function _setSymbol(string memory newSymbol) internal {
              _symbol = newSymbol;
            }
            function _setDecimals(uint8 newDecimals) internal {
              _decimals = newDecimals;
            }
            function _beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
            ) internal virtual {}
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          /**
           * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
           * Wrappers over Solidity's arithmetic operations with added overflow
           * checks.
           *
           * Arithmetic operations in Solidity wrap on overflow. This can easily result
           * in bugs, because programmers usually assume that an overflow raises an
           * error, which is the standard behavior in high level programming languages.
           * `SafeMath` restores this intuition by reverting the transaction when 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.
           */
          library SafeMath {
              /**
               * @dev Returns the addition of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `+` operator.
               *
               * Requirements:
               * - Addition cannot overflow.
               */
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a, 'SafeMath: addition overflow');
                  return c;
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  return sub(a, b, 'SafeMath: subtraction overflow');
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               * - Subtraction cannot overflow.
               */
              function sub(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
              ) internal pure returns (uint256) {
                  require(b <= a, errorMessage);
                  uint256 c = a - b;
                  return c;
              }
              /**
               * @dev Returns the multiplication of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `*` operator.
               *
               * Requirements:
               * - Multiplication cannot overflow.
               */
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) {
                      return 0;
                  }
                  uint256 c = a * b;
                  require(c / a == b, 'SafeMath: multiplication overflow');
                  return c;
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  return div(a, b, 'SafeMath: division by zero');
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               * - The divisor cannot be zero.
               */
              function div(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
              ) internal pure returns (uint256) {
                  // Solidity only automatically asserts when dividing by 0
                  require(b > 0, errorMessage);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  return mod(a, b, 'SafeMath: modulo by zero');
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts with custom message when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               * - The divisor cannot be zero.
               */
              function mod(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
              ) internal pure returns (uint256) {
                  require(b != 0, errorMessage);
                  return a % b;
              }
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          import {ERC20} from '../lib/ERC20.sol';
          import {ITransferHook} from '../interfaces/ITransferHook.sol';
          /**
           * @title ERC20WithSnapshot
           * @notice ERC20 including snapshots of balances on transfer-related actions
           * @author Aave
           **/
          contract ERC20WithSnapshot is ERC20 {
              /// @dev snapshot of a value on a specific block, used for balances
              struct Snapshot {
                  uint128 blockNumber;
                  uint128 value;
              }
              mapping (address => mapping (uint256 => Snapshot)) public _snapshots;
              mapping (address => uint256) public _countsSnapshots;
              /// @dev reference to the Aave governance contract to call (if initialized) on _beforeTokenTransfer
              /// !!! IMPORTANT The Aave governance is considered a trustable contract, being its responsibility
              /// to control all potential reentrancies by calling back the this contract
              ITransferHook public _aaveGovernance;
              event SnapshotDone(address owner, uint128 oldValue, uint128 newValue);
              constructor(string memory name, string memory symbol, uint8 decimals) public ERC20(name, symbol, decimals) {}
              function _setAaveGovernance(ITransferHook aaveGovernance) internal virtual {
                  _aaveGovernance = aaveGovernance;
              }
              /**
              * @dev Writes a snapshot for an owner of tokens
              * @param owner The owner of the tokens
              * @param oldValue The value before the operation that is gonna be executed after the snapshot
              * @param newValue The value after the operation
              */
              function _writeSnapshot(address owner, uint128 oldValue, uint128 newValue) internal virtual {
                  uint128 currentBlock = uint128(block.number);
                  uint256 ownerCountOfSnapshots = _countsSnapshots[owner];
                  mapping (uint256 => Snapshot) storage snapshotsOwner = _snapshots[owner];
                  // Doing multiple operations in the same block
                  if (ownerCountOfSnapshots != 0 && snapshotsOwner[ownerCountOfSnapshots.sub(1)].blockNumber == currentBlock) {
                      snapshotsOwner[ownerCountOfSnapshots.sub(1)].value = newValue;
                  } else {
                      snapshotsOwner[ownerCountOfSnapshots] = Snapshot(currentBlock, newValue);
                      _countsSnapshots[owner] = ownerCountOfSnapshots.add(1);
                  }
                  emit SnapshotDone(owner, oldValue, newValue);
              }
              /**
              * @dev Writes a snapshot before any operation involving transfer of value: _transfer, _mint and _burn
              * - On _transfer, it writes snapshots for both "from" and "to"
              * - On _mint, only for _to
              * - On _burn, only for _from
              * @param from the from address
              * @param to the to address
              * @param amount the amount to transfer
              */
              function _beforeTokenTransfer(address from, address to, uint256 amount) internal override {
                  if (from == to) {
                      return;
                  }
                  if (from != address(0)) {
                      uint256 fromBalance = balanceOf(from);
                      _writeSnapshot(from, uint128(fromBalance), uint128(fromBalance.sub(amount)));
                  }
                  if (to != address(0)) {
                      uint256 toBalance = balanceOf(to);
                      _writeSnapshot(to, uint128(toBalance), uint128(toBalance.add(amount)));
                  }
                  // caching the aave governance address to avoid multiple state loads
                  ITransferHook aaveGovernance = _aaveGovernance;
                  if (aaveGovernance != ITransferHook(0)) {
                      aaveGovernance.onTransfer(from, to, amount);
                  }
              }
          }// SPDX-License-Identifier: MIT
          pragma solidity 0.6.12;
          import './BaseAdminUpgradeabilityProxy.sol';
          import './InitializableUpgradeabilityProxy.sol';
          /**
           * @title InitializableAdminUpgradeabilityProxy
           * @dev From https://github.com/OpenZeppelin/openzeppelin-sdk/tree/solc-0.6/packages/lib/contracts/upgradeability 
           * Extends from BaseAdminUpgradeabilityProxy with an initializer for
           * initializing the implementation, admin, and init data.
           */
          contract InitializableAdminUpgradeabilityProxy is
              BaseAdminUpgradeabilityProxy,
              InitializableUpgradeabilityProxy
          {
              /**
               * Contract initializer.
               * @param _logic address of the initial implementation.
               * @param _admin Address of the proxy administrator.
               * @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,
                  address _admin,
                  bytes memory _data
              ) public payable {
                  require(_implementation() == address(0));
                  InitializableUpgradeabilityProxy.initialize(_logic, _data);
                  assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
                  _setAdmin(_admin);
              }
              /**
               * @dev Only fall back when the sender is not the admin.
               */
              function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) {
                  BaseAdminUpgradeabilityProxy._willFallback();
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.6.12;
          import './BaseUpgradeabilityProxy.sol';
          /**
           * @title InitializableUpgradeabilityProxy
           * @dev From https://github.com/OpenZeppelin/openzeppelin-sdk/tree/solc-0.6/packages/lib/contracts/upgradeability
           * 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: MIT
          pragma solidity 0.6.12;
          import {IERC20} from "../interfaces/IERC20.sol";
          import {SafeMath} from "./SafeMath.sol";
          import {Address} from "./Address.sol";
          /**
           * @title SafeERC20
           * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
           * Wrappers around ERC20 operations that throw on failure (when the token
           * contract returns false). Tokens that return no value (and instead revert or
           * throw on failure) are also supported, non-reverting calls are assumed to be
           * successful.
           * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
           * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
           */
          library SafeERC20 {
              using SafeMath for uint256;
              using Address for address;
              function safeTransfer(IERC20 token, address to, uint256 value) internal {
                  callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
              }
              function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                  callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
              }
              function safeApprove(IERC20 token, address spender, uint256 value) internal {
                  require((value == 0) || (token.allowance(address(this), spender) == 0),
                      "SafeERC20: approve from non-zero to non-zero allowance"
                  );
                  callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
              }
              
              function callOptionalReturn(IERC20 token, bytes memory data) private {
                  require(address(token).isContract(), "SafeERC20: call to non-contract");
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = address(token).call(data);
                  require(success, "SafeERC20: low-level call failed");
                  if (returndata.length > 0) { // Return data is optional
                      // solhint-disable-next-line max-line-length
                      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                  }
              }
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          pragma experimental ABIEncoderV2;
          import {IAaveIncentivesController} from '../interfaces/IAaveIncentivesController.sol';
          import {DistributionTypes} from '../lib/DistributionTypes.sol';
          import {IAToken} from '../interfaces/IAToken.sol';
          contract ATokenMock is IAToken {
            IAaveIncentivesController public _aic;
            uint256 internal _userBalance;
            uint256 internal _totalSupply;
            // hack to be able to test event from EI properly
            event RewardsAccrued(address indexed user, uint256 amount);
            // hack to be able to test event from Distribution manager properly
            event AssetConfigUpdated(address indexed asset, uint256 emission);
            event AssetIndexUpdated(address indexed asset, uint256 index);
            event UserIndexUpdated(address indexed user, address indexed asset, uint256 index);
            constructor(IAaveIncentivesController aic) public {
              _aic = aic;
            }
            function handleActionOnAic(
              address user,
              uint256 userBalance,
              uint256 totalSupply
            ) external {
              _aic.handleAction(user, userBalance, totalSupply);
            }
            function setUserBalanceAndSupply(uint256 userBalance, uint256 totalSupply) public {
              _userBalance = userBalance;
              _totalSupply = totalSupply;
            }
            function getScaledUserBalanceAndSupply(address user)
              external
              override
              view
              returns (uint256, uint256)
            {
              return (_userBalance, _totalSupply);
            }
            function cleanUserState() external {
              _userBalance = 0;
              _totalSupply = 0;
            }
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          import {ITransferHook} from "../interfaces/ITransferHook.sol";
          contract MockTransferHook is ITransferHook {
              event MockHookEvent();
              function onTransfer(address from, address to, uint256 amount) external override {
                  emit MockHookEvent();
              }
          }// SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          pragma experimental ABIEncoderV2;
          import {SafeMath} from '../lib/SafeMath.sol';
          import {DistributionTypes} from '../lib/DistributionTypes.sol';
          import {IAaveDistributionManager} from '../interfaces/IAaveDistributionManager.sol';
          /**
           * @title AaveDistributionManager
           * @notice Accounting contract to manage multiple staking distributions
           * @author Aave
           **/
          contract AaveDistributionManager is IAaveDistributionManager {
            using SafeMath for uint256;
            struct AssetData {
              uint128 emissionPerSecond;
              uint128 lastUpdateTimestamp;
              uint256 index;
              mapping(address => uint256) users;
            }
            uint256 public immutable DISTRIBUTION_END;
            address public immutable EMISSION_MANAGER;
            uint8 public constant PRECISION = 18;
            mapping(address => AssetData) public assets;
            event AssetConfigUpdated(address indexed asset, uint256 emission);
            event AssetIndexUpdated(address indexed asset, uint256 index);
            event UserIndexUpdated(address indexed user, address indexed asset, uint256 index);
            constructor(address emissionManager, uint256 distributionDuration) public {
              DISTRIBUTION_END = block.timestamp.add(distributionDuration);
              EMISSION_MANAGER = emissionManager;
            }
            /**
             * @dev Configures the distribution of rewards for a list of assets
             * @param assetsConfigInput The list of configurations to apply
             **/
            function configureAssets(DistributionTypes.AssetConfigInput[] calldata assetsConfigInput) external override {
              require(msg.sender == EMISSION_MANAGER, 'ONLY_EMISSION_MANAGER');
              for (uint256 i = 0; i < assetsConfigInput.length; i++) {
                AssetData storage assetConfig = assets[assetsConfigInput[i].underlyingAsset];
                _updateAssetStateInternal(
                  assetsConfigInput[i].underlyingAsset,
                  assetConfig,
                  assetsConfigInput[i].totalStaked
                );
                assetConfig.emissionPerSecond = assetsConfigInput[i].emissionPerSecond;
                emit AssetConfigUpdated(
                  assetsConfigInput[i].underlyingAsset,
                  assetsConfigInput[i].emissionPerSecond
                );
              }
            }
            /**
             * @dev Updates the state of one distribution, mainly rewards index and timestamp
             * @param underlyingAsset The address used as key in the distribution, for example sAAVE or the aTokens addresses on Aave
             * @param assetConfig Storage pointer to the distribution's config
             * @param totalStaked Current total of staked assets for this distribution
             * @return The new distribution index
             **/
            function _updateAssetStateInternal(
              address underlyingAsset,
              AssetData storage assetConfig,
              uint256 totalStaked
            ) internal returns (uint256) {
              uint256 oldIndex = assetConfig.index;
              uint128 lastUpdateTimestamp = assetConfig.lastUpdateTimestamp;
              if (block.timestamp == lastUpdateTimestamp) {
                return oldIndex;
              }
              uint256 newIndex = _getAssetIndex(
                oldIndex,
                assetConfig.emissionPerSecond,
                lastUpdateTimestamp,
                totalStaked
              );
              if (newIndex != oldIndex) {
                assetConfig.index = newIndex;
                emit AssetIndexUpdated(underlyingAsset, newIndex);
              }
              assetConfig.lastUpdateTimestamp = uint128(block.timestamp);
              return newIndex;
            }
            /**
             * @dev Updates the state of an user in a distribution
             * @param user The user's address
             * @param asset The address of the reference asset of the distribution
             * @param stakedByUser Amount of tokens staked by the user in the distribution at the moment
             * @param totalStaked Total tokens staked in the distribution
             * @return The accrued rewards for the user until the moment
             **/
            function _updateUserAssetInternal(
              address user,
              address asset,
              uint256 stakedByUser,
              uint256 totalStaked
            ) internal returns (uint256) {
              AssetData storage assetData = assets[asset];
              uint256 userIndex = assetData.users[user];
              uint256 accruedRewards = 0;
              uint256 newIndex = _updateAssetStateInternal(asset, assetData, totalStaked);
              if (userIndex != newIndex) {
                if (stakedByUser != 0) {
                  accruedRewards = _getRewards(stakedByUser, newIndex, userIndex);
                }
                assetData.users[user] = newIndex;
                emit UserIndexUpdated(user, asset, newIndex);
              }
              return accruedRewards;
            }
            /**
             * @dev Used by "frontend" stake contracts to update the data of an user when claiming rewards from there
             * @param user The address of the user
             * @param stakes List of structs of the user data related with his stake
             * @return The accrued rewards for the user until the moment
             **/
            function _claimRewards(address user, DistributionTypes.UserStakeInput[] memory stakes)
              internal
              returns (uint256)
            {
              uint256 accruedRewards = 0;
              for (uint256 i = 0; i < stakes.length; i++) {
                accruedRewards = accruedRewards.add(
                  _updateUserAssetInternal(
                    user,
                    stakes[i].underlyingAsset,
                    stakes[i].stakedByUser,
                    stakes[i].totalStaked
                  )
                );
              }
              return accruedRewards;
            }
            /**
             * @dev Return the accrued rewards for an user over a list of distribution
             * @param user The address of the user
             * @param stakes List of structs of the user data related with his stake
             * @return The accrued rewards for the user until the moment
             **/
            function _getUnclaimedRewards(address user, DistributionTypes.UserStakeInput[] memory stakes)
              internal
              view
              returns (uint256)
            {
              uint256 accruedRewards = 0;
              for (uint256 i = 0; i < stakes.length; i++) {
                AssetData storage assetConfig = assets[stakes[i].underlyingAsset];
                uint256 assetIndex = _getAssetIndex(
                  assetConfig.index,
                  assetConfig.emissionPerSecond,
                  assetConfig.lastUpdateTimestamp,
                  stakes[i].totalStaked
                );
                accruedRewards = accruedRewards.add(
                  _getRewards(stakes[i].stakedByUser, assetIndex, assetConfig.users[user])
                );
              }
              return accruedRewards;
            }
            /**
             * @dev Internal function for the calculation of user's rewards on a distribution
             * @param principalUserBalance Amount staked by the user on a distribution
             * @param reserveIndex Current index of the distribution
             * @param userIndex Index stored for the user, representation his staking moment
             * @return The rewards
             **/
            function _getRewards(
              uint256 principalUserBalance,
              uint256 reserveIndex,
              uint256 userIndex
            ) internal pure returns (uint256) {
              return principalUserBalance.mul(reserveIndex.sub(userIndex)).div(10**uint256(PRECISION));
            }
            /**
             * @dev Calculates the next value of an specific distribution index, with validations
             * @param currentIndex Current index of the distribution
             * @param emissionPerSecond Representing the total rewards distributed per second per asset unit, on the distribution
             * @param lastUpdateTimestamp Last moment this distribution was updated
             * @param totalBalance of tokens considered for the distribution
             * @return The new index.
             **/
            function _getAssetIndex(
              uint256 currentIndex,
              uint256 emissionPerSecond,
              uint128 lastUpdateTimestamp,
              uint256 totalBalance
            ) internal view returns (uint256) {
              if (
                emissionPerSecond == 0 ||
                totalBalance == 0 ||
                lastUpdateTimestamp == block.timestamp ||
                lastUpdateTimestamp >= DISTRIBUTION_END
              ) {
                return currentIndex;
              }
              uint256 currentTimestamp = block.timestamp > DISTRIBUTION_END
                ? DISTRIBUTION_END
                : block.timestamp;
              uint256 timeDelta = currentTimestamp.sub(lastUpdateTimestamp);
              return
                emissionPerSecond.mul(timeDelta).mul(10**uint256(PRECISION)).div(totalBalance).add(
                  currentIndex
                );
            }
            /**
             * @dev Returns the data of an user on a distribution
             * @param user Address of the user
             * @param asset The address of the reference asset of the distribution
             * @return The new index
             **/
            function getUserAssetData(address user, address asset) public view returns (uint256) {
              return assets[asset].users[user];
            }
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          pragma experimental ABIEncoderV2;
          import {DistributionTypes} from '../lib/DistributionTypes.sol';
          import {IERC20} from '../interfaces/IERC20.sol';
          import {IAToken} from '../interfaces/IAToken.sol';
          import {IAaveIncentivesController} from '../interfaces/IAaveIncentivesController.sol';
          import {IStakedAave} from '../interfaces/IStakedAave.sol';
          import {VersionedInitializable} from '../utils/VersionedInitializable.sol';
          import {AaveDistributionManager} from './AaveDistributionManager.sol';
          /**
           * @title AaveIncentivesController
           * @notice Distributor contract for rewards to the Aave protocol
           * @author Aave
           **/
          contract AaveIncentivesController is
            IAaveIncentivesController,
            VersionedInitializable,
            AaveDistributionManager
          {
            uint256 public constant REVISION = 1;
            IStakedAave public immutable PSM;
            IERC20 public immutable REWARD_TOKEN;
            address public immutable REWARDS_VAULT;
            uint256 public immutable EXTRA_PSM_REWARD;
            mapping(address => uint256) internal _usersUnclaimedRewards;
            event RewardsAccrued(address indexed user, uint256 amount);
            event RewardsClaimed(address indexed user, address indexed to, uint256 amount);
            constructor(
              IERC20 rewardToken,
              address rewardsVault,
              IStakedAave psm,
              uint256 extraPsmReward,
              address emissionManager,
              uint128 distributionDuration
            ) public AaveDistributionManager(emissionManager, distributionDuration) {
              REWARD_TOKEN = rewardToken;
              REWARDS_VAULT = rewardsVault;
              PSM = psm;
              EXTRA_PSM_REWARD = extraPsmReward;
            }
            /**
             * @dev Called by the proxy contract. Not used at the moment, but for the future
             **/
            function initialize() external initializer {
              // to unlock possibility to stake on behalf of the user
              REWARD_TOKEN.approve(address(PSM), type(uint256).max);
            }
            /**
             * @dev Called by the corresponding asset on any update that affects the rewards distribution
             * @param user The address of the user
             * @param userBalance The balance of the user of the asset in the lending pool
             * @param totalSupply The total supply of the asset in the lending pool
             **/
            function handleAction(
              address user,
              uint256 userBalance,
              uint256 totalSupply
            ) external override {
              uint256 accruedRewards = _updateUserAssetInternal(user, msg.sender, userBalance, totalSupply);
              if (accruedRewards != 0) {
                _usersUnclaimedRewards[user] = _usersUnclaimedRewards[user].add(accruedRewards);
                emit RewardsAccrued(user, accruedRewards);
              }
            }
            /**
             * @dev Returns the total of rewards of an user, already accrued + not yet accrued
             * @param user The address of the user
             * @return The rewards
             **/
            function getRewardsBalance(address[] calldata assets, address user)
              external
              override
              view
              returns (uint256)
            {
              uint256 unclaimedRewards = _usersUnclaimedRewards[user];
              DistributionTypes.UserStakeInput[] memory userState = new DistributionTypes.UserStakeInput[](
                assets.length
              );
              for (uint256 i = 0; i < assets.length; i++) {
                userState[i].underlyingAsset = assets[i];
                (userState[i].stakedByUser, userState[i].totalStaked) = IAToken(assets[i])
                  .getScaledUserBalanceAndSupply(user);
              }
              unclaimedRewards = unclaimedRewards.add(_getUnclaimedRewards(user, userState));
              return unclaimedRewards;
            }
            /**
             * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards
             * @param amount Amount of rewards to claim
             * @param to Address that will be receiving the rewards
             * @param stake Boolean flag to determined if the claimed rewards should be staked in the Safety Module or not
             * @return Rewards claimed
             **/
            function claimRewards(
              address[] calldata assets,
              uint256 amount,
              address to,
              bool stake
            ) external override returns (uint256) {
              if (amount == 0) {
                return 0;
              }
              address user = msg.sender;
              uint256 unclaimedRewards = _usersUnclaimedRewards[user];
              DistributionTypes.UserStakeInput[] memory userState = new DistributionTypes.UserStakeInput[](
                assets.length
              );
              for (uint256 i = 0; i < assets.length; i++) {
                userState[i].underlyingAsset = assets[i];
                (userState[i].stakedByUser, userState[i].totalStaked) = IAToken(assets[i])
                  .getScaledUserBalanceAndSupply(user);
              }
              uint256 accruedRewards = _claimRewards(user, userState);
              if (accruedRewards != 0) {
                unclaimedRewards = unclaimedRewards.add(accruedRewards);
                emit RewardsAccrued(user, accruedRewards);
              }
              if (unclaimedRewards == 0) {
                return 0;
              }
              uint256 amountToClaim = amount > unclaimedRewards ? unclaimedRewards : amount;
              _usersUnclaimedRewards[user] = unclaimedRewards - amountToClaim; // Safe due to the previous line
              if (stake) {
                amountToClaim = amountToClaim.add(amountToClaim.mul(EXTRA_PSM_REWARD).div(100));
                REWARD_TOKEN.transferFrom(REWARDS_VAULT, address(this), amountToClaim);
                PSM.stake(to, amountToClaim);
              } else {
                REWARD_TOKEN.transferFrom(REWARDS_VAULT, to, amountToClaim);
              }
              emit RewardsClaimed(msg.sender, to, amountToClaim);
              return amountToClaim;
            }
            /**
             * @dev returns the unclaimed rewards of the user
             * @param _user the address of the user
             * @return the unclaimed user rewards
             */
            function getUserUnclaimedRewards(address _user) external view returns (uint256) {
              return _usersUnclaimedRewards[_user];
            }
            /**
             * @dev returns the revision of the implementation contract
             */
            function getRevision() internal override pure returns (uint256) {
              return REVISION;
            }
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          /**
           * @title VersionedInitializable
           *
           * @dev Helper contract to support initializer functions. To use it, replace
           * the constructor with a function that has the `initializer` modifier.
           * 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.
           *
           * @author Aave, inspired by the OpenZeppelin Initializable contract
           */
          abstract contract VersionedInitializable {
              /**
               * @dev Indicates that the contract has been initialized.
               */
              uint256 internal lastInitializedRevision = 0;
              /**
               * @dev Modifier to use in the initializer function of a contract.
               */
              modifier initializer() {
                  uint256 revision = getRevision();
                  require(
                      revision > lastInitializedRevision,
                      'Contract instance has already been initialized'
                  );
                  lastInitializedRevision = revision;
                  _;
              }
              /// @dev returns the revision number of the contract.
              /// Needs to be defined in the inherited class as a constant.
              function getRevision() internal virtual pure returns (uint256);
              // Reserved storage space to allow for layout changes in the future.
              uint256[50] private ______gap;
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          pragma experimental ABIEncoderV2;
          import {IERC20} from '../interfaces/IERC20.sol';
          import {StakedToken} from './StakedToken.sol';
          /**
           * @title StakedAave
           * @notice StakedToken with AAVE token as staked token
           * @author Aave
           **/
          contract StakedAave is StakedToken {
            string internal constant NAME = 'Staked Aave';
            string internal constant SYMBOL = 'stkAAVE';
            uint8 internal constant DECIMALS = 18;
            
            constructor(
              IERC20 stakedToken,
              IERC20 rewardToken,
              uint256 cooldownSeconds,
              uint256 unstakeWindow,
              address rewardsVault,
              address emissionManager,
              uint128 distributionDuration
            ) public StakedToken(
              stakedToken,
              rewardToken,
              cooldownSeconds,
              unstakeWindow,
              rewardsVault,
              emissionManager,
              distributionDuration,
              NAME,
              SYMBOL,
              DECIMALS) {}
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          pragma experimental ABIEncoderV2;
          import {IERC20} from '../interfaces/IERC20.sol';
          import {IStakedAave} from '../interfaces/IStakedAave.sol';
          import {ITransferHook} from '../interfaces/ITransferHook.sol';
          import {ERC20WithSnapshot} from '../lib/ERC20WithSnapshot.sol';
          import {SafeERC20} from '../lib/SafeERC20.sol';
          import {VersionedInitializable} from '../utils/VersionedInitializable.sol';
          import {DistributionTypes} from '../lib/DistributionTypes.sol';
          import {AaveDistributionManager} from './AaveDistributionManager.sol';
          /**
           * @title StakedToken
           * @notice Contract to stake Aave token, tokenize the position and get rewards, inheriting from a distribution manager contract
           * @author Aave
           **/
          contract StakedToken is IStakedAave, ERC20WithSnapshot, VersionedInitializable, AaveDistributionManager {
            using SafeERC20 for IERC20;
            uint256 public constant REVISION = 1;
            IERC20 public immutable STAKED_TOKEN;
            IERC20 public immutable REWARD_TOKEN;
            uint256 public immutable COOLDOWN_SECONDS;
            /// @notice Seconds available to redeem once the cooldown period is fullfilled
            uint256 public immutable UNSTAKE_WINDOW;
            /// @notice Address to pull from the rewards, needs to have approved this contract
            address public immutable REWARDS_VAULT;
            mapping(address => uint256) public stakerRewardsToClaim;
            mapping(address => uint256) public stakersCooldowns;
            event Staked(address indexed from, address indexed onBehalfOf, uint256 amount);
            event Redeem(address indexed from, address indexed to, uint256 amount);
            event RewardsAccrued(address user, uint256 amount);
            event RewardsClaimed(address indexed from, address indexed to, uint256 amount);
            event Cooldown(address indexed user);
            constructor(
              IERC20 stakedToken,
              IERC20 rewardToken,
              uint256 cooldownSeconds,
              uint256 unstakeWindow,
              address rewardsVault,
              address emissionManager,
              uint128 distributionDuration,
              string memory name,
              string memory symbol,
              uint8 decimals
            ) public ERC20WithSnapshot(name, symbol, decimals) AaveDistributionManager(emissionManager, distributionDuration) {
              STAKED_TOKEN = stakedToken;
              REWARD_TOKEN = rewardToken;
              COOLDOWN_SECONDS = cooldownSeconds;
              UNSTAKE_WINDOW = unstakeWindow;
              REWARDS_VAULT = rewardsVault;
            }
            /**
             * @dev Called by the proxy contract
             **/
            function initialize(ITransferHook aaveGovernance, string calldata name, string calldata symbol, uint8 decimals) external initializer {
              _setName(name);
              _setSymbol(symbol);
              _setDecimals(decimals);
              _setAaveGovernance(aaveGovernance);
            }
            function stake(address onBehalfOf, uint256 amount) external override {
              require(amount != 0, 'INVALID_ZERO_AMOUNT');
              uint256 balanceOfUser = balanceOf(onBehalfOf);
              uint256 accruedRewards = _updateUserAssetInternal(
                onBehalfOf,
                address(this),
                balanceOfUser,
                totalSupply()
              );
              if (accruedRewards != 0) {
                emit RewardsAccrued(onBehalfOf, accruedRewards);
                stakerRewardsToClaim[onBehalfOf] = stakerRewardsToClaim[onBehalfOf].add(accruedRewards);
              }
              stakersCooldowns[onBehalfOf] = getNextCooldownTimestamp(0, amount, onBehalfOf, balanceOfUser);
              _mint(onBehalfOf, amount);
              IERC20(STAKED_TOKEN).safeTransferFrom(msg.sender, address(this), amount);
              emit Staked(msg.sender, onBehalfOf, amount);
            }
            /**
             * @dev Redeems staked tokens, and stop earning rewards
             * @param to Address to redeem to
             * @param amount Amount to redeem
             **/
            function redeem(address to, uint256 amount) external override {
              require(amount != 0, 'INVALID_ZERO_AMOUNT');
              //solium-disable-next-line
              uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];
              require(
                block.timestamp > cooldownStartTimestamp.add(COOLDOWN_SECONDS),
                'INSUFFICIENT_COOLDOWN'
              );
              require(
                block.timestamp.sub(cooldownStartTimestamp.add(COOLDOWN_SECONDS)) <= UNSTAKE_WINDOW,
                'UNSTAKE_WINDOW_FINISHED'
              );
              uint256 balanceOfMessageSender = balanceOf(msg.sender);
              uint256 amountToRedeem = (amount > balanceOfMessageSender) ? balanceOfMessageSender : amount;
              _updateCurrentUnclaimedRewards(msg.sender, balanceOfMessageSender, true);
              _burn(msg.sender, amountToRedeem);
              if (balanceOfMessageSender.sub(amountToRedeem) == 0) {
                stakersCooldowns[msg.sender] = 0;
              }
              IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);
              emit Redeem(msg.sender, to, amountToRedeem);
            }
            /**
             * @dev Activates the cooldown period to unstake
             * - It can't be called if the user is not staking
             **/
            function cooldown() external override {
              require(balanceOf(msg.sender) != 0, "INVALID_BALANCE_ON_COOLDOWN");
              //solium-disable-next-line
              stakersCooldowns[msg.sender] = block.timestamp;
              emit Cooldown(msg.sender);
            }
            /**
             * @dev Claims an `amount` of `REWARD_TOKEN` to the address `to`
             * @param to Address to stake for
             * @param amount Amount to stake
             **/
            function claimRewards(address to, uint256 amount) external override {
              uint256 newTotalRewards = _updateCurrentUnclaimedRewards(
                msg.sender,
                balanceOf(msg.sender),
                false
              );
              uint256 amountToClaim = (amount == type(uint256).max) ? newTotalRewards : amount;
              stakerRewardsToClaim[msg.sender] = newTotalRewards.sub(amountToClaim, "INVALID_AMOUNT");
              REWARD_TOKEN.safeTransferFrom(REWARDS_VAULT, to, amountToClaim);
              emit RewardsClaimed(msg.sender, to, amountToClaim);
            }
            /**
             * @dev Internal ERC20 _transfer of the tokenized staked tokens
             * @param from Address to transfer from
             * @param to Address to transfer to
             * @param amount Amount to transfer
             **/
            function _transfer(
              address from,
              address to,
              uint256 amount
            ) internal override {
              uint256 balanceOfFrom = balanceOf(from);
              // Sender
              _updateCurrentUnclaimedRewards(from, balanceOfFrom, true);
              // Recipient
              if (from != to) {
                uint256 balanceOfTo = balanceOf(to);
                _updateCurrentUnclaimedRewards(to, balanceOfTo, true);
                uint256 previousSenderCooldown = stakersCooldowns[from];
                stakersCooldowns[to] = getNextCooldownTimestamp(previousSenderCooldown, amount, to, balanceOfTo);
                // if cooldown was set and whole balance of sender was transferred - clear cooldown
                if (balanceOfFrom == amount && previousSenderCooldown != 0) {
                  stakersCooldowns[from] = 0;
                }
              }
              super._transfer(from, to, amount);
            }
            /**
             * @dev Updates the user state related with his accrued rewards
             * @param user Address of the user
             * @param userBalance The current balance of the user
             * @param updateStorage Boolean flag used to update or not the stakerRewardsToClaim of the user
             * @return The unclaimed rewards that were added to the total accrued
             **/
            function _updateCurrentUnclaimedRewards(
              address user,
              uint256 userBalance,
              bool updateStorage
            ) internal returns (uint256) {
              uint256 accruedRewards = _updateUserAssetInternal(
                user,
                address(this),
                userBalance,
                totalSupply()
              );
              uint256 unclaimedRewards = stakerRewardsToClaim[user].add(accruedRewards);
              if (accruedRewards != 0) {
                if (updateStorage) {
                  stakerRewardsToClaim[user] = unclaimedRewards;
                }
                emit RewardsAccrued(user, accruedRewards);
              }
              return unclaimedRewards;
            }
            /**
             * @dev Calculates the how is gonna be a new cooldown timestamp depending on the sender/receiver situation
             *  - If the timestamp of the sender is "better" or the timestamp of the recipient is 0, we take the one of the recipient
             *  - Weighted average of from/to cooldown timestamps if:
             *    # The sender doesn't have the cooldown activated (timestamp 0).
             *    # The sender timestamp is expired
             *    # The sender has a "worse" timestamp
             *  - If the receiver's cooldown timestamp expired (too old), the next is 0
             * @param fromCooldownTimestamp Cooldown timestamp of the sender
             * @param amountToReceive Amount
             * @param toAddress Address of the recipient
             * @param toBalance Current balance of the receiver
             * @return The new cooldown timestamp
             **/
            function getNextCooldownTimestamp(
              uint256 fromCooldownTimestamp,
              uint256 amountToReceive,
              address toAddress,
              uint256 toBalance
            ) public returns (uint256) {
              uint256 toCooldownTimestamp = stakersCooldowns[toAddress];
              if (toCooldownTimestamp == 0) {
                return 0;
              }
              uint256 minimalValidCooldownTimestamp = block.timestamp.sub(COOLDOWN_SECONDS).sub(
                UNSTAKE_WINDOW
              );
              if (minimalValidCooldownTimestamp > toCooldownTimestamp) {
                toCooldownTimestamp = 0;
              } else {
                uint256 fromCooldownTimestamp = (minimalValidCooldownTimestamp > fromCooldownTimestamp)
                  ? block.timestamp
                  : fromCooldownTimestamp;
                if (fromCooldownTimestamp < toCooldownTimestamp) {
                  return toCooldownTimestamp;
                } else {
                  toCooldownTimestamp = (
                    amountToReceive.mul(fromCooldownTimestamp).add(toBalance.mul(toCooldownTimestamp))
                  )
                    .div(amountToReceive.add(toBalance));
                }
              }
              stakersCooldowns[toAddress] = toCooldownTimestamp;
              return toCooldownTimestamp;
            }
            /**
             * @dev Return the total rewards pending to claim by an staker
             * @param staker The staker address
             * @return The rewards
             */
            function getTotalRewardsBalance(address staker) external view returns (uint256) {
                DistributionTypes.UserStakeInput[] memory userStakeInputs
               = new DistributionTypes.UserStakeInput[](1);
              userStakeInputs[0] = DistributionTypes.UserStakeInput({
                underlyingAsset: address(this),
                stakedByUser: balanceOf(staker),
                totalStaked: totalSupply()
              });
              return stakerRewardsToClaim[staker].add(_getUnclaimedRewards(staker, userStakeInputs));
            }
            /**
             * @dev returns the revision of the implementation contract
             * @return The revision
             */
            function getRevision() internal override pure returns (uint256) {
              return REVISION;
            }
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.12;
          import '../lib/ERC20.sol';
          /**
           * @title ERC20Mintable
           * @dev ERC20 minting logic
           */
          contract MintableErc20 is ERC20 {
              constructor(
                  string memory name,
                  string memory symbol,
                  uint8 decimals
              ) public ERC20(name, symbol, decimals) {}
              /**
               * @dev Function to mint tokens
               * @param value The amount of tokens to mint.
               * @return A boolean that indicates if the operation was successful.
               */
              function mint(uint256 value) public returns (bool) {
                  _mint(msg.sender, value);
                  return true;
              }
          }
          

          File 4 of 6: StakedTokenV2Rev3
          /**
           *Submitted for verification at Etherscan.io on 2020-12-10
           */
          
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.7.5;
          pragma experimental ABIEncoderV2;
          
          interface IGovernancePowerDelegationToken {
            enum DelegationType {VOTING_POWER, PROPOSITION_POWER}
          
            /**
             * @dev emitted when a user delegates to another
             * @param delegator the delegator
             * @param delegatee the delegatee
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            event DelegateChanged(
              address indexed delegator,
              address indexed delegatee,
              DelegationType delegationType
            );
          
            /**
             * @dev emitted when an action changes the delegated power of a user
             * @param user the user which delegated power has changed
             * @param amount the amount of delegated power for the user
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            event DelegatedPowerChanged(address indexed user, uint256 amount, DelegationType delegationType);
          
            /**
             * @dev delegates the specific power to a delegatee
             * @param delegatee the user which delegated power has changed
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            function delegateByType(address delegatee, DelegationType delegationType) external virtual;
          
            /**
             * @dev delegates all the powers to a specific user
             * @param delegatee the user to which the power will be delegated
             **/
            function delegate(address delegatee) external virtual;
          
            /**
             * @dev returns the delegatee of an user
             * @param delegator the address of the delegator
             **/
            function getDelegateeByType(address delegator, DelegationType delegationType)
              external
              view
              virtual
              returns (address);
          
            /**
             * @dev returns the current delegated power of a user. The current power is the
             * power delegated at the time of the last snapshot
             * @param user the user
             **/
            function getPowerCurrent(address user, DelegationType delegationType)
              external
              view
              virtual
              returns (uint256);
          
            /**
             * @dev returns the delegated power of a user at a certain block
             * @param user the user
             **/
            function getPowerAtBlock(
              address user,
              uint256 blockNumber,
              DelegationType delegationType
            ) external view virtual returns (uint256);
          
            /**
             * @dev returns the total supply at a certain block number
             **/
            function totalSupplyAt(uint256 blockNumber) external view virtual returns (uint256);
          }
          
          /**
           * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
           * Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with GSN meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
            function _msgSender() internal view virtual returns (address payable) {
              return msg.sender;
            }
          
            function _msgData() internal view virtual returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
            }
          }
          
          /**
           * @dev Interface of the ERC20 standard as defined in the EIP.
           * From https://github.com/OpenZeppelin/openzeppelin-contracts
           */
          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);
          }
          
          /**
           * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
           * Wrappers over Solidity's arithmetic operations with added overflow
           * checks.
           *
           * Arithmetic operations in Solidity wrap on overflow. This can easily result
           * in bugs, because programmers usually assume that an overflow raises an
           * error, which is the standard behavior in high level programming languages.
           * `SafeMath` restores this intuition by reverting the transaction when 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.
           */
          library SafeMath {
            /**
             * @dev Returns the addition of two unsigned integers, reverting on
             * overflow.
             *
             * Counterpart to Solidity's `+` operator.
             *
             * Requirements:
             * - Addition cannot overflow.
             */
            function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, 'SafeMath: addition overflow');
          
              return c;
            }
          
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             * - Subtraction cannot overflow.
             */
            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, 'SafeMath: subtraction overflow');
            }
          
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             * - Subtraction cannot overflow.
             */
            function sub(
              uint256 a,
              uint256 b,
              string memory errorMessage
            ) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
          
              return c;
            }
          
            /**
             * @dev Returns the multiplication of two unsigned integers, reverting on
             * overflow.
             *
             * Counterpart to Solidity's `*` operator.
             *
             * Requirements:
             * - Multiplication cannot overflow.
             */
            function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
              if (a == 0) {
                return 0;
              }
          
              uint256 c = a * b;
              require(c / a == b, 'SafeMath: multiplication overflow');
          
              return c;
            }
          
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts on
             * division by zero. The result is rounded towards zero.
             *
             * Counterpart to Solidity's `/` operator. Note: this function uses a
             * `revert` opcode (which leaves remaining gas untouched) while Solidity
             * uses an invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, 'SafeMath: division by zero');
            }
          
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
             * division by zero. The result is rounded towards zero.
             *
             * Counterpart to Solidity's `/` operator. Note: this function uses a
             * `revert` opcode (which leaves remaining gas untouched) while Solidity
             * uses an invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function div(
              uint256 a,
              uint256 b,
              string memory errorMessage
            ) internal pure returns (uint256) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
          
              return c;
            }
          
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts when dividing by zero.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, 'SafeMath: modulo by zero');
            }
          
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts with custom message when dividing by zero.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function mod(
              uint256 a,
              uint256 b,
              string memory errorMessage
            ) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
            }
          }
          
          /**
           * @dev Collection of functions related to the address type
           * From https://github.com/OpenZeppelin/openzeppelin-contracts
           */
          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');
            }
          }
          
          /**
           * @dev Implementation of the {IERC20} interface.
           *
           * This implementation is agnostic to the way tokens are created. This means
           * that a supply mechanism has to be added in a derived contract using {_mint}.
           * For a generic mechanism see {ERC20PresetMinterPauser}.
           *
           * TIP: For a detailed writeup see our guide
           * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
           * to implement supply mechanisms].
           *
           * We have followed general OpenZeppelin guidelines: functions revert instead
           * of returning `false` on failure. This behavior is nonetheless conventional
           * and does not conflict with the expectations of ERC20 applications.
           *
           * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
           * This allows applications to reconstruct the allowance for all accounts just
           * by listening to said events. Other implementations of the EIP may not emit
           * these events, as it isn't required by the specification.
           *
           * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
           * functions have been added to mitigate the well-known issues around setting
           * allowances. See {IERC20-approve}.
           */
          contract ERC20 is Context, IERC20 {
            using SafeMath for uint256;
            using Address for address;
          
            mapping(address => uint256) private _balances;
          
            mapping(address => mapping(address => uint256)) private _allowances;
          
            uint256 private _totalSupply;
          
            string internal _name;
            string internal _symbol;
            uint8 private _decimals;
          
            /**
             * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
             * a default value of 18.
             *
             * To select a different value for {decimals}, use {_setupDecimals}.
             *
             * All three of these values are immutable: they can only be set once during
             * construction.
             */
            constructor(string memory name, string memory symbol) public {
              _name = name;
              _symbol = symbol;
              _decimals = 18;
            }
          
            /**
             * @dev Returns the name of the token.
             */
            function name() public view returns (string memory) {
              return _name;
            }
          
            /**
             * @dev Returns the symbol of the token, usually a shorter version of the
             * name.
             */
            function symbol() public view returns (string memory) {
              return _symbol;
            }
          
            /**
             * @dev Returns the number of decimals used to get its user representation.
             * For example, if `decimals` equals `2`, a balance of `505` tokens should
             * be displayed to a user as `5,05` (`505 / 10 ** 2`).
             *
             * Tokens usually opt for a value of 18, imitating the relationship between
             * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
             * called.
             *
             * NOTE: This information is only used for _display_ purposes: it in
             * no way affects any of the arithmetic of the contract, including
             * {IERC20-balanceOf} and {IERC20-transfer}.
             */
            function decimals() public view returns (uint8) {
              return _decimals;
            }
          
            /**
             * @dev See {IERC20-totalSupply}.
             */
            function totalSupply() public view override returns (uint256) {
              return _totalSupply;
            }
          
            /**
             * @dev See {IERC20-balanceOf}.
             */
            function balanceOf(address account) public view override returns (uint256) {
              return _balances[account];
            }
          
            /**
             * @dev See {IERC20-transfer}.
             *
             * Requirements:
             *
             * - `recipient` cannot be the zero address.
             * - the caller must have a balance of at least `amount`.
             */
            function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(_msgSender(), recipient, amount);
              return true;
            }
          
            /**
             * @dev See {IERC20-allowance}.
             */
            function allowance(address owner, address spender)
              public
              view
              virtual
              override
              returns (uint256)
            {
              return _allowances[owner][spender];
            }
          
            /**
             * @dev See {IERC20-approve}.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             */
            function approve(address spender, uint256 amount) public virtual override returns (bool) {
              _approve(_msgSender(), spender, amount);
              return true;
            }
          
            /**
             * @dev See {IERC20-transferFrom}.
             *
             * Emits an {Approval} event indicating the updated allowance. This is not
             * required by the EIP. See the note at the beginning of {ERC20};
             *
             * Requirements:
             * - `sender` and `recipient` cannot be the zero address.
             * - `sender` must have a balance of at least `amount`.
             * - the caller must have allowance for ``sender``'s tokens of at least
             * `amount`.
             */
            function transferFrom(
              address sender,
              address recipient,
              uint256 amount
            ) public virtual override returns (bool) {
              _transfer(sender, recipient, amount);
              _approve(
                sender,
                _msgSender(),
                _allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance')
              );
              return true;
            }
          
            /**
             * @dev Atomically increases the allowance granted to `spender` by the caller.
             *
             * This is an alternative to {approve} that can be used as a mitigation for
             * problems described in {IERC20-approve}.
             *
             * Emits an {Approval} event indicating the updated allowance.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             */
            function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
              return true;
            }
          
            /**
             * @dev Atomically decreases the allowance granted to `spender` by the caller.
             *
             * This is an alternative to {approve} that can be used as a mitigation for
             * problems described in {IERC20-approve}.
             *
             * Emits an {Approval} event indicating the updated allowance.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             * - `spender` must have allowance for the caller of at least
             * `subtractedValue`.
             */
            function decreaseAllowance(address spender, uint256 subtractedValue)
              public
              virtual
              returns (bool)
            {
              _approve(
                _msgSender(),
                spender,
                _allowances[_msgSender()][spender].sub(
                  subtractedValue,
                  'ERC20: decreased allowance below zero'
                )
              );
              return true;
            }
          
            /**
             * @dev Moves tokens `amount` from `sender` to `recipient`.
             *
             * This is internal function is equivalent to {transfer}, and can be used to
             * e.g. implement automatic token fees, slashing mechanisms, etc.
             *
             * Emits a {Transfer} event.
             *
             * Requirements:
             *
             * - `sender` cannot be the zero address.
             * - `recipient` cannot be the zero address.
             * - `sender` must have a balance of at least `amount`.
             */
            function _transfer(
              address sender,
              address recipient,
              uint256 amount
            ) internal virtual {
              require(sender != address(0), 'ERC20: transfer from the zero address');
              require(recipient != address(0), 'ERC20: transfer to the zero address');
          
              _beforeTokenTransfer(sender, recipient, amount);
          
              _balances[sender] = _balances[sender].sub(amount, 'ERC20: transfer amount exceeds balance');
              _balances[recipient] = _balances[recipient].add(amount);
              emit Transfer(sender, recipient, amount);
            }
          
            /** @dev Creates `amount` tokens and assigns them to `account`, increasing
             * the total supply.
             *
             * Emits a {Transfer} event with `from` set to the zero address.
             *
             * Requirements
             *
             * - `to` cannot be the zero address.
             */
            function _mint(address account, uint256 amount) internal virtual {
              require(account != address(0), 'ERC20: mint to the zero address');
          
              _beforeTokenTransfer(address(0), account, amount);
          
              _totalSupply = _totalSupply.add(amount);
              _balances[account] = _balances[account].add(amount);
              emit Transfer(address(0), account, amount);
            }
          
            /**
             * @dev Destroys `amount` tokens from `account`, reducing the
             * total supply.
             *
             * Emits a {Transfer} event with `to` set to the zero address.
             *
             * Requirements
             *
             * - `account` cannot be the zero address.
             * - `account` must have at least `amount` tokens.
             */
            function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), 'ERC20: burn from the zero address');
          
              _beforeTokenTransfer(account, address(0), amount);
          
              _balances[account] = _balances[account].sub(amount, 'ERC20: burn amount exceeds balance');
              _totalSupply = _totalSupply.sub(amount);
              emit Transfer(account, address(0), amount);
            }
          
            /**
             * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
             *
             * This is internal function is equivalent to `approve`, and can be used to
             * e.g. set automatic allowances for certain subsystems, etc.
             *
             * Emits an {Approval} event.
             *
             * Requirements:
             *
             * - `owner` cannot be the zero address.
             * - `spender` cannot be the zero address.
             */
            function _approve(
              address owner,
              address spender,
              uint256 amount
            ) internal virtual {
              require(owner != address(0), 'ERC20: approve from the zero address');
              require(spender != address(0), 'ERC20: approve to the zero address');
          
              _allowances[owner][spender] = amount;
              emit Approval(owner, spender, amount);
            }
          
            /**
             * @dev Sets {decimals} to a value other than the default one of 18.
             *
             * WARNING: This function should only be called from the constructor. Most
             * applications that interact with token contracts will not expect
             * {decimals} to ever change, and may work incorrectly if it does.
             */
            function _setupDecimals(uint8 decimals_) internal {
              _decimals = decimals_;
            }
          
            /**
             * @dev Hook that is called before any transfer of tokens. This includes
             * minting and burning.
             *
             * Calling conditions:
             *
             * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
             * will be to transferred to `to`.
             * - when `from` is zero, `amount` tokens will be minted for `to`.
             * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
             * - `from` and `to` are never both zero.
             *
             * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
             */
            function _beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
            ) internal virtual {}
          }
          
          interface IStakedAave {
            function stake(address to, uint256 amount) external;
          
            function redeem(address to, uint256 amount) external;
          
            function cooldown() external;
          
            function claimRewards(address to, uint256 amount) external;
          }
          
          interface ITransferHook {
            function onTransfer(
              address from,
              address to,
              uint256 amount
            ) external;
          }
          
          library DistributionTypes {
            struct AssetConfigInput {
              uint128 emissionPerSecond;
              uint256 totalStaked;
              address underlyingAsset;
            }
          
            struct UserStakeInput {
              address underlyingAsset;
              uint256 stakedByUser;
              uint256 totalStaked;
            }
          }
          
          /**
           * @title SafeERC20
           * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
           * Wrappers around ERC20 operations that throw on failure (when the token
           * contract returns false). Tokens that return no value (and instead revert or
           * throw on failure) are also supported, non-reverting calls are assumed to be
           * successful.
           * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
           * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
           */
          library SafeERC20 {
            using SafeMath for uint256;
            using Address for address;
          
            function safeTransfer(
              IERC20 token,
              address to,
              uint256 value
            ) internal {
              callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
            }
          
            function safeTransferFrom(
              IERC20 token,
              address from,
              address to,
              uint256 value
            ) internal {
              callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
            }
          
            function safeApprove(
              IERC20 token,
              address spender,
              uint256 value
            ) internal {
              require(
                (value == 0) || (token.allowance(address(this), spender) == 0),
                'SafeERC20: approve from non-zero to non-zero allowance'
              );
              callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
            }
          
            function callOptionalReturn(IERC20 token, bytes memory data) private {
              require(address(token).isContract(), 'SafeERC20: call to non-contract');
          
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = address(token).call(data);
              require(success, 'SafeERC20: low-level call failed');
          
              if (returndata.length > 0) {
                // Return data is optional
                // solhint-disable-next-line max-line-length
                require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed');
              }
            }
          }
          
          /**
           * @title VersionedInitializable
           *
           * @dev Helper contract to support initializer functions. To use it, replace
           * the constructor with a function that has the `initializer` modifier.
           * 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.
           *
           * @author Aave, inspired by the OpenZeppelin Initializable contract
           */
          abstract contract VersionedInitializable {
            /**
             * @dev Indicates that the contract has been initialized.
             */
            uint256 internal lastInitializedRevision = 0;
          
            /**
             * @dev Modifier to use in the initializer function of a contract.
             */
            modifier initializer() {
              uint256 revision = getRevision();
              require(revision > lastInitializedRevision, 'Contract instance has already been initialized');
          
              lastInitializedRevision = revision;
          
              _;
            }
          
            /// @dev returns the revision number of the contract.
            /// Needs to be defined in the inherited class as a constant.
            function getRevision() internal pure virtual returns (uint256);
          
            // Reserved storage space to allow for layout changes in the future.
            uint256[50] private ______gap;
          }
          
          interface IAaveDistributionManager {
            function configureAssets(DistributionTypes.AssetConfigInput[] calldata assetsConfigInput)
              external;
          }
          
          /**
           * @title AaveDistributionManager
           * @notice Accounting contract to manage multiple staking distributions
           * @author Aave
           **/
          contract AaveDistributionManager is IAaveDistributionManager {
            using SafeMath for uint256;
          
            struct AssetData {
              uint128 emissionPerSecond;
              uint128 lastUpdateTimestamp;
              uint256 index;
              mapping(address => uint256) users;
            }
          
            uint256 public immutable DISTRIBUTION_END;
          
            address public immutable EMISSION_MANAGER;
          
            uint8 public constant PRECISION = 18;
          
            mapping(address => AssetData) public assets;
          
            event AssetConfigUpdated(address indexed asset, uint256 emission);
            event AssetIndexUpdated(address indexed asset, uint256 index);
            event UserIndexUpdated(address indexed user, address indexed asset, uint256 index);
          
            constructor(address emissionManager, uint256 distributionDuration) public {
              DISTRIBUTION_END = block.timestamp.add(distributionDuration);
              EMISSION_MANAGER = emissionManager;
            }
          
            /**
             * @dev Configures the distribution of rewards for a list of assets
             * @param assetsConfigInput The list of configurations to apply
             **/
            function configureAssets(DistributionTypes.AssetConfigInput[] calldata assetsConfigInput)
              external
              override
            {
              require(msg.sender == EMISSION_MANAGER, 'ONLY_EMISSION_MANAGER');
          
              for (uint256 i = 0; i < assetsConfigInput.length; i++) {
                AssetData storage assetConfig = assets[assetsConfigInput[i].underlyingAsset];
          
                _updateAssetStateInternal(
                  assetsConfigInput[i].underlyingAsset,
                  assetConfig,
                  assetsConfigInput[i].totalStaked
                );
          
                assetConfig.emissionPerSecond = assetsConfigInput[i].emissionPerSecond;
          
                emit AssetConfigUpdated(
                  assetsConfigInput[i].underlyingAsset,
                  assetsConfigInput[i].emissionPerSecond
                );
              }
            }
          
            /**
             * @dev Updates the state of one distribution, mainly rewards index and timestamp
             * @param underlyingAsset The address used as key in the distribution, for example sAAVE or the aTokens addresses on Aave
             * @param assetConfig Storage pointer to the distribution's config
             * @param totalStaked Current total of staked assets for this distribution
             * @return The new distribution index
             **/
            function _updateAssetStateInternal(
              address underlyingAsset,
              AssetData storage assetConfig,
              uint256 totalStaked
            ) internal returns (uint256) {
              uint256 oldIndex = assetConfig.index;
              uint128 lastUpdateTimestamp = assetConfig.lastUpdateTimestamp;
          
              if (block.timestamp == lastUpdateTimestamp) {
                return oldIndex;
              }
          
              uint256 newIndex =
                _getAssetIndex(oldIndex, assetConfig.emissionPerSecond, lastUpdateTimestamp, totalStaked);
          
              if (newIndex != oldIndex) {
                assetConfig.index = newIndex;
                emit AssetIndexUpdated(underlyingAsset, newIndex);
              }
          
              assetConfig.lastUpdateTimestamp = uint128(block.timestamp);
          
              return newIndex;
            }
          
            /**
             * @dev Updates the state of an user in a distribution
             * @param user The user's address
             * @param asset The address of the reference asset of the distribution
             * @param stakedByUser Amount of tokens staked by the user in the distribution at the moment
             * @param totalStaked Total tokens staked in the distribution
             * @return The accrued rewards for the user until the moment
             **/
            function _updateUserAssetInternal(
              address user,
              address asset,
              uint256 stakedByUser,
              uint256 totalStaked
            ) internal returns (uint256) {
              AssetData storage assetData = assets[asset];
              uint256 userIndex = assetData.users[user];
              uint256 accruedRewards = 0;
          
              uint256 newIndex = _updateAssetStateInternal(asset, assetData, totalStaked);
          
              if (userIndex != newIndex) {
                if (stakedByUser != 0) {
                  accruedRewards = _getRewards(stakedByUser, newIndex, userIndex);
                }
          
                assetData.users[user] = newIndex;
                emit UserIndexUpdated(user, asset, newIndex);
              }
          
              return accruedRewards;
            }
          
            /**
             * @dev Used by "frontend" stake contracts to update the data of an user when claiming rewards from there
             * @param user The address of the user
             * @param stakes List of structs of the user data related with his stake
             * @return The accrued rewards for the user until the moment
             **/
            function _claimRewards(address user, DistributionTypes.UserStakeInput[] memory stakes)
              internal
              returns (uint256)
            {
              uint256 accruedRewards = 0;
          
              for (uint256 i = 0; i < stakes.length; i++) {
                accruedRewards = accruedRewards.add(
                  _updateUserAssetInternal(
                    user,
                    stakes[i].underlyingAsset,
                    stakes[i].stakedByUser,
                    stakes[i].totalStaked
                  )
                );
              }
          
              return accruedRewards;
            }
          
            /**
             * @dev Return the accrued rewards for an user over a list of distribution
             * @param user The address of the user
             * @param stakes List of structs of the user data related with his stake
             * @return The accrued rewards for the user until the moment
             **/
            function _getUnclaimedRewards(address user, DistributionTypes.UserStakeInput[] memory stakes)
              internal
              view
              returns (uint256)
            {
              uint256 accruedRewards = 0;
          
              for (uint256 i = 0; i < stakes.length; i++) {
                AssetData storage assetConfig = assets[stakes[i].underlyingAsset];
                uint256 assetIndex =
                  _getAssetIndex(
                    assetConfig.index,
                    assetConfig.emissionPerSecond,
                    assetConfig.lastUpdateTimestamp,
                    stakes[i].totalStaked
                  );
          
                accruedRewards = accruedRewards.add(
                  _getRewards(stakes[i].stakedByUser, assetIndex, assetConfig.users[user])
                );
              }
              return accruedRewards;
            }
          
            /**
             * @dev Internal function for the calculation of user's rewards on a distribution
             * @param principalUserBalance Amount staked by the user on a distribution
             * @param reserveIndex Current index of the distribution
             * @param userIndex Index stored for the user, representation his staking moment
             * @return The rewards
             **/
            function _getRewards(
              uint256 principalUserBalance,
              uint256 reserveIndex,
              uint256 userIndex
            ) internal pure returns (uint256) {
              return principalUserBalance.mul(reserveIndex.sub(userIndex)).div(10**uint256(PRECISION));
            }
          
            /**
             * @dev Calculates the next value of an specific distribution index, with validations
             * @param currentIndex Current index of the distribution
             * @param emissionPerSecond Representing the total rewards distributed per second per asset unit, on the distribution
             * @param lastUpdateTimestamp Last moment this distribution was updated
             * @param totalBalance of tokens considered for the distribution
             * @return The new index.
             **/
            function _getAssetIndex(
              uint256 currentIndex,
              uint256 emissionPerSecond,
              uint128 lastUpdateTimestamp,
              uint256 totalBalance
            ) internal view returns (uint256) {
              if (
                emissionPerSecond == 0 ||
                totalBalance == 0 ||
                lastUpdateTimestamp == block.timestamp ||
                lastUpdateTimestamp >= DISTRIBUTION_END
              ) {
                return currentIndex;
              }
          
              uint256 currentTimestamp =
                block.timestamp > DISTRIBUTION_END ? DISTRIBUTION_END : block.timestamp;
              uint256 timeDelta = currentTimestamp.sub(lastUpdateTimestamp);
              return
                emissionPerSecond.mul(timeDelta).mul(10**uint256(PRECISION)).div(totalBalance).add(
                  currentIndex
                );
            }
          
            /**
             * @dev Returns the data of an user on a distribution
             * @param user Address of the user
             * @param asset The address of the reference asset of the distribution
             * @return The new index
             **/
            function getUserAssetData(address user, address asset) public view returns (uint256) {
              return assets[asset].users[user];
            }
          }
          
          /**
           * @notice implementation of the AAVE token contract
           * @author Aave
           */
          abstract contract GovernancePowerDelegationERC20 is ERC20, IGovernancePowerDelegationToken {
            using SafeMath for uint256;
            /// @notice The EIP-712 typehash for the delegation struct used by the contract
            bytes32 public constant DELEGATE_BY_TYPE_TYPEHASH =
              keccak256('DelegateByType(address delegatee,uint256 type,uint256 nonce,uint256 expiry)');
          
            bytes32 public constant DELEGATE_TYPEHASH =
              keccak256('Delegate(address delegatee,uint256 nonce,uint256 expiry)');
          
            /// @dev snapshot of a value on a specific block, used for votes
            struct Snapshot {
              uint128 blockNumber;
              uint128 value;
            }
          
            /**
             * @dev delegates one specific power to a delegatee
             * @param delegatee the user which delegated power has changed
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            function delegateByType(address delegatee, DelegationType delegationType) external override {
              _delegateByType(msg.sender, delegatee, delegationType);
            }
          
            /**
             * @dev delegates all the powers to a specific user
             * @param delegatee the user to which the power will be delegated
             **/
            function delegate(address delegatee) external override {
              _delegateByType(msg.sender, delegatee, DelegationType.VOTING_POWER);
              _delegateByType(msg.sender, delegatee, DelegationType.PROPOSITION_POWER);
            }
          
            /**
             * @dev returns the delegatee of an user
             * @param delegator the address of the delegator
             **/
            function getDelegateeByType(address delegator, DelegationType delegationType)
              external
              view
              override
              returns (address)
            {
              (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType);
          
              return _getDelegatee(delegator, delegates);
            }
          
            /**
             * @dev returns the current delegated power of a user. The current power is the
             * power delegated at the time of the last snapshot
             * @param user the user
             **/
            function getPowerCurrent(address user, DelegationType delegationType)
              external
              view
              override
              returns (uint256)
            {
              (
                mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
                mapping(address => uint256) storage snapshotsCounts,
          
              ) = _getDelegationDataByType(delegationType);
          
              return _searchByBlockNumber(snapshots, snapshotsCounts, user, block.number);
            }
          
            /**
             * @dev returns the delegated power of a user at a certain block
             * @param user the user
             **/
            function getPowerAtBlock(
              address user,
              uint256 blockNumber,
              DelegationType delegationType
            ) external view override returns (uint256) {
              (
                mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
                mapping(address => uint256) storage snapshotsCounts,
          
              ) = _getDelegationDataByType(delegationType);
          
              return _searchByBlockNumber(snapshots, snapshotsCounts, user, blockNumber);
            }
          
            /**
             * @dev returns the total supply at a certain block number
             * used by the voting strategy contracts to calculate the total votes needed for threshold/quorum
             * In this initial implementation with no AAVE minting, simply returns the current supply
             * A snapshots mapping will need to be added in case a mint function is added to the AAVE token in the future
             **/
            function totalSupplyAt(uint256 blockNumber) external view override returns (uint256) {
              return super.totalSupply();
            }
          
            /**
             * @dev delegates the specific power to a delegatee
             * @param delegatee the user which delegated power has changed
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            function _delegateByType(
              address delegator,
              address delegatee,
              DelegationType delegationType
            ) internal {
              require(delegatee != address(0), 'INVALID_DELEGATEE');
          
              (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType);
          
              uint256 delegatorBalance = balanceOf(delegator);
          
              address previousDelegatee = _getDelegatee(delegator, delegates);
          
              delegates[delegator] = delegatee;
          
              _moveDelegatesByType(previousDelegatee, delegatee, delegatorBalance, delegationType);
              emit DelegateChanged(delegator, delegatee, delegationType);
            }
          
            /**
             * @dev moves delegated power from one user to another
             * @param from the user from which delegated power is moved
             * @param to the user that will receive the delegated power
             * @param amount the amount of delegated power to be moved
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            function _moveDelegatesByType(
              address from,
              address to,
              uint256 amount,
              DelegationType delegationType
            ) internal {
              if (from == to) {
                return;
              }
          
              (
                mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
                mapping(address => uint256) storage snapshotsCounts,
          
              ) = _getDelegationDataByType(delegationType);
          
              if (from != address(0)) {
                uint256 previous = 0;
                uint256 fromSnapshotsCount = snapshotsCounts[from];
          
                if (fromSnapshotsCount != 0) {
                  previous = snapshots[from][fromSnapshotsCount - 1].value;
                } else {
                  previous = balanceOf(from);
                }
          
                _writeSnapshot(
                  snapshots,
                  snapshotsCounts,
                  from,
                  uint128(previous),
                  uint128(previous.sub(amount))
                );
          
                emit DelegatedPowerChanged(from, previous.sub(amount), delegationType);
              }
              if (to != address(0)) {
                uint256 previous = 0;
                uint256 toSnapshotsCount = snapshotsCounts[to];
                if (toSnapshotsCount != 0) {
                  previous = snapshots[to][toSnapshotsCount - 1].value;
                } else {
                  previous = balanceOf(to);
                }
          
                _writeSnapshot(
                  snapshots,
                  snapshotsCounts,
                  to,
                  uint128(previous),
                  uint128(previous.add(amount))
                );
          
                emit DelegatedPowerChanged(to, previous.add(amount), delegationType);
              }
            }
          
            /**
             * @dev searches a snapshot by block number. Uses binary search.
             * @param snapshots the snapshots mapping
             * @param snapshotsCounts the number of snapshots
             * @param user the user for which the snapshot is being searched
             * @param blockNumber the block number being searched
             **/
            function _searchByBlockNumber(
              mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
              mapping(address => uint256) storage snapshotsCounts,
              address user,
              uint256 blockNumber
            ) internal view returns (uint256) {
              require(blockNumber <= block.number, 'INVALID_BLOCK_NUMBER');
          
              uint256 snapshotsCount = snapshotsCounts[user];
          
              if (snapshotsCount == 0) {
                return balanceOf(user);
              }
          
              // First check most recent balance
              if (snapshots[user][snapshotsCount - 1].blockNumber <= blockNumber) {
                return snapshots[user][snapshotsCount - 1].value;
              }
          
              // Next check implicit zero balance
              if (snapshots[user][0].blockNumber > blockNumber) {
                return 0;
              }
          
              uint256 lower = 0;
              uint256 upper = snapshotsCount - 1;
              while (upper > lower) {
                uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
                Snapshot memory snapshot = snapshots[user][center];
                if (snapshot.blockNumber == blockNumber) {
                  return snapshot.value;
                } else if (snapshot.blockNumber < blockNumber) {
                  lower = center;
                } else {
                  upper = center - 1;
                }
              }
              return snapshots[user][lower].value;
            }
          
            /**
             * @dev returns the delegation data (snapshot, snapshotsCount, list of delegates) by delegation type
             * NOTE: Ideal implementation would have mapped this in a struct by delegation type. Unfortunately,
             * the AAVE token and StakeToken already include a mapping for the snapshots, so we require contracts
             * who inherit from this to provide access to the delegation data by overriding this method.
             * @param delegationType the type of delegation
             **/
            function _getDelegationDataByType(DelegationType delegationType)
              internal
              view
              virtual
              returns (
                mapping(address => mapping(uint256 => Snapshot)) storage, //snapshots
                mapping(address => uint256) storage, //snapshots count
                mapping(address => address) storage //delegatees list
              );
          
            /**
             * @dev Writes a snapshot for an owner of tokens
             * @param owner The owner of the tokens
             * @param oldValue The value before the operation that is gonna be executed after the snapshot
             * @param newValue The value after the operation
             */
            function _writeSnapshot(
              mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
              mapping(address => uint256) storage snapshotsCounts,
              address owner,
              uint128 oldValue,
              uint128 newValue
            ) internal {
              uint128 currentBlock = uint128(block.number);
          
              uint256 ownerSnapshotsCount = snapshotsCounts[owner];
              mapping(uint256 => Snapshot) storage snapshotsOwner = snapshots[owner];
          
              // Doing multiple operations in the same block
              if (
                ownerSnapshotsCount != 0 &&
                snapshotsOwner[ownerSnapshotsCount - 1].blockNumber == currentBlock
              ) {
                snapshotsOwner[ownerSnapshotsCount - 1].value = newValue;
              } else {
                snapshotsOwner[ownerSnapshotsCount] = Snapshot(currentBlock, newValue);
                snapshotsCounts[owner] = ownerSnapshotsCount + 1;
              }
            }
          
            /**
             * @dev returns the user delegatee. If a user never performed any delegation,
             * his delegated address will be 0x0. In that case we simply return the user itself
             * @param delegator the address of the user for which return the delegatee
             * @param delegates the array of delegates for a particular type of delegation
             **/
            function _getDelegatee(address delegator, mapping(address => address) storage delegates)
              internal
              view
              returns (address)
            {
              address previousDelegatee = delegates[delegator];
          
              if (previousDelegatee == address(0)) {
                return delegator;
              }
          
              return previousDelegatee;
            }
          }
          
          /**
           * @title ERC20WithSnapshot
           * @notice ERC20 including snapshots of balances on transfer-related actions
           * @author Aave
           **/
          abstract contract GovernancePowerWithSnapshot is GovernancePowerDelegationERC20 {
            using SafeMath for uint256;
          
            /**
             * @dev The following storage layout points to the prior StakedToken.sol implementation:
             * _snapshots => _votingSnapshots
             * _snapshotsCounts =>  _votingSnapshotsCounts
             * _aaveGovernance => _aaveGovernance
             */
            mapping(address => mapping(uint256 => Snapshot)) public _votingSnapshots;
            mapping(address => uint256) public _votingSnapshotsCounts;
          
            /// @dev reference to the Aave governance contract to call (if initialized) on _beforeTokenTransfer
            /// !!! IMPORTANT The Aave governance is considered a trustable contract, being its responsibility
            /// to control all potential reentrancies by calling back the this contract
            ITransferHook public _aaveGovernance;
          
            function _setAaveGovernance(ITransferHook aaveGovernance) internal virtual {
              _aaveGovernance = aaveGovernance;
            }
          }
          
          /**
           * @title StakedToken
           * @notice Contract to stake Aave token, tokenize the position and get rewards, inheriting from a distribution manager contract
           * @author Aave
           **/
          contract StakedTokenV2Rev3 is
            IStakedAave,
            GovernancePowerWithSnapshot,
            VersionedInitializable,
            AaveDistributionManager
          {
            using SafeMath for uint256;
            using SafeERC20 for IERC20;
          
            /// @dev Start of Storage layout from StakedToken v1
            uint256 public constant REVISION = 3;
          
            IERC20 public immutable STAKED_TOKEN;
            IERC20 public immutable REWARD_TOKEN;
            uint256 public immutable COOLDOWN_SECONDS;
          
            /// @notice Seconds available to redeem once the cooldown period is fullfilled
            uint256 public immutable UNSTAKE_WINDOW;
          
            /// @notice Address to pull from the rewards, needs to have approved this contract
            address public immutable REWARDS_VAULT;
          
            mapping(address => uint256) public stakerRewardsToClaim;
            mapping(address => uint256) public stakersCooldowns;
          
            /// @dev End of Storage layout from StakedToken v1
          
            /// @dev To see the voting mappings, go to GovernancePowerWithSnapshot.sol
            mapping(address => address) internal _votingDelegates;
          
            mapping(address => mapping(uint256 => Snapshot)) internal _propositionPowerSnapshots;
            mapping(address => uint256) internal _propositionPowerSnapshotsCounts;
            mapping(address => address) internal _propositionPowerDelegates;
          
            bytes32 public DOMAIN_SEPARATOR;
            bytes public constant EIP712_REVISION = bytes('1');
            bytes32 internal constant EIP712_DOMAIN =
              keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');
            bytes32 public constant PERMIT_TYPEHASH =
              keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');
          
            /// @dev owner => next valid nonce to submit with permit()
            mapping(address => uint256) public _nonces;
          
            event Staked(address indexed from, address indexed onBehalfOf, uint256 amount);
            event Redeem(address indexed from, address indexed to, uint256 amount);
          
            event RewardsAccrued(address user, uint256 amount);
            event RewardsClaimed(address indexed from, address indexed to, uint256 amount);
          
            event Cooldown(address indexed user);
          
            constructor(
              IERC20 stakedToken,
              IERC20 rewardToken,
              uint256 cooldownSeconds,
              uint256 unstakeWindow,
              address rewardsVault,
              address emissionManager,
              uint128 distributionDuration,
              string memory name,
              string memory symbol,
              uint8 decimals,
              address governance
            ) public ERC20(name, symbol) AaveDistributionManager(emissionManager, distributionDuration) {
              STAKED_TOKEN = stakedToken;
              REWARD_TOKEN = rewardToken;
              COOLDOWN_SECONDS = cooldownSeconds;
              UNSTAKE_WINDOW = unstakeWindow;
              REWARDS_VAULT = rewardsVault;
              _aaveGovernance = ITransferHook(governance);
              ERC20._setupDecimals(decimals);
            }
          
            /**
             * @dev Called by the proxy contract
             **/
            function initialize() external initializer {
              uint256 chainId;
          
              //solium-disable-next-line
              assembly {
                chainId := chainid()
              }
          
              DOMAIN_SEPARATOR = keccak256(
                abi.encode(
                  EIP712_DOMAIN,
                  keccak256(bytes(name())),
                  keccak256(EIP712_REVISION),
                  chainId,
                  address(this)
                )
              );
          
              // Update lastUpdateTimestamp of stkAave to reward users since the end of the prior staking period
              AssetData storage assetData = assets[address(this)];
              assetData.lastUpdateTimestamp = 1620594720;
            }
          
            function stake(address onBehalfOf, uint256 amount) external override {
              require(amount != 0, 'INVALID_ZERO_AMOUNT');
              uint256 balanceOfUser = balanceOf(onBehalfOf);
          
              uint256 accruedRewards =
                _updateUserAssetInternal(onBehalfOf, address(this), balanceOfUser, totalSupply());
              if (accruedRewards != 0) {
                emit RewardsAccrued(onBehalfOf, accruedRewards);
                stakerRewardsToClaim[onBehalfOf] = stakerRewardsToClaim[onBehalfOf].add(accruedRewards);
              }
          
              stakersCooldowns[onBehalfOf] = getNextCooldownTimestamp(0, amount, onBehalfOf, balanceOfUser);
          
              _mint(onBehalfOf, amount);
              IERC20(STAKED_TOKEN).safeTransferFrom(msg.sender, address(this), amount);
          
              emit Staked(msg.sender, onBehalfOf, amount);
            }
          
            /**
             * @dev Redeems staked tokens, and stop earning rewards
             * @param to Address to redeem to
             * @param amount Amount to redeem
             **/
            function redeem(address to, uint256 amount) external override {
              require(amount != 0, 'INVALID_ZERO_AMOUNT');
              //solium-disable-next-line
              uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];
              require(
                block.timestamp > cooldownStartTimestamp.add(COOLDOWN_SECONDS),
                'INSUFFICIENT_COOLDOWN'
              );
              require(
                block.timestamp.sub(cooldownStartTimestamp.add(COOLDOWN_SECONDS)) <= UNSTAKE_WINDOW,
                'UNSTAKE_WINDOW_FINISHED'
              );
              uint256 balanceOfMessageSender = balanceOf(msg.sender);
          
              uint256 amountToRedeem = (amount > balanceOfMessageSender) ? balanceOfMessageSender : amount;
          
              _updateCurrentUnclaimedRewards(msg.sender, balanceOfMessageSender, true);
          
              _burn(msg.sender, amountToRedeem);
          
              if (balanceOfMessageSender.sub(amountToRedeem) == 0) {
                stakersCooldowns[msg.sender] = 0;
              }
          
              IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);
          
              emit Redeem(msg.sender, to, amountToRedeem);
            }
          
            /**
             * @dev Activates the cooldown period to unstake
             * - It can't be called if the user is not staking
             **/
            function cooldown() external override {
              require(balanceOf(msg.sender) != 0, 'INVALID_BALANCE_ON_COOLDOWN');
              //solium-disable-next-line
              stakersCooldowns[msg.sender] = block.timestamp;
          
              emit Cooldown(msg.sender);
            }
          
            /**
             * @dev Claims an `amount` of `REWARD_TOKEN` to the address `to`
             * @param to Address to stake for
             * @param amount Amount to stake
             **/
            function claimRewards(address to, uint256 amount) external override {
              uint256 newTotalRewards =
                _updateCurrentUnclaimedRewards(msg.sender, balanceOf(msg.sender), false);
              uint256 amountToClaim = (amount == type(uint256).max) ? newTotalRewards : amount;
          
              stakerRewardsToClaim[msg.sender] = newTotalRewards.sub(amountToClaim, 'INVALID_AMOUNT');
          
              REWARD_TOKEN.safeTransferFrom(REWARDS_VAULT, to, amountToClaim);
          
              emit RewardsClaimed(msg.sender, to, amountToClaim);
            }
          
            /**
             * @dev Internal ERC20 _transfer of the tokenized staked tokens
             * @param from Address to transfer from
             * @param to Address to transfer to
             * @param amount Amount to transfer
             **/
            function _transfer(
              address from,
              address to,
              uint256 amount
            ) internal override {
              uint256 balanceOfFrom = balanceOf(from);
              // Sender
              _updateCurrentUnclaimedRewards(from, balanceOfFrom, true);
          
              // Recipient
              if (from != to) {
                uint256 balanceOfTo = balanceOf(to);
                _updateCurrentUnclaimedRewards(to, balanceOfTo, true);
          
                uint256 previousSenderCooldown = stakersCooldowns[from];
                stakersCooldowns[to] = getNextCooldownTimestamp(
                  previousSenderCooldown,
                  amount,
                  to,
                  balanceOfTo
                );
                // if cooldown was set and whole balance of sender was transferred - clear cooldown
                if (balanceOfFrom == amount && previousSenderCooldown != 0) {
                  stakersCooldowns[from] = 0;
                }
              }
          
              super._transfer(from, to, amount);
            }
          
            /**
             * @dev Updates the user state related with his accrued rewards
             * @param user Address of the user
             * @param userBalance The current balance of the user
             * @param updateStorage Boolean flag used to update or not the stakerRewardsToClaim of the user
             * @return The unclaimed rewards that were added to the total accrued
             **/
            function _updateCurrentUnclaimedRewards(
              address user,
              uint256 userBalance,
              bool updateStorage
            ) internal returns (uint256) {
              uint256 accruedRewards =
                _updateUserAssetInternal(user, address(this), userBalance, totalSupply());
              uint256 unclaimedRewards = stakerRewardsToClaim[user].add(accruedRewards);
          
              if (accruedRewards != 0) {
                if (updateStorage) {
                  stakerRewardsToClaim[user] = unclaimedRewards;
                }
                emit RewardsAccrued(user, accruedRewards);
              }
          
              return unclaimedRewards;
            }
          
            /**
             * @dev Calculates the how is gonna be a new cooldown timestamp depending on the sender/receiver situation
             *  - If the timestamp of the sender is "better" or the timestamp of the recipient is 0, we take the one of the recipient
             *  - Weighted average of from/to cooldown timestamps if:
             *    # The sender doesn't have the cooldown activated (timestamp 0).
             *    # The sender timestamp is expired
             *    # The sender has a "worse" timestamp
             *  - If the receiver's cooldown timestamp expired (too old), the next is 0
             * @param fromCooldownTimestamp Cooldown timestamp of the sender
             * @param amountToReceive Amount
             * @param toAddress Address of the recipient
             * @param toBalance Current balance of the receiver
             * @return The new cooldown timestamp
             **/
            function getNextCooldownTimestamp(
              uint256 fromCooldownTimestamp,
              uint256 amountToReceive,
              address toAddress,
              uint256 toBalance
            ) public view returns (uint256) {
              uint256 toCooldownTimestamp = stakersCooldowns[toAddress];
              if (toCooldownTimestamp == 0) {
                return 0;
              }
          
              uint256 minimalValidCooldownTimestamp =
                block.timestamp.sub(COOLDOWN_SECONDS).sub(UNSTAKE_WINDOW);
          
              if (minimalValidCooldownTimestamp > toCooldownTimestamp) {
                toCooldownTimestamp = 0;
              } else {
                uint256 fromCooldownTimestamp =
                  (minimalValidCooldownTimestamp > fromCooldownTimestamp)
                    ? block.timestamp
                    : fromCooldownTimestamp;
          
                if (fromCooldownTimestamp < toCooldownTimestamp) {
                  return toCooldownTimestamp;
                } else {
                  toCooldownTimestamp = (
                    amountToReceive.mul(fromCooldownTimestamp).add(toBalance.mul(toCooldownTimestamp))
                  )
                    .div(amountToReceive.add(toBalance));
                }
              }
              return toCooldownTimestamp;
            }
          
            /**
             * @dev Return the total rewards pending to claim by an staker
             * @param staker The staker address
             * @return The rewards
             */
            function getTotalRewardsBalance(address staker) external view returns (uint256) {
              DistributionTypes.UserStakeInput[] memory userStakeInputs =
                new DistributionTypes.UserStakeInput[](1);
              userStakeInputs[0] = DistributionTypes.UserStakeInput({
                underlyingAsset: address(this),
                stakedByUser: balanceOf(staker),
                totalStaked: totalSupply()
              });
              return stakerRewardsToClaim[staker].add(_getUnclaimedRewards(staker, userStakeInputs));
            }
          
            /**
             * @dev returns the revision of the implementation contract
             * @return The revision
             */
            function getRevision() internal pure override returns (uint256) {
              return REVISION;
            }
          
            /**
             * @dev implements the permit function as for https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
             * @param owner the owner of the funds
             * @param spender the spender
             * @param value the amount
             * @param deadline the deadline timestamp, type(uint256).max for no deadline
             * @param v signature param
             * @param s signature param
             * @param r signature param
             */
          
            function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external {
              require(owner != address(0), 'INVALID_OWNER');
              //solium-disable-next-line
              require(block.timestamp <= deadline, 'INVALID_EXPIRATION');
              uint256 currentValidNonce = _nonces[owner];
              bytes32 digest =
                keccak256(
                  abi.encodePacked(
                    '\x19\x01',
                    DOMAIN_SEPARATOR,
                    keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
                  )
                );
          
              require(owner == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE');
              _nonces[owner] = currentValidNonce.add(1);
              _approve(owner, spender, value);
            }
          
            /**
             * @dev Writes a snapshot before any operation involving transfer of value: _transfer, _mint and _burn
             * - On _transfer, it writes snapshots for both "from" and "to"
             * - On _mint, only for _to
             * - On _burn, only for _from
             * @param from the from address
             * @param to the to address
             * @param amount the amount to transfer
             */
            function _beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
            ) internal override {
              address votingFromDelegatee = _votingDelegates[from];
              address votingToDelegatee = _votingDelegates[to];
          
              if (votingFromDelegatee == address(0)) {
                votingFromDelegatee = from;
              }
              if (votingToDelegatee == address(0)) {
                votingToDelegatee = to;
              }
          
              _moveDelegatesByType(
                votingFromDelegatee,
                votingToDelegatee,
                amount,
                DelegationType.VOTING_POWER
              );
          
              address propPowerFromDelegatee = _propositionPowerDelegates[from];
              address propPowerToDelegatee = _propositionPowerDelegates[to];
          
              if (propPowerFromDelegatee == address(0)) {
                propPowerFromDelegatee = from;
              }
              if (propPowerToDelegatee == address(0)) {
                propPowerToDelegatee = to;
              }
          
              _moveDelegatesByType(
                propPowerFromDelegatee,
                propPowerToDelegatee,
                amount,
                DelegationType.PROPOSITION_POWER
              );
          
              // caching the aave governance address to avoid multiple state loads
              ITransferHook aaveGovernance = _aaveGovernance;
              if (aaveGovernance != ITransferHook(0)) {
                aaveGovernance.onTransfer(from, to, amount);
              }
            }
          
            function _getDelegationDataByType(DelegationType delegationType)
              internal
              view
              override
              returns (
                mapping(address => mapping(uint256 => Snapshot)) storage, //snapshots
                mapping(address => uint256) storage, //snapshots count
                mapping(address => address) storage //delegatees list
              )
            {
              if (delegationType == DelegationType.VOTING_POWER) {
                return (_votingSnapshots, _votingSnapshotsCounts, _votingDelegates);
              } else {
                return (
                  _propositionPowerSnapshots,
                  _propositionPowerSnapshotsCounts,
                  _propositionPowerDelegates
                );
              }
            }
          
            /**
             * @dev Delegates power from signatory to `delegatee`
             * @param delegatee The address to delegate votes to
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             * @param nonce The contract state required to match the signature
             * @param expiry The time at which to expire the signature
             * @param v The recovery byte of the signature
             * @param r Half of the ECDSA signature pair
             * @param s Half of the ECDSA signature pair
             */
            function delegateByTypeBySig(
              address delegatee,
              DelegationType delegationType,
              uint256 nonce,
              uint256 expiry,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) public {
              bytes32 structHash =
                keccak256(
                  abi.encode(DELEGATE_BY_TYPE_TYPEHASH, delegatee, uint256(delegationType), nonce, expiry)
                );
              bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash));
              address signatory = ecrecover(digest, v, r, s);
              require(signatory != address(0), 'INVALID_SIGNATURE');
              require(nonce == _nonces[signatory]++, 'INVALID_NONCE');
              require(block.timestamp <= expiry, 'INVALID_EXPIRATION');
              _delegateByType(signatory, delegatee, delegationType);
            }
          
            /**
             * @dev Delegates power from signatory to `delegatee`
             * @param delegatee The address to delegate votes to
             * @param nonce The contract state required to match the signature
             * @param expiry The time at which to expire the signature
             * @param v The recovery byte of the signature
             * @param r Half of the ECDSA signature pair
             * @param s Half of the ECDSA signature pair
             */
            function delegateBySig(
              address delegatee,
              uint256 nonce,
              uint256 expiry,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) public {
              bytes32 structHash = keccak256(abi.encode(DELEGATE_TYPEHASH, delegatee, nonce, expiry));
              bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash));
              address signatory = ecrecover(digest, v, r, s);
              require(signatory != address(0), 'INVALID_SIGNATURE');
              require(nonce == _nonces[signatory]++, 'INVALID_NONCE');
              require(block.timestamp <= expiry, 'INVALID_EXPIRATION');
              _delegateByType(signatory, delegatee, DelegationType.VOTING_POWER);
              _delegateByType(signatory, delegatee, DelegationType.PROPOSITION_POWER);
            }
          }

          File 5 of 6: InitializableAdminUpgradeabilityProxy
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.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.6.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: agpl-3.0
          pragma solidity 0.6.10;
          interface ITransferHook {
              function onTransfer(address from, address to, uint256 amount) external;
          }pragma solidity ^0.6.2;
          /**
           * @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");
              }
          }pragma solidity ^0.6.0;
          import './UpgradeabilityProxy.sol';
          /**
           * @title BaseAdminUpgradeabilityProxy
           * @dev This contract combines an upgradeability proxy with an authorization
           * mechanism for administrative tasks.
           * 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 BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
            /**
             * @dev Emitted when the administration has been transferred.
             * @param previousAdmin Address of the previous admin.
             * @param newAdmin Address of the new admin.
             */
            event AdminChanged(address previousAdmin, address newAdmin);
            /**
             * @dev Storage slot with the admin of the contract.
             * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
             * validated in the constructor.
             */
            bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
            /**
             * @dev Modifier to check whether the `msg.sender` is the admin.
             * If it is, it will run the function. Otherwise, it will delegate the call
             * to the implementation.
             */
            modifier ifAdmin() {
              if (msg.sender == _admin()) {
                _;
              } else {
                _fallback();
              }
            }
            /**
             * @return The address of the proxy admin.
             */
            function admin() external ifAdmin returns (address) {
              return _admin();
            }
            /**
             * @return The address of the implementation.
             */
            function implementation() external ifAdmin returns (address) {
              return _implementation();
            }
            /**
             * @dev Changes the admin of the proxy.
             * Only the current admin can call this function.
             * @param newAdmin Address to transfer proxy administration to.
             */
            function changeAdmin(address newAdmin) external ifAdmin {
              require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
              emit AdminChanged(_admin(), newAdmin);
              _setAdmin(newAdmin);
            }
            /**
             * @dev Upgrade the backing implementation of the proxy.
             * Only the admin can call this function.
             * @param newImplementation Address of the new implementation.
             */
            function upgradeTo(address newImplementation) external ifAdmin {
              _upgradeTo(newImplementation);
            }
            /**
             * @dev Upgrade the backing implementation of the proxy and call a function
             * on the new implementation.
             * This is useful to initialize the proxied contract.
             * @param newImplementation 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) payable external ifAdmin {
              _upgradeTo(newImplementation);
              (bool success,) = newImplementation.delegatecall(data);
              require(success);
            }
            /**
             * @return adm The admin slot.
             */
            function _admin() internal view returns (address adm) {
              bytes32 slot = ADMIN_SLOT;
              assembly {
                adm := sload(slot)
              }
            }
            /**
             * @dev Sets the address of the proxy admin.
             * @param newAdmin Address of the new proxy admin.
             */
            function _setAdmin(address newAdmin) internal {
              bytes32 slot = ADMIN_SLOT;
              assembly {
                sstore(slot, newAdmin)
              }
            }
            /**
             * @dev Only fall back when the sender is not the admin.
             */
            function _willFallback() internal override virtual {
              require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
              super._willFallback();
            }
          }pragma solidity ^0.6.0;
          import './BaseUpgradeabilityProxy.sol';
          /**
           * @title UpgradeabilityProxy
           * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing
           * implementation and init data.
           */
          contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
            /**
             * @dev Contract constructor.
             * @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.
             */
            constructor(address _logic, bytes memory _data) public payable {
              assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
              _setImplementation(_logic);
              if(_data.length > 0) {
                (bool success,) = _logic.delegatecall(_data);
                require(success);
              }
            }  
          }pragma solidity ^0.6.0;
          import './Proxy.sol';
          import './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 override view returns (address impl) {
              bytes32 slot = IMPLEMENTATION_SLOT;
              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;
              assembly {
                sstore(slot, newImplementation)
              }
            }
          }pragma solidity ^0.6.0;
          /**
           * @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.
             * Implemented entirely in `_fallback`.
             */
            fallback () payable external {
              _fallback();
            }
            /**
             * @return The Address of the implementation.
             */
            function _implementation() internal virtual view 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 {
              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: MIT
          pragma solidity ^0.6.0;
          /*
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with GSN meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address payable) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes memory) {
                  this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                  return msg.data;
              }
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "./Context.sol";
          import "../interfaces/IERC20.sol";
          import "./SafeMath.sol";
          import "./Address.sol";
          /**
           * @dev Implementation of the {IERC20} interface.
           *
           * This implementation is agnostic to the way tokens are created. This means
           * that a supply mechanism has to be added in a derived contract using {_mint}.
           * For a generic mechanism see {ERC20PresetMinterPauser}.
           *
           * TIP: For a detailed writeup see our guide
           * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
           * to implement supply mechanisms].
           *
           * We have followed general OpenZeppelin guidelines: functions revert instead
           * of returning `false` on failure. This behavior is nonetheless conventional
           * and does not conflict with the expectations of ERC20 applications.
           *
           * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
           * This allows applications to reconstruct the allowance for all accounts just
           * by listening to said events. Other implementations of the EIP may not emit
           * these events, as it isn't required by the specification.
           *
           * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
           * functions have been added to mitigate the well-known issues around setting
           * allowances. See {IERC20-approve}.
           */
          contract ERC20 is Context, IERC20 {
              using SafeMath for uint256;
              using Address for address;
              mapping (address => uint256) private _balances;
              mapping (address => mapping (address => uint256)) private _allowances;
              uint256 private _totalSupply;
              string internal _name;
              string internal _symbol;
              uint8 private _decimals;
              /**
               * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
               * a default value of 18.
               *
               * To select a different value for {decimals}, use {_setupDecimals}.
               *
               * All three of these values are immutable: they can only be set once during
               * construction.
               */
              constructor (string memory name, string memory symbol) public {
                  _name = name;
                  _symbol = symbol;
                  _decimals = 18;
              }
              /**
               * @dev Returns the name of the token.
               */
              function name() public view returns (string memory) {
                  return _name;
              }
              /**
               * @dev Returns the symbol of the token, usually a shorter version of the
               * name.
               */
              function symbol() public view returns (string memory) {
                  return _symbol;
              }
              /**
               * @dev Returns the number of decimals used to get its user representation.
               * For example, if `decimals` equals `2`, a balance of `505` tokens should
               * be displayed to a user as `5,05` (`505 / 10 ** 2`).
               *
               * Tokens usually opt for a value of 18, imitating the relationship between
               * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
               * called.
               *
               * NOTE: This information is only used for _display_ purposes: it in
               * no way affects any of the arithmetic of the contract, including
               * {IERC20-balanceOf} and {IERC20-transfer}.
               */
              function decimals() public view returns (uint8) {
                  return _decimals;
              }
              /**
               * @dev See {IERC20-totalSupply}.
               */
              function totalSupply() public view override returns (uint256) {
                  return _totalSupply;
              }
              /**
               * @dev See {IERC20-balanceOf}.
               */
              function balanceOf(address account) public view override returns (uint256) {
                  return _balances[account];
              }
              /**
               * @dev See {IERC20-transfer}.
               *
               * Requirements:
               *
               * - `recipient` cannot be the zero address.
               * - the caller must have a balance of at least `amount`.
               */
              function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                  _transfer(_msgSender(), recipient, amount);
                  return true;
              }
              /**
               * @dev See {IERC20-allowance}.
               */
              function allowance(address owner, address spender) public view virtual override returns (uint256) {
                  return _allowances[owner][spender];
              }
              /**
               * @dev See {IERC20-approve}.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               */
              function approve(address spender, uint256 amount) public virtual override returns (bool) {
                  _approve(_msgSender(), spender, amount);
                  return true;
              }
              /**
               * @dev See {IERC20-transferFrom}.
               *
               * Emits an {Approval} event indicating the updated allowance. This is not
               * required by the EIP. See the note at the beginning of {ERC20};
               *
               * Requirements:
               * - `sender` and `recipient` cannot be the zero address.
               * - `sender` must have a balance of at least `amount`.
               * - the caller must have allowance for ``sender``'s tokens of at least
               * `amount`.
               */
              function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
                  _transfer(sender, recipient, amount);
                  _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                  return true;
              }
              /**
               * @dev Atomically increases the allowance granted to `spender` by the caller.
               *
               * This is an alternative to {approve} that can be used as a mitigation for
               * problems described in {IERC20-approve}.
               *
               * Emits an {Approval} event indicating the updated allowance.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               */
              function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                  return true;
              }
              /**
               * @dev Atomically decreases the allowance granted to `spender` by the caller.
               *
               * This is an alternative to {approve} that can be used as a mitigation for
               * problems described in {IERC20-approve}.
               *
               * Emits an {Approval} event indicating the updated allowance.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               * - `spender` must have allowance for the caller of at least
               * `subtractedValue`.
               */
              function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                  return true;
              }
              /**
               * @dev Moves tokens `amount` from `sender` to `recipient`.
               *
               * This is internal function is equivalent to {transfer}, and can be used to
               * e.g. implement automatic token fees, slashing mechanisms, etc.
               *
               * Emits a {Transfer} event.
               *
               * Requirements:
               *
               * - `sender` cannot be the zero address.
               * - `recipient` cannot be the zero address.
               * - `sender` must have a balance of at least `amount`.
               */
              function _transfer(address sender, address recipient, uint256 amount) internal virtual {
                  require(sender != address(0), "ERC20: transfer from the zero address");
                  require(recipient != address(0), "ERC20: transfer to the zero address");
                  _beforeTokenTransfer(sender, recipient, amount);
                  _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                  _balances[recipient] = _balances[recipient].add(amount);
                  emit Transfer(sender, recipient, amount);
              }
              /** @dev Creates `amount` tokens and assigns them to `account`, increasing
               * the total supply.
               *
               * Emits a {Transfer} event with `from` set to the zero address.
               *
               * Requirements
               *
               * - `to` cannot be the zero address.
               */
              function _mint(address account, uint256 amount) internal virtual {
                  require(account != address(0), "ERC20: mint to the zero address");
                  _beforeTokenTransfer(address(0), account, amount);
                  _totalSupply = _totalSupply.add(amount);
                  _balances[account] = _balances[account].add(amount);
                  emit Transfer(address(0), account, amount);
              }
              /**
               * @dev Destroys `amount` tokens from `account`, reducing the
               * total supply.
               *
               * Emits a {Transfer} event with `to` set to the zero address.
               *
               * Requirements
               *
               * - `account` cannot be the zero address.
               * - `account` must have at least `amount` tokens.
               */
              function _burn(address account, uint256 amount) internal virtual {
                  require(account != address(0), "ERC20: burn from the zero address");
                  _beforeTokenTransfer(account, address(0), amount);
                  _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                  _totalSupply = _totalSupply.sub(amount);
                  emit Transfer(account, address(0), amount);
              }
              /**
               * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
               *
               * This is internal function is equivalent to `approve`, and can be used to
               * e.g. set automatic allowances for certain subsystems, etc.
               *
               * Emits an {Approval} event.
               *
               * Requirements:
               *
               * - `owner` cannot be the zero address.
               * - `spender` cannot be the zero address.
               */
              function _approve(address owner, address spender, uint256 amount) internal virtual {
                  require(owner != address(0), "ERC20: approve from the zero address");
                  require(spender != address(0), "ERC20: approve to the zero address");
                  _allowances[owner][spender] = amount;
                  emit Approval(owner, spender, amount);
              }
              /**
               * @dev Sets {decimals} to a value other than the default one of 18.
               *
               * WARNING: This function should only be called from the constructor. Most
               * applications that interact with token contracts will not expect
               * {decimals} to ever change, and may work incorrectly if it does.
               */
              function _setupDecimals(uint8 decimals_) internal {
                  _decimals = decimals_;
              }
              /**
               * @dev Hook that is called before any transfer of tokens. This includes
               * minting and burning.
               *
               * Calling conditions:
               *
               * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * will be to transferred to `to`.
               * - when `from` is zero, `amount` tokens will be minted for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
               * - `from` and `to` are never both zero.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
          }pragma solidity ^0.6.0;
          /**
           * @dev Wrappers over Solidity's arithmetic operations with added overflow
           * checks.
           *
           * Arithmetic operations in Solidity wrap on overflow. This can easily result
           * in bugs, because programmers usually assume that an overflow raises an
           * error, which is the standard behavior in high level programming languages.
           * `SafeMath` restores this intuition by reverting the transaction when 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.
           */
          library SafeMath {
              /**
               * @dev Returns the addition of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `+` operator.
               *
               * Requirements:
               * - Addition cannot overflow.
               */
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a, "SafeMath: addition overflow");
                  return c;
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  return sub(a, b, "SafeMath: subtraction overflow");
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b <= a, errorMessage);
                  uint256 c = a - b;
                  return c;
              }
              /**
               * @dev Returns the multiplication of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `*` operator.
               *
               * Requirements:
               * - Multiplication cannot overflow.
               */
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) {
                      return 0;
                  }
                  uint256 c = a * b;
                  require(c / a == b, "SafeMath: multiplication overflow");
                  return c;
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  return div(a, b, "SafeMath: division by zero");
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  // Solidity only automatically asserts when dividing by 0
                  require(b > 0, errorMessage);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  return mod(a, b, "SafeMath: modulo by zero");
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts with custom message when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b != 0, errorMessage);
                  return a % b;
              }
          }// SPDX-License-Identifier: agpl-3.0
          pragma solidity ^0.6.10;
          import "./BaseAdminUpgradeabilityProxy.sol";
          import "./InitializableUpgradeabilityProxy.sol";
          /**
           * @title InitializableAdminUpgradeabilityProxy
           * @dev Extends from BaseAdminUpgradeabilityProxy with an initializer for 
           * initializing the implementation, admin, and init data.
           */
          contract InitializableAdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy {
              /**
             * Contract initializer.
             * @param _logic address of the initial implementation.
             * @param _admin Address of the proxy administrator.
             * @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, address _admin, bytes memory _data) public payable {
                  require(_implementation() == address(0));
                  InitializableUpgradeabilityProxy.initialize(_logic, _data);
                  assert(ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                  _setAdmin(_admin);
              }
              /**
              * @dev Only fall back when the sender is not the admin.
              */
              function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) {
                  BaseAdminUpgradeabilityProxy._willFallback();
              }
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity ^0.6.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.6.10;
          import {ERC20} from "../open-zeppelin/ERC20.sol";
          import {ITransferHook} from "../interfaces/ITransferHook.sol";
          import {VersionedInitializable} from "../utils/VersionedInitializable.sol";
          /**
          * @notice implementation of the AAVE token contract
          * @author Aave
          */
          contract AaveToken is ERC20, VersionedInitializable {
              /// @dev snapshot of a value on a specific block, used for balances
              struct Snapshot {
                  uint128 blockNumber;
                  uint128 value;
              }
              string internal constant NAME = "Aave Token";
              string internal constant SYMBOL = "AAVE";
              uint8 internal constant DECIMALS = 18;
              /// @dev the amount being distributed for the LEND -> AAVE migration
              uint256 internal constant MIGRATION_AMOUNT = 13000000 ether;
              /// @dev the amount being distributed for the PSI and PEI
              uint256 internal constant DISTRIBUTION_AMOUNT = 3000000 ether;
              uint256 public constant REVISION = 1;
              /// @dev owner => next valid nonce to submit with permit()
              mapping (address => uint256) public _nonces;
              mapping (address => mapping (uint256 => Snapshot)) public _snapshots;
              mapping (address => uint256) public _countsSnapshots;
              /// @dev reference to the Aave governance contract to call (if initialized) on _beforeTokenTransfer
              /// !!! IMPORTANT The Aave governance is considered a trustable contract, being its responsibility
              /// to control all potential reentrancies by calling back the AaveToken
              ITransferHook public _aaveGovernance;
              bytes32 public DOMAIN_SEPARATOR;
              bytes public constant EIP712_REVISION = bytes("1");
              bytes32 internal constant EIP712_DOMAIN = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
              bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
              event SnapshotDone(address owner, uint128 oldValue, uint128 newValue);
              constructor() ERC20(NAME, SYMBOL) public {}
              /**
              * @dev initializes the contract upon assignment to the InitializableAdminUpgradeabilityProxy
              * @param migrator the address of the LEND -> AAVE migration contract
              * @param distributor the address of the AAVE distribution contract
              */
              function initialize(
                  address migrator,
                  address distributor,
                  ITransferHook aaveGovernance
              ) external initializer {
                  uint256 chainId;
                  //solium-disable-next-line
                  assembly {
                      chainId := chainid()
                  }
                  DOMAIN_SEPARATOR = keccak256(abi.encode(
                      EIP712_DOMAIN,
                      keccak256(bytes(NAME)),
                      keccak256(EIP712_REVISION),
                      chainId,
                      address(this)
                  ));
                  _name = NAME;
                  _symbol = SYMBOL;
                  _setupDecimals(DECIMALS);
                  _aaveGovernance = aaveGovernance;
                  _mint(migrator, MIGRATION_AMOUNT);
                  _mint(distributor, DISTRIBUTION_AMOUNT);
              }
              /**
              * @dev implements the permit function as for https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
              * @param owner the owner of the funds
              * @param spender the spender
              * @param value the amount
              * @param deadline the deadline timestamp, type(uint256).max for no deadline
              * @param v signature param
              * @param s signature param
              * @param r signature param
              */
              function permit(
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) external {
                  require(owner != address(0), "INVALID_OWNER");
                  //solium-disable-next-line
                  require(block.timestamp <= deadline, "INVALID_EXPIRATION");
                  uint256 currentValidNonce = _nonces[owner];
                  bytes32 digest = keccak256(
                          abi.encodePacked(
                              "\\x19\\x01",
                              DOMAIN_SEPARATOR,
                              keccak256(
                                  abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
                              )
                  );
                  require(owner == ecrecover(digest, v, r, s), "INVALID_SIGNATURE");
                  _nonces[owner] = currentValidNonce.add(1);
                  _approve(owner, spender, value);
              }
              /**
              * @dev returns the revision of the implementation contract
              */
              function getRevision() internal pure override returns (uint256) {
                  return REVISION;
              }
              /**
              * @dev Writes a snapshot for an owner of tokens
              * @param owner The owner of the tokens
              * @param oldValue The value before the operation that is gonna be executed after the snapshot
              * @param newValue The value after the operation
              */
              function _writeSnapshot(address owner, uint128 oldValue, uint128 newValue) internal {
                  uint128 currentBlock = uint128(block.number);
                  uint256 ownerCountOfSnapshots = _countsSnapshots[owner];
                  mapping (uint256 => Snapshot) storage snapshotsOwner = _snapshots[owner];
                  // Doing multiple operations in the same block
                  if (ownerCountOfSnapshots != 0 && snapshotsOwner[ownerCountOfSnapshots.sub(1)].blockNumber == currentBlock) {
                      snapshotsOwner[ownerCountOfSnapshots.sub(1)].value = newValue;
                  } else {
                      snapshotsOwner[ownerCountOfSnapshots] = Snapshot(currentBlock, newValue);
                      _countsSnapshots[owner] = ownerCountOfSnapshots.add(1);
                  }
                  emit SnapshotDone(owner, oldValue, newValue);
              }
              /**
              * @dev Writes a snapshot before any operation involving transfer of value: _transfer, _mint and _burn
              * - On _transfer, it writes snapshots for both "from" and "to"
              * - On _mint, only for _to
              * - On _burn, only for _from
              * @param from the from address
              * @param to the to address
              * @param amount the amount to transfer
              */
              function _beforeTokenTransfer(address from, address to, uint256 amount) internal override {
                  if (from == to) {
                      return;
                  }
                  if (from != address(0)) {
                      uint256 fromBalance = balanceOf(from);
                      _writeSnapshot(from, uint128(fromBalance), uint128(fromBalance.sub(amount)));
                  }
                  if (to != address(0)) {
                      uint256 toBalance = balanceOf(to);
                      _writeSnapshot(to, uint128(toBalance), uint128(toBalance.add(amount)));
                  }
                  // caching the aave governance address to avoid multiple state loads
                  ITransferHook aaveGovernance = _aaveGovernance;
                  if (aaveGovernance != ITransferHook(0)) {
                      aaveGovernance.onTransfer(from, to, amount);
                  }
              }
          }// SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.10;
          /**
           * @title VersionedInitializable
           *
           * @dev Helper contract to support initializer functions. To use it, replace
           * the constructor with a function that has the `initializer` modifier.
           * 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.
           *
           * @author Aave, inspired by the OpenZeppelin Initializable contract
           */
          abstract contract VersionedInitializable {
              /**
             * @dev Indicates that the contract has been initialized.
             */
              uint256 internal lastInitializedRevision = 0;
             /**
             * @dev Modifier to use in the initializer function of a contract.
             */
              modifier initializer() {
                  uint256 revision = getRevision();
                  require(revision > lastInitializedRevision, "Contract instance has already been initialized");
                  lastInitializedRevision = revision;
                  _;
              }
              /// @dev returns the revision number of the contract.
              /// Needs to be defined in the inherited class as a constant.
              function getRevision() internal pure virtual returns(uint256);
              // Reserved storage space to allow for layout changes in the future.
              uint256[50] private ______gap;
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.10;
          import {IERC20} from "../interfaces/IERC20.sol";
          import {SafeMath} from "../open-zeppelin/SafeMath.sol";
          import {VersionedInitializable} from "../utils/VersionedInitializable.sol";
          /**
          * @title LendToAaveMigrator
          * @notice This contract implements the migration from LEND to AAVE token
          * @author Aave 
          */
          contract LendToAaveMigrator is VersionedInitializable {
              using SafeMath for uint256;
              IERC20 public immutable AAVE;
              IERC20 public immutable LEND;
              uint256 public immutable LEND_AAVE_RATIO;
              uint256 public constant REVISION = 1;
              
              uint256 public _totalLendMigrated;
              /**
              * @dev emitted on migration
              * @param sender the caller of the migration
              * @param amount the amount being migrated
              */
              event LendMigrated(address indexed sender, uint256 indexed amount);
              /**
              * @param aave the address of the AAVE token
              * @param lend the address of the LEND token
              * @param lendAaveRatio the exchange rate between LEND and AAVE 
               */
              constructor(IERC20 aave, IERC20 lend, uint256 lendAaveRatio) public {
                  AAVE = aave;
                  LEND = lend;
                  LEND_AAVE_RATIO = lendAaveRatio;
              }
              /**
              * @dev initializes the implementation
              */
              function initialize() public initializer {
              }
              /**
              * @dev returns true if the migration started
              */
              function migrationStarted() external view returns(bool) {
                  return lastInitializedRevision != 0;
              }
              /**
              * @dev executes the migration from LEND to AAVE. Users need to give allowance to this contract to transfer LEND before executing
              * this transaction.
              * @param amount the amount of LEND to be migrated
              */
              function migrateFromLEND(uint256 amount) external {
                  require(lastInitializedRevision != 0, "MIGRATION_NOT_STARTED");
                  _totalLendMigrated = _totalLendMigrated.add(amount);
                  LEND.transferFrom(msg.sender, address(this), amount);
                  AAVE.transfer(msg.sender, amount.div(LEND_AAVE_RATIO));
                  emit LendMigrated(msg.sender, amount);
              }
              /**
              * @dev returns the implementation revision
              * @return the implementation revision
              */
              function getRevision() internal pure override returns (uint256) {
                  return REVISION;
              }
          }// SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.10;
          import "../interfaces/IERC20.sol";
          contract DoubleTransferHelper {
              IERC20 public immutable AAVE;
              constructor(IERC20 aave) public {
                  AAVE = aave;
              }
              function doubleSend(address to, uint256 amount1, uint256 amount2) external {
                  AAVE.transfer(to, amount1);
                  AAVE.transfer(to, amount2);
              }
          }// SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.10;
          import "../open-zeppelin/ERC20.sol";
          /**
           * @title ERC20Mintable
           * @dev ERC20 minting logic
           */
          contract MintableErc20 is ERC20 {
              constructor(string memory name, string memory symbol, uint8 decimals) ERC20(name, symbol) public {
                  _setupDecimals(decimals);
              }
              /**
               * @dev Function to mint tokens
               * @param value The amount of tokens to mint.
               * @return A boolean that indicates if the operation was successful.
               */
              function mint(uint256 value) public returns (bool) {
                  _mint(msg.sender, value);
                  return true;
              }
          }
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.6.10;
          import {ITransferHook} from "../interfaces/ITransferHook.sol";
          contract MockTransferHook is ITransferHook {
              event MockHookEvent();
              function onTransfer(address from, address to, uint256 amount) external override {
                  emit MockHookEvent();
              }
          }

          File 6 of 6: AaveTokenV2
          // SPDX-License-Identifier: agpl-3.0
          pragma solidity 0.7.5;
          pragma experimental ABIEncoderV2;
          
          
          interface IGovernancePowerDelegationToken {
            
            enum DelegationType {VOTING_POWER, PROPOSITION_POWER}
          
            /**
             * @dev emitted when a user delegates to another
             * @param delegator the delegator
             * @param delegatee the delegatee
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            event DelegateChanged(
              address indexed delegator,
              address indexed delegatee,
              DelegationType delegationType
            );
          
            /**
             * @dev emitted when an action changes the delegated power of a user
             * @param user the user which delegated power has changed
             * @param amount the amount of delegated power for the user
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            event DelegatedPowerChanged(address indexed user, uint256 amount, DelegationType delegationType);
          
            /**
             * @dev delegates the specific power to a delegatee
             * @param delegatee the user which delegated power has changed
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            function delegateByType(address delegatee, DelegationType delegationType) external virtual;
            /**
             * @dev delegates all the powers to a specific user
             * @param delegatee the user to which the power will be delegated
             **/
            function delegate(address delegatee) external virtual;
            /**
             * @dev returns the delegatee of an user
             * @param delegator the address of the delegator
             **/
            function getDelegateeByType(address delegator, DelegationType delegationType)
              external
              virtual
              view
              returns (address);
          
            /**
             * @dev returns the current delegated power of a user. The current power is the
             * power delegated at the time of the last snapshot
             * @param user the user
             **/
            function getPowerCurrent(address user, DelegationType delegationType)
              external
              virtual
              view
              returns (uint256);
          
            /**
             * @dev returns the delegated power of a user at a certain block
             * @param user the user
             **/
            function getPowerAtBlock(
              address user,
              uint256 blockNumber,
              DelegationType delegationType
            ) external virtual view returns (uint256);
           
            /**
            * @dev returns the total supply at a certain block number
            **/
            function totalSupplyAt(uint256 blockNumber) external virtual view returns (uint256);
          }
          
          /**
           * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
           * Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with GSN meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
            function _msgSender() internal view virtual returns (address payable) {
              return msg.sender;
            }
          
            function _msgData() internal view virtual returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
            }
          }
          
          
          /**
           * @dev Interface of the ERC20 standard as defined in the EIP.
           * From https://github.com/OpenZeppelin/openzeppelin-contracts
           */
          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);
          }
          
          
          
          /**
           * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
           * Wrappers over Solidity's arithmetic operations with added overflow
           * checks.
           *
           * Arithmetic operations in Solidity wrap on overflow. This can easily result
           * in bugs, because programmers usually assume that an overflow raises an
           * error, which is the standard behavior in high level programming languages.
           * `SafeMath` restores this intuition by reverting the transaction when 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.
           */
          library SafeMath {
            /**
             * @dev Returns the addition of two unsigned integers, reverting on
             * overflow.
             *
             * Counterpart to Solidity's `+` operator.
             *
             * Requirements:
             * - Addition cannot overflow.
             */
            function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, 'SafeMath: addition overflow');
          
              return c;
            }
          
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             * - Subtraction cannot overflow.
             */
            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, 'SafeMath: subtraction overflow');
            }
          
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             * - Subtraction cannot overflow.
             */
            function sub(
              uint256 a,
              uint256 b,
              string memory errorMessage
            ) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
          
              return c;
            }
          
            /**
             * @dev Returns the multiplication of two unsigned integers, reverting on
             * overflow.
             *
             * Counterpart to Solidity's `*` operator.
             *
             * Requirements:
             * - Multiplication cannot overflow.
             */
            function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
              if (a == 0) {
                return 0;
              }
          
              uint256 c = a * b;
              require(c / a == b, 'SafeMath: multiplication overflow');
          
              return c;
            }
          
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts on
             * division by zero. The result is rounded towards zero.
             *
             * Counterpart to Solidity's `/` operator. Note: this function uses a
             * `revert` opcode (which leaves remaining gas untouched) while Solidity
             * uses an invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, 'SafeMath: division by zero');
            }
          
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
             * division by zero. The result is rounded towards zero.
             *
             * Counterpart to Solidity's `/` operator. Note: this function uses a
             * `revert` opcode (which leaves remaining gas untouched) while Solidity
             * uses an invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function div(
              uint256 a,
              uint256 b,
              string memory errorMessage
            ) internal pure returns (uint256) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
          
              return c;
            }
          
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts when dividing by zero.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, 'SafeMath: modulo by zero');
            }
          
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts with custom message when dividing by zero.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function mod(
              uint256 a,
              uint256 b,
              string memory errorMessage
            ) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
            }
          }
          
          
          
          /**
           * @dev Collection of functions related to the address type
           * From https://github.com/OpenZeppelin/openzeppelin-contracts
           */
          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');
            }
          }
          
          /**
           * @dev Implementation of the {IERC20} interface.
           *
           * This implementation is agnostic to the way tokens are created. This means
           * that a supply mechanism has to be added in a derived contract using {_mint}.
           * For a generic mechanism see {ERC20PresetMinterPauser}.
           *
           * TIP: For a detailed writeup see our guide
           * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
           * to implement supply mechanisms].
           *
           * We have followed general OpenZeppelin guidelines: functions revert instead
           * of returning `false` on failure. This behavior is nonetheless conventional
           * and does not conflict with the expectations of ERC20 applications.
           *
           * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
           * This allows applications to reconstruct the allowance for all accounts just
           * by listening to said events. Other implementations of the EIP may not emit
           * these events, as it isn't required by the specification.
           *
           * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
           * functions have been added to mitigate the well-known issues around setting
           * allowances. See {IERC20-approve}.
           */
          contract ERC20 is Context, IERC20 {
              using SafeMath for uint256;
              using Address for address;
          
              mapping (address => uint256) private _balances;
          
              mapping (address => mapping (address => uint256)) private _allowances;
          
              uint256 private _totalSupply;
          
              string internal _name;
              string internal _symbol;
              uint8 private _decimals;
          
              /**
               * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
               * a default value of 18.
               *
               * To select a different value for {decimals}, use {_setupDecimals}.
               *
               * All three of these values are immutable: they can only be set once during
               * construction.
               */
              constructor (string memory name, string memory symbol) public {
                  _name = name;
                  _symbol = symbol;
                  _decimals = 18;
              }
          
              /**
               * @dev Returns the name of the token.
               */
              function name() public view returns (string memory) {
                  return _name;
              }
          
              /**
               * @dev Returns the symbol of the token, usually a shorter version of the
               * name.
               */
              function symbol() public view returns (string memory) {
                  return _symbol;
              }
          
              /**
               * @dev Returns the number of decimals used to get its user representation.
               * For example, if `decimals` equals `2`, a balance of `505` tokens should
               * be displayed to a user as `5,05` (`505 / 10 ** 2`).
               *
               * Tokens usually opt for a value of 18, imitating the relationship between
               * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
               * called.
               *
               * NOTE: This information is only used for _display_ purposes: it in
               * no way affects any of the arithmetic of the contract, including
               * {IERC20-balanceOf} and {IERC20-transfer}.
               */
              function decimals() public view returns (uint8) {
                  return _decimals;
              }
          
              /**
               * @dev See {IERC20-totalSupply}.
               */
              function totalSupply() public view override returns (uint256) {
                  return _totalSupply;
              }
          
              /**
               * @dev See {IERC20-balanceOf}.
               */
              function balanceOf(address account) public view override returns (uint256) {
                  return _balances[account];
              }
          
              /**
               * @dev See {IERC20-transfer}.
               *
               * Requirements:
               *
               * - `recipient` cannot be the zero address.
               * - the caller must have a balance of at least `amount`.
               */
              function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                  _transfer(_msgSender(), recipient, amount);
                  return true;
              }
          
              /**
               * @dev See {IERC20-allowance}.
               */
              function allowance(address owner, address spender) public view virtual override returns (uint256) {
                  return _allowances[owner][spender];
              }
          
              /**
               * @dev See {IERC20-approve}.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               */
              function approve(address spender, uint256 amount) public virtual override returns (bool) {
                  _approve(_msgSender(), spender, amount);
                  return true;
              }
          
              /**
               * @dev See {IERC20-transferFrom}.
               *
               * Emits an {Approval} event indicating the updated allowance. This is not
               * required by the EIP. See the note at the beginning of {ERC20};
               *
               * Requirements:
               * - `sender` and `recipient` cannot be the zero address.
               * - `sender` must have a balance of at least `amount`.
               * - the caller must have allowance for ``sender``'s tokens of at least
               * `amount`.
               */
              function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
                  _transfer(sender, recipient, amount);
                  _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                  return true;
              }
          
              /**
               * @dev Atomically increases the allowance granted to `spender` by the caller.
               *
               * This is an alternative to {approve} that can be used as a mitigation for
               * problems described in {IERC20-approve}.
               *
               * Emits an {Approval} event indicating the updated allowance.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               */
              function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                  return true;
              }
          
              /**
               * @dev Atomically decreases the allowance granted to `spender` by the caller.
               *
               * This is an alternative to {approve} that can be used as a mitigation for
               * problems described in {IERC20-approve}.
               *
               * Emits an {Approval} event indicating the updated allowance.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               * - `spender` must have allowance for the caller of at least
               * `subtractedValue`.
               */
              function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                  return true;
              }
          
              /**
               * @dev Moves tokens `amount` from `sender` to `recipient`.
               *
               * This is internal function is equivalent to {transfer}, and can be used to
               * e.g. implement automatic token fees, slashing mechanisms, etc.
               *
               * Emits a {Transfer} event.
               *
               * Requirements:
               *
               * - `sender` cannot be the zero address.
               * - `recipient` cannot be the zero address.
               * - `sender` must have a balance of at least `amount`.
               */
              function _transfer(address sender, address recipient, uint256 amount) internal virtual {
                  require(sender != address(0), "ERC20: transfer from the zero address");
                  require(recipient != address(0), "ERC20: transfer to the zero address");
          
                  _beforeTokenTransfer(sender, recipient, amount);
          
                  _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                  _balances[recipient] = _balances[recipient].add(amount);
                  emit Transfer(sender, recipient, amount);
              }
          
              /** @dev Creates `amount` tokens and assigns them to `account`, increasing
               * the total supply.
               *
               * Emits a {Transfer} event with `from` set to the zero address.
               *
               * Requirements
               *
               * - `to` cannot be the zero address.
               */
              function _mint(address account, uint256 amount) internal virtual {
                  require(account != address(0), "ERC20: mint to the zero address");
          
                  _beforeTokenTransfer(address(0), account, amount);
          
                  _totalSupply = _totalSupply.add(amount);
                  _balances[account] = _balances[account].add(amount);
                  emit Transfer(address(0), account, amount);
              }
          
              /**
               * @dev Destroys `amount` tokens from `account`, reducing the
               * total supply.
               *
               * Emits a {Transfer} event with `to` set to the zero address.
               *
               * Requirements
               *
               * - `account` cannot be the zero address.
               * - `account` must have at least `amount` tokens.
               */
              function _burn(address account, uint256 amount) internal virtual {
                  require(account != address(0), "ERC20: burn from the zero address");
          
                  _beforeTokenTransfer(account, address(0), amount);
          
                  _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                  _totalSupply = _totalSupply.sub(amount);
                  emit Transfer(account, address(0), amount);
              }
          
              /**
               * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
               *
               * This is internal function is equivalent to `approve`, and can be used to
               * e.g. set automatic allowances for certain subsystems, etc.
               *
               * Emits an {Approval} event.
               *
               * Requirements:
               *
               * - `owner` cannot be the zero address.
               * - `spender` cannot be the zero address.
               */
              function _approve(address owner, address spender, uint256 amount) internal virtual {
                  require(owner != address(0), "ERC20: approve from the zero address");
                  require(spender != address(0), "ERC20: approve to the zero address");
          
                  _allowances[owner][spender] = amount;
                  emit Approval(owner, spender, amount);
              }
          
              /**
               * @dev Sets {decimals} to a value other than the default one of 18.
               *
               * WARNING: This function should only be called from the constructor. Most
               * applications that interact with token contracts will not expect
               * {decimals} to ever change, and may work incorrectly if it does.
               */
              function _setupDecimals(uint8 decimals_) internal {
                  _decimals = decimals_;
              }
          
              /**
               * @dev Hook that is called before any transfer of tokens. This includes
               * minting and burning.
               *
               * Calling conditions:
               *
               * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * will be to transferred to `to`.
               * - when `from` is zero, `amount` tokens will be minted for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
               * - `from` and `to` are never both zero.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
          }
          
          interface ITransferHook {
            function onTransfer(
              address from,
              address to,
              uint256 amount
            ) external;
          }
          
          
          /**
           * @title SafeERC20
           * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
           * Wrappers around ERC20 operations that throw on failure (when the token
           * contract returns false). Tokens that return no value (and instead revert or
           * throw on failure) are also supported, non-reverting calls are assumed to be
           * successful.
           * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
           * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
           */
          library SafeERC20 {
            using SafeMath for uint256;
            using Address for address;
          
            function safeTransfer(
              IERC20 token,
              address to,
              uint256 value
            ) internal {
              callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
            }
          
            function safeTransferFrom(
              IERC20 token,
              address from,
              address to,
              uint256 value
            ) internal {
              callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
            }
          
            function safeApprove(
              IERC20 token,
              address spender,
              uint256 value
            ) internal {
              require(
                (value == 0) || (token.allowance(address(this), spender) == 0),
                'SafeERC20: approve from non-zero to non-zero allowance'
              );
              callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
            }
          
            function callOptionalReturn(IERC20 token, bytes memory data) private {
              require(address(token).isContract(), 'SafeERC20: call to non-contract');
          
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = address(token).call(data);
              require(success, 'SafeERC20: low-level call failed');
          
              if (returndata.length > 0) {
                // Return data is optional
                // solhint-disable-next-line max-line-length
                require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed');
              }
            }
          }
          
          /**
           * @title VersionedInitializable
           *
           * @dev Helper contract to support initializer functions. To use it, replace
           * the constructor with a function that has the `initializer` modifier.
           * 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.
           *
           * @author Aave, inspired by the OpenZeppelin Initializable contract
           */
          abstract contract VersionedInitializable {
            /**
             * @dev Indicates that the contract has been initialized.
             */
            uint256 internal lastInitializedRevision = 0;
          
            /**
             * @dev Modifier to use in the initializer function of a contract.
             */
            modifier initializer() {
              uint256 revision = getRevision();
              require(revision > lastInitializedRevision, 'Contract instance has already been initialized');
          
              lastInitializedRevision = revision;
          
              _;
            }
          
            /// @dev returns the revision number of the contract.
            /// Needs to be defined in the inherited class as a constant.
            function getRevision() internal pure virtual returns (uint256);
          
            // Reserved storage space to allow for layout changes in the future.
            uint256[50] private ______gap;
          }
          
          
          
          
          /**
           * @notice implementation of the AAVE token contract
           * @author Aave
           */
          abstract contract GovernancePowerDelegationERC20 is ERC20, IGovernancePowerDelegationToken {
            using SafeMath for uint256;
            /// @notice The EIP-712 typehash for the delegation struct used by the contract
            bytes32 public constant DELEGATE_BY_TYPE_TYPEHASH = keccak256(
              'DelegateByType(address delegatee,uint256 type,uint256 nonce,uint256 expiry)'
            );
          
            bytes32 public constant DELEGATE_TYPEHASH = keccak256(
              'Delegate(address delegatee,uint256 nonce,uint256 expiry)'
            );
          
            /// @dev snapshot of a value on a specific block, used for votes
            struct Snapshot {
              uint128 blockNumber;
              uint128 value;
            }
          
            /**
             * @dev delegates one specific power to a delegatee
             * @param delegatee the user which delegated power has changed
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            function delegateByType(address delegatee, DelegationType delegationType) external override {
              _delegateByType(msg.sender, delegatee, delegationType);
            }
          
            /**
             * @dev delegates all the powers to a specific user
             * @param delegatee the user to which the power will be delegated
             **/
            function delegate(address delegatee) external override {
              _delegateByType(msg.sender, delegatee, DelegationType.VOTING_POWER);
              _delegateByType(msg.sender, delegatee, DelegationType.PROPOSITION_POWER);
            }
          
            /**
             * @dev returns the delegatee of an user
             * @param delegator the address of the delegator
             **/
            function getDelegateeByType(address delegator, DelegationType delegationType)
              external
              override
              view
              returns (address)
            {
              (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType);
          
              return _getDelegatee(delegator, delegates);
            }
          
            /**
             * @dev returns the current delegated power of a user. The current power is the
             * power delegated at the time of the last snapshot
             * @param user the user
             **/
            function getPowerCurrent(address user, DelegationType delegationType)
              external
              override
              view
              returns (uint256)
            {
              (
                mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
                mapping(address => uint256) storage snapshotsCounts,
          
              ) = _getDelegationDataByType(delegationType);
          
              return _searchByBlockNumber(snapshots, snapshotsCounts, user, block.number);
            }
          
            /**
             * @dev returns the delegated power of a user at a certain block
             * @param user the user
             **/
            function getPowerAtBlock(
              address user,
              uint256 blockNumber,
              DelegationType delegationType
            ) external override view returns (uint256) {
              (
                mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
                mapping(address => uint256) storage snapshotsCounts,
          
              ) = _getDelegationDataByType(delegationType);
          
              return _searchByBlockNumber(snapshots, snapshotsCounts, user, blockNumber);
            }
          
            /**
             * @dev returns the total supply at a certain block number
             * used by the voting strategy contracts to calculate the total votes needed for threshold/quorum
             * In this initial implementation with no AAVE minting, simply returns the current supply
             * A snapshots mapping will need to be added in case a mint function is added to the AAVE token in the future
             **/
            function totalSupplyAt(uint256 blockNumber) external override view returns (uint256) {
              return super.totalSupply();
            }
          
            /**
             * @dev delegates the specific power to a delegatee
             * @param delegatee the user which delegated power has changed
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            function _delegateByType(
              address delegator,
              address delegatee,
              DelegationType delegationType
            ) internal {
              require(delegatee != address(0), 'INVALID_DELEGATEE');
          
              (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType);
          
              uint256 delegatorBalance = balanceOf(delegator);
          
              address previousDelegatee = _getDelegatee(delegator, delegates);
          
              delegates[delegator] = delegatee;
          
              _moveDelegatesByType(previousDelegatee, delegatee, delegatorBalance, delegationType);
              emit DelegateChanged(delegator, delegatee, delegationType);
            }
          
            /**
             * @dev moves delegated power from one user to another
             * @param from the user from which delegated power is moved
             * @param to the user that will receive the delegated power
             * @param amount the amount of delegated power to be moved
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             **/
            function _moveDelegatesByType(
              address from,
              address to,
              uint256 amount,
              DelegationType delegationType
            ) internal {
              if (from == to) {
                return;
              }
          
              (
                mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
                mapping(address => uint256) storage snapshotsCounts,
          
              ) = _getDelegationDataByType(delegationType);
          
              if (from != address(0)) {
                uint256 previous = 0;
                uint256 fromSnapshotsCount = snapshotsCounts[from];
          
                if (fromSnapshotsCount != 0) {
                  previous = snapshots[from][fromSnapshotsCount - 1].value;
                } else {
                  previous = balanceOf(from);
                }
          
                _writeSnapshot(
                  snapshots,
                  snapshotsCounts,
                  from,
                  uint128(previous),
                  uint128(previous.sub(amount))
                );
          
                emit DelegatedPowerChanged(from, previous.sub(amount), delegationType);
              }
              if (to != address(0)) {
                uint256 previous = 0;
                uint256 toSnapshotsCount = snapshotsCounts[to];
                if (toSnapshotsCount != 0) {
                  previous = snapshots[to][toSnapshotsCount - 1].value;
                } else {
                  previous = balanceOf(to);
                }
          
                _writeSnapshot(
                  snapshots,
                  snapshotsCounts,
                  to,
                  uint128(previous),
                  uint128(previous.add(amount))
                );
          
                emit DelegatedPowerChanged(to, previous.add(amount), delegationType);
              }
            }
          
            /**
             * @dev searches a snapshot by block number. Uses binary search.
             * @param snapshots the snapshots mapping
             * @param snapshotsCounts the number of snapshots
             * @param user the user for which the snapshot is being searched
             * @param blockNumber the block number being searched
             **/
            function _searchByBlockNumber(
              mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
              mapping(address => uint256) storage snapshotsCounts,
              address user,
              uint256 blockNumber
            ) internal view returns (uint256) {
              require(blockNumber <= block.number, 'INVALID_BLOCK_NUMBER');
          
              uint256 snapshotsCount = snapshotsCounts[user];
          
              if (snapshotsCount == 0) {
                return balanceOf(user);
              }
          
              // First check most recent balance
              if (snapshots[user][snapshotsCount - 1].blockNumber <= blockNumber) {
                return snapshots[user][snapshotsCount - 1].value;
              }
          
              // Next check implicit zero balance
              if (snapshots[user][0].blockNumber > blockNumber) {
                return 0;
              }
          
              uint256 lower = 0;
              uint256 upper = snapshotsCount - 1;
              while (upper > lower) {
                uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
                Snapshot memory snapshot = snapshots[user][center];
                if (snapshot.blockNumber == blockNumber) {
                  return snapshot.value;
                } else if (snapshot.blockNumber < blockNumber) {
                  lower = center;
                } else {
                  upper = center - 1;
                }
              }
              return snapshots[user][lower].value;
            }
          
            /**
             * @dev returns the delegation data (snapshot, snapshotsCount, list of delegates) by delegation type
             * NOTE: Ideal implementation would have mapped this in a struct by delegation type. Unfortunately,
             * the AAVE token and StakeToken already include a mapping for the snapshots, so we require contracts
             * who inherit from this to provide access to the delegation data by overriding this method.
             * @param delegationType the type of delegation
             **/
            function _getDelegationDataByType(DelegationType delegationType)
              internal
              virtual
              view
              returns (
                mapping(address => mapping(uint256 => Snapshot)) storage, //snapshots
                mapping(address => uint256) storage, //snapshots count
                mapping(address => address) storage //delegatees list
              );
          
            /**
             * @dev Writes a snapshot for an owner of tokens
             * @param owner The owner of the tokens
             * @param oldValue The value before the operation that is gonna be executed after the snapshot
             * @param newValue The value after the operation
             */
            function _writeSnapshot(
              mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
              mapping(address => uint256) storage snapshotsCounts,
              address owner,
              uint128 oldValue,
              uint128 newValue
            ) internal {
              uint128 currentBlock = uint128(block.number);
          
              uint256 ownerSnapshotsCount = snapshotsCounts[owner];
              mapping(uint256 => Snapshot) storage snapshotsOwner = snapshots[owner];
          
              // Doing multiple operations in the same block
              if (
                ownerSnapshotsCount != 0 &&
                snapshotsOwner[ownerSnapshotsCount - 1].blockNumber == currentBlock
              ) {
                snapshotsOwner[ownerSnapshotsCount - 1].value = newValue;
              } else {
                snapshotsOwner[ownerSnapshotsCount] = Snapshot(currentBlock, newValue);
                snapshotsCounts[owner] = ownerSnapshotsCount + 1;
              }
            }
          
            /**
             * @dev returns the user delegatee. If a user never performed any delegation,
             * his delegated address will be 0x0. In that case we simply return the user itself
             * @param delegator the address of the user for which return the delegatee
             * @param delegates the array of delegates for a particular type of delegation
             **/
            function _getDelegatee(address delegator, mapping(address => address) storage delegates)
              internal
              view
              returns (address)
            {
              address previousDelegatee = delegates[delegator];
          
              if (previousDelegatee == address(0)) {
                return delegator;
              }
          
              return previousDelegatee;
            }
          }
          
          /**
           * @notice implementation of the AAVE token contract
           * @author Aave
           */
          contract AaveTokenV2 is GovernancePowerDelegationERC20, VersionedInitializable {
            using SafeMath for uint256;
          
            string internal constant NAME = 'Aave Token';
            string internal constant SYMBOL = 'AAVE';
            uint8 internal constant DECIMALS = 18;
          
            uint256 public constant REVISION = 2;
          
            /// @dev owner => next valid nonce to submit with permit()
            mapping(address => uint256) public _nonces;
          
            mapping(address => mapping(uint256 => Snapshot)) public _votingSnapshots;
          
            mapping(address => uint256) public _votingSnapshotsCounts;
          
            /// @dev reference to the Aave governance contract to call (if initialized) on _beforeTokenTransfer
            /// !!! IMPORTANT The Aave governance is considered a trustable contract, being its responsibility
            /// to control all potential reentrancies by calling back the AaveToken
            ITransferHook public _aaveGovernance;
          
            bytes32 public DOMAIN_SEPARATOR;
            bytes public constant EIP712_REVISION = bytes('1');
            bytes32 internal constant EIP712_DOMAIN = keccak256(
              'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
            );
            bytes32 public constant PERMIT_TYPEHASH = keccak256(
              'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)'
            );
          
            mapping(address => address) internal _votingDelegates;
          
            mapping(address => mapping(uint256 => Snapshot)) internal _propositionPowerSnapshots;
            mapping(address => uint256) internal _propositionPowerSnapshotsCounts;
          
            mapping(address => address) internal _propositionPowerDelegates;
          
            constructor() public ERC20(NAME, SYMBOL) {}
          
            /**
             * @dev initializes the contract upon assignment to the InitializableAdminUpgradeabilityProxy
             */
            function initialize() external initializer {}
          
            /**
             * @dev implements the permit function as for https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
             * @param owner the owner of the funds
             * @param spender the spender
             * @param value the amount
             * @param deadline the deadline timestamp, type(uint256).max for no deadline
             * @param v signature param
             * @param s signature param
             * @param r signature param
             */
          
            function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external {
              require(owner != address(0), 'INVALID_OWNER');
              //solium-disable-next-line
              require(block.timestamp <= deadline, 'INVALID_EXPIRATION');
              uint256 currentValidNonce = _nonces[owner];
              bytes32 digest = keccak256(
                abi.encodePacked(
                  '\x19\x01',
                  DOMAIN_SEPARATOR,
                  keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
                )
              );
          
              require(owner == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE');
              _nonces[owner] = currentValidNonce.add(1);
              _approve(owner, spender, value);
            }
          
            /**
             * @dev returns the revision of the implementation contract
             */
            function getRevision() internal override pure returns (uint256) {
              return REVISION;
            }
          
            /**
             * @dev Writes a snapshot before any operation involving transfer of value: _transfer, _mint and _burn
             * - On _transfer, it writes snapshots for both "from" and "to"
             * - On _mint, only for _to
             * - On _burn, only for _from
             * @param from the from address
             * @param to the to address
             * @param amount the amount to transfer
             */
            function _beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
            ) internal override {
              address votingFromDelegatee = _getDelegatee(from, _votingDelegates);
              address votingToDelegatee = _getDelegatee(to, _votingDelegates);
          
              _moveDelegatesByType(
                votingFromDelegatee,
                votingToDelegatee,
                amount,
                DelegationType.VOTING_POWER
              );
          
              address propPowerFromDelegatee = _getDelegatee(from, _propositionPowerDelegates);
              address propPowerToDelegatee = _getDelegatee(to, _propositionPowerDelegates);
          
              _moveDelegatesByType(
                propPowerFromDelegatee,
                propPowerToDelegatee,
                amount,
                DelegationType.PROPOSITION_POWER
              );
          
              // caching the aave governance address to avoid multiple state loads
              ITransferHook aaveGovernance = _aaveGovernance;
              if (aaveGovernance != ITransferHook(0)) {
                aaveGovernance.onTransfer(from, to, amount);
              }
            }
          
            function _getDelegationDataByType(DelegationType delegationType)
              internal
              override
              view
              returns (
                mapping(address => mapping(uint256 => Snapshot)) storage, //snapshots
                mapping(address => uint256) storage, //snapshots count
                mapping(address => address) storage //delegatees list
              )
            {
              if (delegationType == DelegationType.VOTING_POWER) {
                return (_votingSnapshots, _votingSnapshotsCounts, _votingDelegates);
              } else {
                return (
                  _propositionPowerSnapshots,
                  _propositionPowerSnapshotsCounts,
                  _propositionPowerDelegates
                );
              }
            }
          
            /**
             * @dev Delegates power from signatory to `delegatee`
             * @param delegatee The address to delegate votes to
             * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
             * @param nonce The contract state required to match the signature
             * @param expiry The time at which to expire the signature
             * @param v The recovery byte of the signature
             * @param r Half of the ECDSA signature pair
             * @param s Half of the ECDSA signature pair
             */
            function delegateByTypeBySig(
              address delegatee,
              DelegationType delegationType,
              uint256 nonce,
              uint256 expiry,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) public {
              bytes32 structHash = keccak256(
                abi.encode(DELEGATE_BY_TYPE_TYPEHASH, delegatee, uint256(delegationType), nonce, expiry)
              );
              bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash));
              address signatory = ecrecover(digest, v, r, s);
              require(signatory != address(0), 'INVALID_SIGNATURE');
              require(nonce == _nonces[signatory]++, 'INVALID_NONCE');
              require(block.timestamp <= expiry, 'INVALID_EXPIRATION');
              _delegateByType(signatory, delegatee, delegationType);
            }
          
            /**
             * @dev Delegates power from signatory to `delegatee`
             * @param delegatee The address to delegate votes to
             * @param nonce The contract state required to match the signature
             * @param expiry The time at which to expire the signature
             * @param v The recovery byte of the signature
             * @param r Half of the ECDSA signature pair
             * @param s Half of the ECDSA signature pair
             */
            function delegateBySig(
              address delegatee,
              uint256 nonce,
              uint256 expiry,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) public {
              bytes32 structHash = keccak256(abi.encode(DELEGATE_TYPEHASH, delegatee, nonce, expiry));
              bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash));
              address signatory = ecrecover(digest, v, r, s);
              require(signatory != address(0), 'INVALID_SIGNATURE');
              require(nonce == _nonces[signatory]++, 'INVALID_NONCE');
              require(block.timestamp <= expiry, 'INVALID_EXPIRATION');
              _delegateByType(signatory, delegatee, DelegationType.VOTING_POWER);
              _delegateByType(signatory, delegatee, DelegationType.PROPOSITION_POWER);
            }
          }