/**
 * SPDX-License-Identifier: MIT
 *
 * Copyright (c) 2018-2020 CENTRE SECZ
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
pragma solidity ^0.6.12;

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());
   }
}


/**
* @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");
   }

   /**
    * @dev Performs a Solidity function call using a low level `call`. A
    * plain`call` is an unsafe replacement for a function call: use this
    * function instead.
    *
    * If `target` reverts with a revert reason, it is bubbled up by this
    * function (like regular Solidity function calls).
    *
    * Returns the raw returned data. To convert to the expected return value,
    * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
    *
    * Requirements:
    *
    * - `target` must be a contract.
    * - calling `target` with `data` must not revert.
    *
    * _Available since v3.1._
    */
   function functionCall(address target, bytes memory data) internal returns (bytes memory) {
     return functionCall(target, data, "Address: low-level call failed");
   }

   /**
    * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
    * `errorMessage` as a fallback revert reason when `target` reverts.
    *
    * _Available since v3.1._
    */
   function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
       return _functionCallWithValue(target, data, 0, errorMessage);
   }

   /**
    * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
    * but also transferring `value` wei to `target`.
    *
    * Requirements:
    *
    * - the calling contract must have an ETH balance of at least `value`.
    * - the called Solidity function must be `payable`.
    *
    * _Available since v3.1._
    */
   function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
       return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
   }

   /**
    * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
    * with `errorMessage` as a fallback revert reason when `target` reverts.
    *
    * _Available since v3.1._
    */
   function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
       require(address(this).balance >= value, "Address: insufficient balance for call");
       return _functionCallWithValue(target, data, value, errorMessage);
   }

   function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
       require(isContract(target), "Address: call to non-contract");

       // solhint-disable-next-line avoid-low-level-calls
       (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
       if (success) {
           return returndata;
       } else {
           // Look for revert reason and bubble it up if present
           if (returndata.length > 0) {
               // The easiest way to bubble the revert reason is using memory via assembly

               // solhint-disable-next-line no-inline-assembly
               assembly {
                   let returndata_size := mload(returndata)
                   revert(add(32, returndata), returndata_size)
               }
           } else {
               revert(errorMessage);
           }
       }
   }
}

/**
* @notice 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.
* @dev Forked from https://github.com/zeppelinos/zos-lib/blob/8a16ef3ad17ec7430e3a9d2b5e3f39b8204f8c8d/contracts/upgradeability/UpgradeabilityProxy.sol
* Modifications:
* 1. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20)
* 2. Use Address utility library from the latest OpenZeppelin (5/13/20)
*/
contract UpgradeabilityProxy is Proxy {
   /**
    * @dev Emitted when the implementation is upgraded.
    * @param implementation Address of the new implementation.
    */
   event Upgraded(address implementation);

   /**
    * @dev Storage slot with the address of the current implementation.
    * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
    * validated in the constructor.
    */
   bytes32
       private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;

   /**
    * @dev Contract constructor.
    * @param implementationContract Address of the initial implementation.
    */
   constructor(address implementationContract) public {
       assert(
           IMPLEMENTATION_SLOT ==
               keccak256("org.zeppelinos.proxy.implementation")
       );

       _setImplementation(implementationContract);
   }

   /**
    * @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) private {
       require(
           Address.isContract(newImplementation),
           "Cannot set a proxy implementation to a non-contract address"
       );

       bytes32 slot = IMPLEMENTATION_SLOT;

       assembly {
           sstore(slot, newImplementation)
       }
   }
}
/**
* @notice This contract combines an upgradeability proxy with an authorization
* mechanism for administrative tasks.
* @dev Forked from https://github.com/zeppelinos/zos-lib/blob/8a16ef3ad17ec7430e3a9d2b5e3f39b8204f8c8d/contracts/upgradeability/AdminUpgradeabilityProxy.sol
* Modifications:
* 1. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20)
* 2. Remove ifAdmin modifier from admin() and implementation() (5/13/20)
*/
contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
   /**
    * @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 "org.zeppelinos.proxy.admin", and is
    * validated in the constructor.
    */
   bytes32
       private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;

   /**
    * @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();
       }
   }

   /**
    * @dev Contract constructor.
    * It sets the `msg.sender` as the proxy administrator.
    * @param implementationContract address of the initial implementation.
    */
   constructor(address implementationContract)
       public
       UpgradeabilityProxy(implementationContract)
   {
       assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));

       _setAdmin(msg.sender);
   }

   /**
    * @return The address of the proxy admin.
    */
   function admin() external view returns (address) {
       return _admin();
   }

   /**
    * @return The address of the implementation.
    */
   function implementation() external view 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/develop/abi-spec.html#function-selector-and-argument-encoding.
    */
   function upgradeToAndCall(address newImplementation, bytes calldata data)
       external
       payable
       ifAdmin
   {
       _upgradeTo(newImplementation);
       // prettier-ignore
       // solhint-disable-next-line avoid-low-level-calls
       (bool success,) = address(this).call{value: msg.value}(data);
       // solhint-disable-next-line reason-string
       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 {
       require(
           msg.sender != _admin(),
           "Cannot call fallback function from the proxy admin"
       );
       super._willFallback();
   }
}

/**
 * @title FiatTokenProxy
 * @dev This contract proxies FiatToken calls and enables FiatToken upgrades
 */
contract FiatTokenProxy is AdminUpgradeabilityProxy {
    constructor(address implementationContract)
        public
        AdminUpgradeabilityProxy(implementationContract)
    {}
}