// File: contracts/lending/compound/governance/GovernanceBravoDelegator.sol



pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2;

import "./GovernorBravoInterfaces.sol";

// https://etherscan.io/address/0xc0Da02939E1441F497fd74F78cE7Decb17B66529#code

contract GovernorBravoDelegator is
  GovernorBravoDelegatorStorage,
  GovernorBravoEvents
{
  constructor(
    address timelock_,
    address comp_,
    address admin_,
    address implementation_,
    uint votingPeriod_,
    uint votingDelay_,
    uint proposalThreshold_
  ) public {
    // Admin set to msg.sender for initialization
    admin = msg.sender;

    delegateTo(
      implementation_,
      abi.encodeWithSignature(
        "initialize(address,address,uint256,uint256,uint256)",
        timelock_,
        comp_,
        votingPeriod_,
        votingDelay_,
        proposalThreshold_
      )
    );

    _setImplementation(implementation_);

    admin = admin_;
  }

  /**
   * @notice Called by the admin to update the implementation of the delegator
   * @param implementation_ The address of the new implementation for delegation
   */
  function _setImplementation(address implementation_) public {
    require(
      msg.sender == admin,
      "GovernorBravoDelegator::_setImplementation: admin only"
    );
    require(
      implementation_ != address(0),
      "GovernorBravoDelegator::_setImplementation: invalid implementation address"
    );

    address oldImplementation = implementation;
    implementation = implementation_;

    emit NewImplementation(oldImplementation, implementation);
  }

  /**
   * @notice Internal method to delegate execution to another contract
   * @dev It returns to the external caller whatever the implementation returns or forwards reverts
   * @param callee The contract to delegatecall
   * @param data The raw data to delegatecall
   */
  function delegateTo(address callee, bytes memory data) internal {
    (bool success, bytes memory returnData) = callee.delegatecall(data);
    assembly {
      if eq(success, 0) {
        revert(add(returnData, 0x20), returndatasize)
      }
    }
  }

  /**
   * @dev Delegates execution to an implementation contract.
   * It returns to the external caller whatever the implementation returns
   * or forwards reverts.
   */
  function() external payable {
    // delegate all other functions to current implementation
    (bool success, ) = implementation.delegatecall(msg.data);

    assembly {
      let free_mem_ptr := mload(0x40)
      returndatacopy(free_mem_ptr, 0, returndatasize)

      switch success
      case 0 {
        revert(free_mem_ptr, returndatasize)
      }
      default {
        return(free_mem_ptr, returndatasize)
      }
    }
  }
}

// File: contracts/lending/compound/governance/GovernorBravoInterfaces.sol



pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2;

// https://etherscan.io/address/0xeF3B6E9e13706A8F01fe98fdCf66335dc5CfdEED#code

// TODO: check the difference between this file and the one located here: 0xc0Da02939E1441F497fd74F78cE7Decb17B66529

contract GovernorBravoEvents {
  /// @notice An event emitted when a new proposal is created
  event ProposalCreated(
    uint id,
    address proposer,
    address[] targets,
    uint[] values,
    string[] signatures,
    bytes[] calldatas,
    uint startBlock,
    uint endBlock,
    string description
  );

  /// @notice An event emitted when a vote has been cast on a proposal
  /// @param voter The address which casted a vote
  /// @param proposalId The proposal id which was voted on
  /// @param support Support value for the vote. 0=against, 1=for, 2=abstain
  /// @param votes Number of votes which were cast by the voter
  /// @param reason The reason given for the vote by the voter
  event VoteCast(
    address indexed voter,
    uint proposalId,
    uint8 support,
    uint votes,
    string reason
  );

  /// @notice An event emitted when a proposal has been canceled
  event ProposalCanceled(uint id);

  /// @notice An event emitted when a proposal has been queued in the Timelock
  event ProposalQueued(uint id, uint eta);

  /// @notice An event emitted when a proposal has been executed in the Timelock
  event ProposalExecuted(uint id);

  /// @notice An event emitted when the voting delay is set
  event VotingDelaySet(uint oldVotingDelay, uint newVotingDelay);

  /// @notice An event emitted when the voting period is set
  event VotingPeriodSet(uint oldVotingPeriod, uint newVotingPeriod);

  /// @notice Emitted when implementation is changed
  event NewImplementation(address oldImplementation, address newImplementation);

  /// @notice Emitted when proposal threshold is set
  event ProposalThresholdSet(
    uint oldProposalThreshold,
    uint newProposalThreshold
  );

  /// @notice Emitted when pendingAdmin is changed
  event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);

  /// @notice Emitted when pendingAdmin is accepted, which means admin is updated
  event NewAdmin(address oldAdmin, address newAdmin);

  /// @notice Emitted when whitelist account expiration is set
  event WhitelistAccountExpirationSet(address account, uint expiration);

  /// @notice Emitted when the whitelistGuardian is set
  event WhitelistGuardianSet(address oldGuardian, address newGuardian);
}

