contract ProxyRegistry is Ownable {
  /* DelegateProxy implementation contract. Must be initialized. */
  address public delegateProxyImplementation;

  /* Authenticated proxies by user. */
  mapping(address => OwnableDelegateProxy) public proxies;

  /* Contracts pending access. */
  mapping(address => uint256) public pending;

  /* Contracts allowed to call those proxies. */
  mapping(address => bool) public contracts;

  /* Delay period for adding an authenticated contract.
       This mitigates a particular class of potential attack on the Wyvern DAO (which owns this registry) - if at any point the value of assets held by proxy contracts exceeded the value of half the WYV supply (votes in the DAO),
       a malicious but rational attacker could buy half the Wyvern and grant themselves access to all the proxy contracts. A delay period renders this attack nonthreatening - given two weeks, if that happened, users would have
       plenty of time to notice and transfer their assets.
    */
  uint256 public DELAY_PERIOD = 2 weeks;

  /**
   * Start the process to enable access for specified contract. Subject to delay period.
   *
   * @dev ProxyRegistry owner only
   * @param addr Address to which to grant permissions
   */
  function startGrantAuthentication(address addr) public onlyOwner {
    require(!contracts[addr] && pending[addr] == 0);
    pending[addr] = now;
  }

  /**
   * End the process to nable access for specified contract after delay period has passed.
   *
   * @dev ProxyRegistry owner only
   * @param addr Address to which to grant permissions
   */
  function endGrantAuthentication(address addr) public onlyOwner {
    require(!contracts[addr] && pending[addr] != 0 && ((pending[addr] + DELAY_PERIOD) < now));
    pending[addr] = 0;
    contracts[addr] = true;
  }

  /**
   * Revoke access for specified contract. Can be done instantly.
   *
   * @dev ProxyRegistry owner only
   * @param addr Address of which to revoke permissions
   */
  function revokeAuthentication(address addr) public onlyOwner {
    contracts[addr] = false;
  }

  /**
   * Register a proxy contract with this registry
   *
   * @dev Must be called by the user which the proxy is for, creates a new AuthenticatedProxy
   * @return proxy New AuthenticatedProxy contract
   */
  function registerProxy() public returns (OwnableDelegateProxy proxy) {
    require(address(proxies[msg.sender]) == address(0));
    proxy = new OwnableDelegateProxy(
      msg.sender,
      delegateProxyImplementation,
      abi.encodeWithSignature("initialize(address,address)", msg.sender, address(this))
    );
    proxies[msg.sender] = proxy;
  }
}
function registerProxy() public returns (OwnableDelegateProxy proxy) {
  require(address(proxies[msg.sender]) == address(0));
  proxy = new OwnableDelegateProxy(
    msg.sender,
    delegateProxyImplementation,
    abi.encodeWithSignature("initialize(address,address)", msg.sender, address(this))
  );
  proxies[msg.sender] = proxy;
}

/**
 * Register a proxy contract with this registry
 *
 * @dev Must be called by the user which the proxy is for, creates a new AuthenticatedProxy
 * @return proxy New AuthenticatedProxy contract
 */