contract GovernorBravoDelegatorStorage {
  /// @notice Administrator for this contract
  address public admin;

  /// @notice Pending administrator for this contract
  address public pendingAdmin;

  /// @notice Active brains of Governor
  address public implementation;
}

/**
 * @title Storage for Governor Bravo Delegate
 * @notice For future upgrades, do not change GovernorBravoDelegateStorageV1. Create a new
 * contract which implements GovernorBravoDelegateStorageV1 and following the naming convention
 * GovernorBravoDelegateStorageVX.
 */
contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage {
  /// @notice The delay before voting on a proposal may take place, once proposed, in blocks
  uint public votingDelay;

  /// @notice The duration of voting on a proposal, in blocks
  uint public votingPeriod;

  /// @notice The number of votes required in order for a voter to become a proposer
  uint public proposalThreshold;

  /// @notice Initial proposal id set at become
  uint public initialProposalId;

  /// @notice The total number of proposals
  uint public proposalCount;

  /// @notice The address of the Compound Protocol Timelock
  TimelockInterface public timelock;

  /// @notice The address of the Compound governance token
  CompInterface public comp;

  /// @notice The official record of all proposals ever proposed
  mapping(uint => Proposal) public proposals;

  /// @notice The latest proposal for each proposer
  mapping(address => uint) public latestProposalIds;

  struct Proposal {
    /// @notice Unique id for looking up a proposal
    uint id;
    /// @notice Creator of the proposal
    address proposer;
    /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds
    uint eta;
    /// @notice the ordered list of target addresses for calls to be made
    address[] targets;
    /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made
    uint[] values;
    /// @notice The ordered list of function signatures to be called
    string[] signatures;
    /// @notice The ordered list of calldata to be passed to each call
    bytes[] calldatas;
    /// @notice The block at which voting begins: holders must delegate their votes prior to this block
    uint startBlock;
    /// @notice The block at which voting ends: votes must be cast prior to this block
    uint endBlock;
    /// @notice Current number of votes in favor of this proposal
    uint forVotes;
    /// @notice Current number of votes in opposition to this proposal
    uint againstVotes;
    /// @notice Current number of votes for abstaining for this proposal
    uint abstainVotes;
    /// @notice Flag marking whether the proposal has been canceled
    bool canceled;
    /// @notice Flag marking whether the proposal has been executed
    bool executed;
    /// @notice Receipts of ballots for the entire set of voters
    mapping(address => Receipt) receipts;
  }

  /// @notice Ballot receipt record for a voter
  struct Receipt {
    /// @notice Whether or not a vote has been cast
    bool hasVoted;
    /// @notice Whether or not the voter supports the proposal or abstains
    uint8 support;
    /// @notice The number of votes the voter had, which were cast
    uint96 votes;
  }

  /// @notice Possible states that a proposal may be in
  enum ProposalState {
    Pending,
    Active,
    Canceled,
    Defeated,
    Succeeded,
    Queued,
    Expired,
    Executed
  }
}

contract GovernorBravoDelegateStorageV2 is GovernorBravoDelegateStorageV1 {
  /// @notice Stores the expiration of account whitelist status as a timestamp
  mapping(address => uint) public whitelistAccountExpirations;

  /// @notice Address which manages whitelisted proposals and whitelist accounts
  address public whitelistGuardian;
}

interface TimelockInterface {
  function delay() external view returns (uint);

  function GRACE_PERIOD() external view returns (uint);

  function acceptAdmin() external;

  function queuedTransactions(bytes32 hash) external view returns (bool);

  function queueTransaction(
    address target,
    uint value,
    string calldata signature,
    bytes calldata data,
    uint eta
  ) external returns (bytes32);

  function cancelTransaction(
    address target,
    uint value,
    string calldata signature,
    bytes calldata data,
    uint eta
  ) external;

  function executeTransaction(
    address target,
    uint value,
    string calldata signature,
    bytes calldata data,
    uint eta
  ) external payable returns (bytes memory);
}

interface CompInterface {
  function getPriorVotes(
    address account,
    uint blockNumber
  ) external view returns (uint96);
}

interface GovernorAlpha {
  /// @notice The total number of proposals
  function proposalCount() external returns (uint);
}