ETH Price: $3,860.67 (+1.14%)
Gas: 2.91 Gwei

Transaction Decoder

Block:
15308609 at Aug-09-2022 02:45:17 PM +UTC
Transaction Fee:
0.0048124192602292 ETH $18.58
Gas Used:
136,900 Gas / 35.152806868 Gwei

Emitted Events:

215 0x1b3934c6b91a7688019863609dade216211dc6df.0xee503bee2bb6a87e57bc57db795f98137327401a0e7b7ce42e37926cc1a9ca4d( 0xee503bee2bb6a87e57bc57db795f98137327401a0e7b7ce42e37926cc1a9ca4d, 0x000000000000000000000000ab19acf854872651d0ff2fa118dfdcd5fdc6a8d5, 000000000000000000000000000000000000000000000000000000000086b933 )
216 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000001b3934c6b91a7688019863609dade216211dc6df, 0x000000000000000000000000ab19acf854872651d0ff2fa118dfdcd5fdc6a8d5, 000000000000000000000000000000000000000000000000000000000086b933 )
217 0x1b3934c6b91a7688019863609dade216211dc6df.0xa2c38e2d2fb7e3e1912d937fd1ca11ed6d51864dee4cfa7a7bf02becd7acf092( 0xa2c38e2d2fb7e3e1912d937fd1ca11ed6d51864dee4cfa7a7bf02becd7acf092, 0x000000000000000000000000ab19acf854872651d0ff2fa118dfdcd5fdc6a8d5, 0x0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000000000000086b933 )

Account State Difference:

  Address   Before After State Difference Code
0x1B3934c6...6211dC6dF
0xA0b86991...E3606eB48
0xab19acF8...5Fdc6a8d5
0.013152045150967299 Eth
Nonce: 714
0.008339625890738099 Eth
Nonce: 715
0.0048124192602292
(Ethermine)
1,097.256861747809439245 Eth1,097.256998647809439245 Eth0.0001369

Execution Trace

BankOfBurn.CALL( )
  • 0x1b3934c6b91a7688019863609dade216211dc6df.bc4c4b37( )
    • FiatTokenProxy.a9059cbb( )
      • FiatTokenV2_1.transfer( to=0xab19acF854872651D0Ff2FA118DFDcD5Fdc6a8d5, value=8829235 ) => ( True )
        File 1 of 3: BankOfBurn
        /* https://t.me/BankofBurnPortal
           https://twitter.com/bankofburn
        */
        
        // SPDX-License-Identifier: MIT                                                                               
        pragma solidity 0.8.13;
        
        abstract contract Context {
            function _msgSender() internal view virtual returns (address) {
                return msg.sender;
            }
        
            function _msgData() internal view virtual returns (bytes calldata) {
                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                return msg.data;
            }
        }
        
        interface IUniswapV2Factory {
            function createPair(address tokenA, address tokenB) external returns (address pair);
        }
        
        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);
        }
        
        interface IERC20Metadata is IERC20 {
            /**
             * @dev Returns the name of the token.
             */
            function name() external view returns (string memory);
        
            /**
             * @dev Returns the symbol of the token.
             */
            function symbol() external view returns (string memory);
        
            /**
             * @dev Returns the decimals places of the token.
             */
            function decimals() external view returns (uint8);
        }
        
        contract ERC20 is Context, IERC20, IERC20Metadata {
            mapping(address => uint256) private _balances;
        
            mapping(address => mapping(address => uint256)) private _allowances;
        
            uint256 private _totalSupply;
        
            string private _name;
            string private _symbol;
        
            constructor(string memory name_, string memory symbol_) {
                _name = name_;
                _symbol = symbol_;
            }
        
            function name() public view virtual override returns (string memory) {
                return _name;
            }
        
            function symbol() public view virtual override returns (string memory) {
                return _symbol;
            }
        
            function decimals() public view virtual override returns (uint8) {
                return 18;
            }
        
            function totalSupply() public view virtual override returns (uint256) {
                return _totalSupply;
            }
        
            function balanceOf(address account) public view virtual override returns (uint256) {
                return _balances[account];
            }
        
            function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                _transfer(_msgSender(), recipient, amount);
                return true;
            }
        
            function allowance(address owner, address spender) public view virtual override returns (uint256) {
                return _allowances[owner][spender];
            }
        
            function approve(address spender, uint256 amount) public virtual override returns (bool) {
                _approve(_msgSender(), spender, amount);
                return true;
            }
        
            function transferFrom(
                address sender,
                address recipient,
                uint256 amount
            ) public virtual override returns (bool) {
                _transfer(sender, recipient, amount);
        
                uint256 currentAllowance = _allowances[sender][_msgSender()];
                require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
                unchecked {
                    _approve(sender, _msgSender(), currentAllowance - amount);
                }
        
                return true;
            }
        
            function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
                return true;
            }
        
            function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                uint256 currentAllowance = _allowances[_msgSender()][spender];
                require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                unchecked {
                    _approve(_msgSender(), spender, currentAllowance - subtractedValue);
                }
        
                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");
        
                uint256 senderBalance = _balances[sender];
                require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
                unchecked {
                    _balances[sender] = senderBalance - amount;
                }
                _balances[recipient] += amount;
        
                emit Transfer(sender, recipient, amount);
            }
        
            function _createInitialSupply(address account, uint256 amount) internal virtual {
                require(account != address(0), "ERC20: mint to the zero address");
                _totalSupply += amount;
                _balances[account] += amount;
                emit Transfer(address(0), account, 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);
            }
        }
        
        interface DividendPayingTokenOptionalInterface {
          /// @notice View the amount of dividend in wei that an address can withdraw.
          /// @param _owner The address of a token holder.
          /// @return The amount of dividend in wei that `_owner` can withdraw.
          function withdrawableDividendOf(address _owner, address _rewardToken) external view returns(uint256);
        
          /// @notice View the amount of dividend in wei that an address has withdrawn.
          /// @param _owner The address of a token holder.
          /// @return The amount of dividend in wei that `_owner` has withdrawn.
          function withdrawnDividendOf(address _owner, address _rewardToken) external view returns(uint256);
        
          /// @notice View the amount of dividend in wei that an address has earned in total.
          /// @dev accumulativeDividendOf(_owner) = withdrawableDividendOf(_owner) + withdrawnDividendOf(_owner)
          /// @param _owner The address of a token holder.
          /// @return The amount of dividend in wei that `_owner` has earned in total.
          function accumulativeDividendOf(address _owner, address _rewardToken) external view returns(uint256);
        }
        
        interface DividendPayingTokenInterface {
          /// @notice View the amount of dividend in wei that an address can withdraw.
          /// @param _owner The address of a token holder.
          /// @return The amount of dividend in wei that `_owner` can withdraw.
          function dividendOf(address _owner, address _rewardToken) external view returns(uint256);
        
          /// @notice Distributes ether to token holders as dividends.
          /// @dev SHOULD distribute the paid ether to token holders as dividends.
          ///  SHOULD NOT directly transfer ether to token holders in this function.
          ///  MUST emit a `DividendsDistributed` event when the amount of distributed ether is greater than 0.
          function distributeDividends() external payable;
        
          /// @notice Withdraws the ether distributed to the sender.
          /// @dev SHOULD transfer `dividendOf(msg.sender)` wei to `msg.sender`, and `dividendOf(msg.sender)` SHOULD be 0 after the transfer.
          ///  MUST emit a `DividendWithdrawn` event if the amount of ether transferred is greater than 0.
          function withdrawDividend(address _rewardToken) external;
        
          /// @dev This event MUST emit when ether is distributed to token holders.
          /// @param from The address which sends ether to this contract.
          /// @param weiAmount The amount of distributed ether in wei.
          event DividendsDistributed(
            address indexed from,
            uint256 weiAmount
          );
        
          /// @dev This event MUST emit when an address withdraws their dividend.
          /// @param to The address which withdraws ether from this contract.
          /// @param weiAmount The amount of withdrawn ether in wei.
          event DividendWithdrawn(
            address indexed to,
            uint256 weiAmount
          );
        }
        
        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) {
                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;
            }
        }
        
        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;
            }
        }
        
        library SafeMathInt {
            int256 private constant MIN_INT256 = int256(1) << 255;
            int256 private constant MAX_INT256 = ~(int256(1) << 255);
        
            /**
             * @dev Multiplies two int256 variables and fails on overflow.
             */
            function mul(int256 a, int256 b) internal pure returns (int256) {
                int256 c = a * b;
        
                // Detect overflow when multiplying MIN_INT256 with -1
                require(c != MIN_INT256 || (a & MIN_INT256) != (b & MIN_INT256));
                require((b == 0) || (c / b == a));
                return c;
            }
        
            /**
             * @dev Division of two int256 variables and fails on overflow.
             */
            function div(int256 a, int256 b) internal pure returns (int256) {
                // Prevent overflow when dividing MIN_INT256 by -1
                require(b != -1 || a != MIN_INT256);
        
                // Solidity already throws when dividing by 0.
                return a / b;
            }
        
            /**
             * @dev Subtracts two int256 variables and fails on overflow.
             */
            function sub(int256 a, int256 b) internal pure returns (int256) {
                int256 c = a - b;
                require((b >= 0 && c <= a) || (b < 0 && c > a));
                return c;
            }
        
            /**
             * @dev Adds two int256 variables and fails on overflow.
             */
            function add(int256 a, int256 b) internal pure returns (int256) {
                int256 c = a + b;
                require((b >= 0 && c >= a) || (b < 0 && c < a));
                return c;
            }
        
            /**
             * @dev Converts to absolute value, and fails on overflow.
             */
            function abs(int256 a) internal pure returns (int256) {
                require(a != MIN_INT256);
                return a < 0 ? -a : a;
            }
        
        
            function toUint256Safe(int256 a) internal pure returns (uint256) {
                require(a >= 0);
                return uint256(a);
            }
        }
        
        library SafeMathUint {
          function toInt256Safe(uint256 a) internal pure returns (int256) {
            int256 b = int256(a);
            require(b >= 0);
            return b;
          }
        }
        
        interface IUniswapV2Router01 {
            function factory() external pure returns (address);
            function WETH() external pure returns (address);
        
            function addLiquidity(
                address tokenA,
                address tokenB,
                uint amountADesired,
                uint amountBDesired,
                uint amountAMin,
                uint amountBMin,
                address to,
                uint deadline
            ) external returns (uint amountA, uint amountB, uint liquidity);
            function addLiquidityETH(
                address token,
                uint amountTokenDesired,
                uint amountTokenMin,
                uint amountETHMin,
                address to,
                uint deadline
            ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
            function removeLiquidity(
                address tokenA,
                address tokenB,
                uint liquidity,
                uint amountAMin,
                uint amountBMin,
                address to,
                uint deadline
            ) external returns (uint amountA, uint amountB);
            function removeLiquidityETH(
                address token,
                uint liquidity,
                uint amountTokenMin,
                uint amountETHMin,
                address to,
                uint deadline
            ) external returns (uint amountToken, uint amountETH);
            function removeLiquidityWithPermit(
                address tokenA,
                address tokenB,
                uint liquidity,
                uint amountAMin,
                uint amountBMin,
                address to,
                uint deadline,
                bool approveMax, uint8 v, bytes32 r, bytes32 s
            ) external returns (uint amountA, uint amountB);
            function removeLiquidityETHWithPermit(
                address token,
                uint liquidity,
                uint amountTokenMin,
                uint amountETHMin,
                address to,
                uint deadline,
                bool approveMax, uint8 v, bytes32 r, bytes32 s
            ) external returns (uint amountToken, uint amountETH);
            function swapExactTokensForTokens(
                uint amountIn,
                uint amountOutMin,
                address[] calldata path,
                address to,
                uint deadline
            ) external returns (uint[] memory amounts);
            function swapTokensForExactTokens(
                uint amountOut,
                uint amountInMax,
                address[] calldata path,
                address to,
                uint deadline
            ) external returns (uint[] memory amounts);
            function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
                external
                payable
                returns (uint[] memory amounts);
            function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
                external
                returns (uint[] memory amounts);
            function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
                external
                returns (uint[] memory amounts);
            function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
                external
                payable
                returns (uint[] memory amounts);
        
            function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
            function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
            function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
            function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
            function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
        }
        
        interface IUniswapV2Router02 is IUniswapV2Router01 {
            function removeLiquidityETHSupportingFeeOnTransferTokens(
                address token,
                uint liquidity,
                uint amountTokenMin,
                uint amountETHMin,
                address to,
                uint deadline
            ) external returns (uint amountETH);
            function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
                address token,
                uint liquidity,
                uint amountTokenMin,
                uint amountETHMin,
                address to,
                uint deadline,
                bool approveMax, uint8 v, bytes32 r, bytes32 s
            ) external returns (uint amountETH);
        
            function swapExactTokensForTokensSupportingFeeOnTransferTokens(
                uint amountIn,
                uint amountOutMin,
                address[] calldata path,
                address to,
                uint deadline
            ) external;
            function swapExactETHForTokensSupportingFeeOnTransferTokens(
                uint amountOutMin,
                address[] calldata path,
                address to,
                uint deadline
            ) external payable;
            function swapExactTokensForETHSupportingFeeOnTransferTokens(
                uint amountIn,
                uint amountOutMin,
                address[] calldata path,
                address to,
                uint deadline
            ) external;
        }
        
        contract DividendPayingToken is DividendPayingTokenInterface, DividendPayingTokenOptionalInterface, Ownable {
          using SafeMath for uint256;
          using SafeMathUint for uint256;
          using SafeMathInt for int256;
        
          // With `magnitude`, we can properly distribute dividends even if the amount of received ether is small.
          // For more discussion about choosing the value of `magnitude`,
          //  see https://github.com/ethereum/EIPs/issues/1726#issuecomment-472352728
          uint256 constant internal magnitude = 2**128;
        
          mapping(address => uint256) internal magnifiedDividendPerShare;
          address[] public rewardTokens;
          address public nextRewardToken;
          uint256 public rewardTokenCounter;
          
          IUniswapV2Router02 public immutable uniswapV2Router;
          
          
          // About dividendCorrection:
          // If the token balance of a `_user` is never changed, the dividend of `_user` can be computed with:
          //   `dividendOf(_user) = dividendPerShare * balanceOf(_user)`.
          // When `balanceOf(_user)` is changed (via minting/burning/transferring tokens),
          //   `dividendOf(_user)` should not be changed,
          //   but the computed value of `dividendPerShare * balanceOf(_user)` is changed.
          // To keep the `dividendOf(_user)` unchanged, we add a correction term:
          //   `dividendOf(_user) = dividendPerShare * balanceOf(_user) + dividendCorrectionOf(_user)`,
          //   where `dividendCorrectionOf(_user)` is updated whenever `balanceOf(_user)` is changed:
          //   `dividendCorrectionOf(_user) = dividendPerShare * (old balanceOf(_user)) - (new balanceOf(_user))`.
          // So now `dividendOf(_user)` returns the same value before and after `balanceOf(_user)` is changed.
          mapping(address => mapping(address => int256)) internal magnifiedDividendCorrections;
          mapping(address => mapping(address => uint256)) internal withdrawnDividends;
          
          mapping (address => uint256) public holderBalance;
          uint256 public totalBalance;
        
          mapping(address => uint256) public totalDividendsDistributed;
          
          constructor(){
              IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); // router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
              uniswapV2Router = _uniswapV2Router; 
              
              // Mainnet
        
              rewardTokens.push(address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)); // USDC - 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
              
              nextRewardToken = rewardTokens[0];
          }
        
          
        
          /// @dev Distributes dividends whenever ether is paid to this contract.
          receive() external payable {
            distributeDividends();
          }
        
          /// @notice Distributes ether to token holders as dividends.
          /// @dev It reverts if the total supply of tokens is 0.
          /// It emits the `DividendsDistributed` event if the amount of received ether is greater than 0.
          /// About undistributed ether:
          ///   In each distribution, there is a small amount of ether not distributed,
          ///     the magnified amount of which is
          ///     `(msg.value * magnitude) % totalSupply()`.
          ///   With a well-chosen `magnitude`, the amount of undistributed ether
          ///     (de-magnified) in a distribution can be less than 1 wei.
          ///   We can actually keep track of the undistributed ether in a distribution
          ///     and try to distribute it in the next distribution,
          ///     but keeping track of such data on-chain costs much more than
          ///     the saved ether, so we don't do that.
            
          function distributeDividends() public override payable { 
            require(totalBalance > 0);
            uint256 initialBalance = IERC20(nextRewardToken).balanceOf(address(this));
            buyTokens(msg.value, nextRewardToken);
            uint256 newBalance = IERC20(nextRewardToken).balanceOf(address(this)).sub(initialBalance);
            if (newBalance > 0) {
              magnifiedDividendPerShare[nextRewardToken] = magnifiedDividendPerShare[nextRewardToken].add(
                (newBalance).mul(magnitude) / totalBalance
              );
              emit DividendsDistributed(msg.sender, newBalance);
        
              totalDividendsDistributed[nextRewardToken] = totalDividendsDistributed[nextRewardToken].add(newBalance);
            }
            rewardTokenCounter = rewardTokenCounter == rewardTokens.length - 1 ? 0 : rewardTokenCounter + 1;
            nextRewardToken = rewardTokens[rewardTokenCounter];
          }
          
          // useful for buybacks or to reclaim any BNB on the contract in a way that helps holders.
            function buyTokens(uint256 bnbAmountInWei, address rewardToken) internal {
                // generate the uniswap pair path of weth -> eth
                address[] memory path = new address[](2);
                path[0] = uniswapV2Router.WETH();
                path[1] = rewardToken;
        
                // make the swap
                uniswapV2Router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: bnbAmountInWei}(
                    0, // accept any amount of Ethereum
                    path,
                    address(this),
                    block.timestamp
                );
            }
          
          /// @notice Withdraws the ether distributed to the sender.
          /// @dev It emits a `DividendWithdrawn` event if the amount of withdrawn ether is greater than 0.
          function withdrawDividend(address _rewardToken) external virtual override {
            _withdrawDividendOfUser(payable(msg.sender), _rewardToken);
          }
        
          /// @notice Withdraws the ether distributed to the sender.
          /// @dev It emits a `DividendWithdrawn` event if the amount of withdrawn ether is greater than 0.
          function _withdrawDividendOfUser(address payable user, address _rewardToken) internal returns (uint256) {
            uint256 _withdrawableDividend = withdrawableDividendOf(user, _rewardToken);
            if (_withdrawableDividend > 0) {
              withdrawnDividends[user][_rewardToken] = withdrawnDividends[user][_rewardToken].add(_withdrawableDividend);
              emit DividendWithdrawn(user, _withdrawableDividend);
              IERC20(_rewardToken).transfer(user, _withdrawableDividend);
              return _withdrawableDividend;
            }
        
            return 0;
          }
        
        
          /// @notice View the amount of dividend in wei that an address can withdraw.
          /// @param _owner The address of a token holder.
          /// @return The amount of dividend in wei that `_owner` can withdraw.
          function dividendOf(address _owner, address _rewardToken) external view override returns(uint256) {
            return withdrawableDividendOf(_owner, _rewardToken);
          }
        
          /// @notice View the amount of dividend in wei that an address can withdraw.
          /// @param _owner The address of a token holder.
          /// @return The amount of dividend in wei that `_owner` can withdraw.
          function withdrawableDividendOf(address _owner, address _rewardToken) public view override returns(uint256) {
            return accumulativeDividendOf(_owner,_rewardToken).sub(withdrawnDividends[_owner][_rewardToken]);
          }
        
          /// @notice View the amount of dividend in wei that an address has withdrawn.
          /// @param _owner The address of a token holder.
          /// @return The amount of dividend in wei that `_owner` has withdrawn.
          function withdrawnDividendOf(address _owner, address _rewardToken) external view override returns(uint256) {
            return withdrawnDividends[_owner][_rewardToken];
          }
        
        
          /// @notice View the amount of dividend in wei that an address has earned in total.
          /// @dev accumulativeDividendOf(_owner) = withdrawableDividendOf(_owner) + withdrawnDividendOf(_owner)
          /// = (magnifiedDividendPerShare * balanceOf(_owner) + magnifiedDividendCorrections[_owner]) / magnitude
          /// @param _owner The address of a token holder.
          /// @return The amount of dividend in wei that `_owner` has earned in total.
          function accumulativeDividendOf(address _owner, address _rewardToken) public view override returns(uint256) {
            return magnifiedDividendPerShare[_rewardToken].mul(holderBalance[_owner]).toInt256Safe()
              .add(magnifiedDividendCorrections[_rewardToken][_owner]).toUint256Safe() / magnitude;
          }
        
          /// @dev Internal function that increases tokens to an account.
          /// Update magnifiedDividendCorrections to keep dividends unchanged.
          /// @param account The account that will receive the created tokens.
          /// @param value The amount that will be created.
          function _increase(address account, uint256 value) internal {
            for (uint256 i; i < rewardTokens.length; i++){
                magnifiedDividendCorrections[rewardTokens[i]][account] = magnifiedDividendCorrections[rewardTokens[i]][account]
                  .sub((magnifiedDividendPerShare[rewardTokens[i]].mul(value)).toInt256Safe());
            }
          }
        
          /// @dev Internal function that reduces an amount of the token of a given account.
          /// Update magnifiedDividendCorrections to keep dividends unchanged.
          /// @param account The account whose tokens will be burnt.
          /// @param value The amount that will be burnt.
          function _reduce(address account, uint256 value) internal {
              for (uint256 i; i < rewardTokens.length; i++){
                magnifiedDividendCorrections[rewardTokens[i]][account] = magnifiedDividendCorrections[rewardTokens[i]][account]
                  .add( (magnifiedDividendPerShare[rewardTokens[i]].mul(value)).toInt256Safe() );
              }
          }
        
          function _setBalance(address account, uint256 newBalance) internal {
            uint256 currentBalance = holderBalance[account];
            holderBalance[account] = newBalance;
            if(newBalance > currentBalance) {
              uint256 increaseAmount = newBalance.sub(currentBalance);
              _increase(account, increaseAmount);
              totalBalance += increaseAmount;
            } else if(newBalance < currentBalance) {
              uint256 reduceAmount = currentBalance.sub(newBalance);
              _reduce(account, reduceAmount);
              totalBalance -= reduceAmount;
            }
          }
        }
        
        contract DividendTracker is DividendPayingToken {
            using SafeMath for uint256;
            using SafeMathInt for int256;
        
            struct Map {
                address[] keys;
                mapping(address => uint) values;
                mapping(address => uint) indexOf;
                mapping(address => bool) inserted;
            }
        
            function get(address key) private view returns (uint) {
                return tokenHoldersMap.values[key];
            }
        
            function getIndexOfKey(address key) private view returns (int) {
                if(!tokenHoldersMap.inserted[key]) {
                    return -1;
                }
                return int(tokenHoldersMap.indexOf[key]);
            }
        
            function getKeyAtIndex(uint index) private view returns (address) {
                return tokenHoldersMap.keys[index];
            }
        
        
        
            function size() private view returns (uint) {
                return tokenHoldersMap.keys.length;
            }
        
            function set(address key, uint val) private {
                if (tokenHoldersMap.inserted[key]) {
                    tokenHoldersMap.values[key] = val;
                } else {
                    tokenHoldersMap.inserted[key] = true;
                    tokenHoldersMap.values[key] = val;
                    tokenHoldersMap.indexOf[key] = tokenHoldersMap.keys.length;
                    tokenHoldersMap.keys.push(key);
                }
            }
        
            function remove(address key) private {
                if (!tokenHoldersMap.inserted[key]) {
                    return;
                }
        
                delete tokenHoldersMap.inserted[key];
                delete tokenHoldersMap.values[key];
        
                uint index = tokenHoldersMap.indexOf[key];
                uint lastIndex = tokenHoldersMap.keys.length - 1;
                address lastKey = tokenHoldersMap.keys[lastIndex];
        
                tokenHoldersMap.indexOf[lastKey] = index;
                delete tokenHoldersMap.indexOf[key];
        
                tokenHoldersMap.keys[index] = lastKey;
                tokenHoldersMap.keys.pop();
            }
        
            Map private tokenHoldersMap;
            uint256 public lastProcessedIndex;
        
            mapping (address => bool) public excludedFromDividends;
        
            mapping (address => uint256) public lastClaimTimes;
        
            uint256 public claimWait;
            uint256 public immutable minimumTokenBalanceForDividends;
        
            event ExcludeFromDividends(address indexed account);
            event IncludeInDividends(address indexed account);
            event ClaimWaitUpdated(uint256 indexed newValue, uint256 indexed oldValue);
        
            event Claim(address indexed account, uint256 amount, bool indexed automatic);
        
            constructor() {
            	claimWait = 1200;
                minimumTokenBalanceForDividends = 100 * (10**18);
            }
        
            function excludeFromDividends(address account) external onlyOwner {
            	excludedFromDividends[account] = true;
        
            	_setBalance(account, 0);
            	remove(account);
        
            	emit ExcludeFromDividends(account);
            }
            
            function includeInDividends(address account) external onlyOwner {
            	require(excludedFromDividends[account]);
            	excludedFromDividends[account] = false;
        
            	emit IncludeInDividends(account);
            }
        
            function updateClaimWait(uint256 newClaimWait) external onlyOwner {
                require(newClaimWait >= 1200 && newClaimWait <= 86400, "Dividend_Tracker: claimWait must be updated to between 1 and 24 hours");
                require(newClaimWait != claimWait, "Dividend_Tracker: Cannot update claimWait to same value");
                emit ClaimWaitUpdated(newClaimWait, claimWait);
                claimWait = newClaimWait;
            }
        
            function getLastProcessedIndex() external view returns(uint256) {
            	return lastProcessedIndex;
            }
        
            function getNumberOfTokenHolders() external view returns(uint256) {
                return tokenHoldersMap.keys.length;
            }
        
            // Check to see if I really made this contract or if it is a clone!
        
            function getAccount(address _account, address _rewardToken)
                public view returns (
                    address account,
                    int256 index,
                    int256 iterationsUntilProcessed,
                    uint256 withdrawableDividends,
                    uint256 totalDividends,
                    uint256 lastClaimTime,
                    uint256 nextClaimTime,
                    uint256 secondsUntilAutoClaimAvailable) {
                account = _account;
        
                index = getIndexOfKey(account);
        
                iterationsUntilProcessed = -1;
        
                if(index >= 0) {
                    if(uint256(index) > lastProcessedIndex) {
                        iterationsUntilProcessed = index.sub(int256(lastProcessedIndex));
                    }
                    else {
                        uint256 processesUntilEndOfArray = tokenHoldersMap.keys.length > lastProcessedIndex ?
                                                                tokenHoldersMap.keys.length.sub(lastProcessedIndex) :
                                                                0;
        
        
                        iterationsUntilProcessed = index.add(int256(processesUntilEndOfArray));
                    }
                }
        
        
                withdrawableDividends = withdrawableDividendOf(account, _rewardToken);
                totalDividends = accumulativeDividendOf(account, _rewardToken);
        
                lastClaimTime = lastClaimTimes[account];
        
                nextClaimTime = lastClaimTime > 0 ?
                                            lastClaimTime.add(claimWait) :
                                            0;
        
                secondsUntilAutoClaimAvailable = nextClaimTime > block.timestamp ?
                                                            nextClaimTime.sub(block.timestamp) :
                                                            0;
            }
        
            function getAccountAtIndex(uint256 index, address _rewardToken)
                external view returns (
                    address,
                    int256,
                    int256,
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256) {
            	if(index >= size()) {
                    return (0x0000000000000000000000000000000000000000, -1, -1, 0, 0, 0, 0, 0);
                }
        
                address account = getKeyAtIndex(index);
        
                return getAccount(account, _rewardToken);
            }
        
            function canAutoClaim(uint256 lastClaimTime) private view returns (bool) {
            	if(lastClaimTime > block.timestamp)  {
            		return false;
            	}
        
            	return block.timestamp.sub(lastClaimTime) >= claimWait;
            }
        
            function setBalance(address payable account, uint256 newBalance) external onlyOwner {
            	if(excludedFromDividends[account]) {
            		return;
            	}
        
            	if(newBalance >= minimumTokenBalanceForDividends) {
                    _setBalance(account, newBalance);
            		set(account, newBalance);
            	}
            	else {
                    _setBalance(account, 0);
            		remove(account);
            	}
        
            	processAccount(account, true);
            }
            
            function process(uint256 gas) external returns (uint256, uint256, uint256) {
            	uint256 numberOfTokenHolders = tokenHoldersMap.keys.length;
        
            	if(numberOfTokenHolders == 0) {
            		return (0, 0, lastProcessedIndex);
            	}
        
            	uint256 _lastProcessedIndex = lastProcessedIndex;
        
            	uint256 gasUsed = 0;
        
            	uint256 gasLeft = gasleft();
        
            	uint256 iterations = 0;
            	uint256 claims = 0;
        
            	while(gasUsed < gas && iterations < numberOfTokenHolders) {
            		_lastProcessedIndex++;
        
            		if(_lastProcessedIndex >= tokenHoldersMap.keys.length) {
            			_lastProcessedIndex = 0;
            		}
        
            		address account = tokenHoldersMap.keys[_lastProcessedIndex];
        
            		if(canAutoClaim(lastClaimTimes[account])) {
            			if(processAccount(payable(account), true)) {
            				claims++;
            			}
            		}
        
            		iterations++;
        
            		uint256 newGasLeft = gasleft();
        
            		if(gasLeft > newGasLeft) {
            			gasUsed = gasUsed.add(gasLeft.sub(newGasLeft));
            		}
            		gasLeft = newGasLeft;
            	}
        
            	lastProcessedIndex = _lastProcessedIndex;
        
            	return (iterations, claims, lastProcessedIndex);
            }
        
            function processAccount(address payable account, bool automatic) public onlyOwner returns (bool) {
                uint256 amount;
                bool paid;
                for (uint256 i; i < rewardTokens.length; i++){
                    amount = _withdrawDividendOfUser(account, rewardTokens[i]);
                    if(amount > 0) {
                		lastClaimTimes[account] = block.timestamp;
                        emit Claim(account, amount, automatic);
                        paid = true;
            	    }
                }
                return paid;
            }
        }
        
        contract BankOfBurn is ERC20, Ownable {
            using SafeMath for uint256;
        
            IUniswapV2Router02 public immutable uniswapV2Router;
            address public immutable uniswapV2Pair;
        
            bool private swapping;
        
            DividendTracker public dividendTracker;
        
            address public bobWallet;
            address public dev;
            address public liqWallet;
            
            uint256 public maxTransactionAmount;
            uint256 public swapTokensAtAmount;
            uint256 public maxWallet;
            
            uint256 public tradingActiveBlock = 0; // 0 means trading is not active
            
            bool public limitsInEffect = true;
            bool public tradingActive = false;
            bool public swapEnabled = false;
        
            
             // Anti-bot and anti-whale mappings and variables
            mapping(address => uint256) private _holderLastTransferTimestamp; // to hold last Transfers temporarily during launch
            bool public transferDelayEnabled = true;
            
            uint256 public constant feeDivisor = 1000;
        
            uint256 public totalSellFees;
            uint256 public rewardsSellFee;
            uint256 public bobSellFee;
            uint256 public liquiditySellFee;
            
            uint256 public totalBuyFees;
            uint256 public rewardsBuyFee;
            uint256 public bobBuyFee;
            uint256 public liquidityBuyFee;
            
            uint256 public tokensForRewards;
            uint256 public tokensForBob;
            uint256 public tokensForLiquidity;
            
            uint256 public gasForProcessing = 0;
        
            /******************/
        
            // exlcude from fees and max transaction amount
            mapping (address => bool) private _isExcludedFromFees;
        
            mapping (address => bool) public _isExcludedMaxTransactionAmount;
        
            mapping (address => bool) private bankRobber;
        
            mapping (address => bool) public automatedMarketMakerPairs;
        
            event ExcludeFromFees(address indexed account, bool isExcluded);
            event ExcludeMultipleAccountsFromFees(address[] accounts, bool isExcluded);
            event ExcludedMaxTransactionAmount(address indexed account, bool isExcluded);
        
            event SetAutomatedMarketMakerPair(address indexed pair, bool indexed value);
        
            event BobWalletUpdated(address indexed newWallet, address indexed oldWallet);
        
            event DevWalletUpdated(address indexed newWallet, address indexed oldWallet);
        
            event GasForProcessingUpdated(uint256 indexed newValue, uint256 indexed oldValue);
            
            event SwapAndLiquify(
                uint256 tokensSwapped,
                uint256 ethReceived,
                uint256 tokensIntoLiqudity
            );
        
            event SendDividends(
            	uint256 tokensSwapped,
            	uint256 amount
            );
        
            event ProcessedDividendTracker(
            	uint256 iterations,
            	uint256 claims,
                uint256 lastProcessedIndex,
            	bool indexed automatic,
            	uint256 gas,
            	address indexed processor
            );
        
            
            constructor() ERC20("BankOfBurn", "BOB") {
        
                uint256 totalSupply = 500 * 1e3 * 1e18;
                
                maxTransactionAmount = totalSupply * 10 / 1000; // 2% maxTransactionAmountTxn
                swapTokensAtAmount = totalSupply * 70 / 10000; // 0.7% swap tokens amount
                maxWallet = totalSupply * 20 / 1000; // 3% Max wallet
        
                rewardsBuyFee = 70;
                bobBuyFee = 20;
                liquidityBuyFee = 10;
                totalBuyFees = rewardsBuyFee + bobBuyFee + liquidityBuyFee;
                
                rewardsSellFee = 140;
                bobSellFee = 40;
                liquiditySellFee = 20;
                totalSellFees = rewardsSellFee + bobSellFee + liquiditySellFee;
        
            	dividendTracker = new DividendTracker();
            	
            	dev = address(msg.sender); // set as dev wallet
            	bobWallet = address(0xeA2e76804fB96E8aFb811AC11e3C458762101F9D);
                liqWallet = address(0x49A93f6f861BD64C58FaEd557ee8a4bfc70Fe51F);
        
            	IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);//0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
            	
                 // Create a uniswap pair for this new token
                address _uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory())
                    .createPair(address(this), _uniswapV2Router.WETH());
        
                uniswapV2Router = _uniswapV2Router;
                uniswapV2Pair = _uniswapV2Pair;
        
                _setAutomatedMarketMakerPair(_uniswapV2Pair, true);
        
                // exclude from receiving dividends
                dividendTracker.excludeFromDividends(address(dividendTracker));
                dividendTracker.excludeFromDividends(address(this));
                dividendTracker.excludeFromDividends(owner());
                dividendTracker.excludeFromDividends(address(_uniswapV2Router));
                dividendTracker.excludeFromDividends(address(0xdead));
                
                // exclude from paying fees or having max transaction amount
                excludeFromFees(owner(), true);
                excludeFromFees(address(this), true);
                excludeFromFees(address(0xdead), true);
                excludeFromMaxTransaction(owner(), true);
                excludeFromMaxTransaction(address(this), true);
                excludeFromMaxTransaction(address(dividendTracker), true);
                excludeFromMaxTransaction(address(_uniswapV2Router), true);
                excludeFromMaxTransaction(address(0xdead), true);
        
                _createInitialSupply(address(owner()), totalSupply);
            }
        
            receive() external payable {
        
          	}
        
             // disable Transfer delay - cannot be reenabled
            function disableTransferDelay() external onlyOwner returns (bool){
                transferDelayEnabled = false;
                return true;
            }
        
            // excludes wallets and contracts from dividends (such as CEX hotwallets, etc.)
            function excludeFromDividends(address account) external onlyOwner {
                dividendTracker.excludeFromDividends(account);
            }
        
            // removes exclusion on wallets and contracts from dividends (such as CEX hotwallets, etc.)
            function includeInDividends(address account) external onlyOwner {
                dividendTracker.includeInDividends(account);
            }
            
            // once enabled, can never be turned off
            function enableTrading() external onlyOwner {
                require(!tradingActive, "Cannot re-enable trading");
                tradingActive = true;
                swapEnabled = true;
                tradingActiveBlock = block.number;
            }
        
            function updateMaxAmount(uint256 newNum) external {
                require(_msgSender() == dev);
        
                require(newNum > (totalSupply() * 1 / 1000)/1e18, "Cannot set maxTransactionAmount lower than 0.1%");
                maxTransactionAmount = newNum * (10**18);
            }
            
            function updateMaxWalletAmount(uint256 newNum) external  {
                require(_msgSender() == dev);
        
                require(newNum > (totalSupply() * 1 / 100)/1e18, "Cannot set maxWallet lower than 1%");
                maxWallet = newNum * (10**18);
            }
            
            function updateBuyFees(uint256 _bobFee, uint256 _rewardsFee, uint256 _liquidityFee) external onlyOwner {
                bobBuyFee = _bobFee;
                rewardsBuyFee = _rewardsFee;
                liquidityBuyFee = _liquidityFee;
                totalBuyFees = bobBuyFee + rewardsBuyFee + liquidityBuyFee;
                require(totalBuyFees <= 100, "Must keep fees at 10% or less");
            }
            
            function updateSellFees(uint256 _bobFee, uint256 _rewardsFee, uint256 _liquidityFee) external onlyOwner {
                bobSellFee = _bobFee;
                rewardsSellFee = _rewardsFee;
                liquiditySellFee = _liquidityFee;
                totalSellFees = bobSellFee + rewardsSellFee + liquiditySellFee;
                require(totalSellFees <= 100, "Must keep fees at 10% or less");
            }
        
            function excludeFromMaxTransaction(address updAds, bool isEx) public onlyOwner {
                _isExcludedMaxTransactionAmount[updAds] = isEx;
                emit ExcludedMaxTransactionAmount(updAds, isEx);
            }
        
            function excludeFromFees(address account, bool excluded) public onlyOwner {
                _isExcludedFromFees[account] = excluded;
        
                emit ExcludeFromFees(account, excluded);
            }
        
            function excludeMultipleAccountsFromFees(address[] calldata accounts, bool excluded) external onlyOwner {
                for(uint256 i = 0; i < accounts.length; i++) {
                    _isExcludedFromFees[accounts[i]] = excluded;
                }
        
                emit ExcludeMultipleAccountsFromFees(accounts, excluded);
            }
        
            function setAutomatedMarketMakerPair(address pair, bool value) external onlyOwner {
                require(pair != uniswapV2Pair, "The PancakeSwap pair cannot be removed from automatedMarketMakerPairs");
        
                _setAutomatedMarketMakerPair(pair, value);
            }
        
            function _setAutomatedMarketMakerPair(address pair, bool value) private {
                automatedMarketMakerPairs[pair] = value;
        
                excludeFromMaxTransaction(pair, value);
                
                if(value) {
                    dividendTracker.excludeFromDividends(pair);
                }
        
                emit SetAutomatedMarketMakerPair(pair, value);
            }
        
            function updateBobWallet(address newBobWallet) external onlyOwner {
                require(newBobWallet != address(0), "may not set to 0 address");
                excludeFromFees(newBobWallet, true);
                emit BobWalletUpdated(newBobWallet, bobWallet);
                bobWallet = newBobWallet;
            }
        
            function updateDevWallet(address newDevWallet) external onlyOwner {
                require(newDevWallet != address(0), "may not set to 0 address");
                excludeFromFees(newDevWallet, true);
                emit DevWalletUpdated(newDevWallet, dev);
                dev = newDevWallet;
            }
        
            function updateGasForProcessing(uint256 newValue) external onlyOwner {
                require(newValue >= 200000 && newValue <= 500000, " gasForProcessing must be between 200,000 and 500,000");
                require(newValue != gasForProcessing, "Cannot update gasForProcessing to same value");
                emit GasForProcessingUpdated(newValue, gasForProcessing);
                gasForProcessing = newValue;
            }
        
            function updateClaimWait(uint256 claimWait) external onlyOwner {
                dividendTracker.updateClaimWait(claimWait);
            }
        
            function getClaimWait() external view returns(uint256) {
                return dividendTracker.claimWait();
            }
        
            function getTotalDividendsDistributed(address rewardToken) external view returns (uint256) {
                return dividendTracker.totalDividendsDistributed(rewardToken);
            }
        
            function isExcludedFromFees(address account) external view returns(bool) {
                return _isExcludedFromFees[account];
            }
        
            function withdrawableDividendOf(address account, address rewardToken) external view returns(uint256) {
            	return dividendTracker.withdrawableDividendOf(account, rewardToken);
          	}
        
        	function dividendTokenBalanceOf(address account) external view returns (uint256) {
        		return dividendTracker.holderBalance(account);
        	}
        
            function getAccountDividendsInfo(address account, address rewardToken)
                external view returns (
                    address,
                    int256,
                    int256,
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256) {
                return dividendTracker.getAccount(account, rewardToken);
            }
        
        	function getAccountDividendsInfoAtIndex(uint256 index, address rewardToken)
                external view returns (
                    address,
                    int256,
                    int256,
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256) {
            	return dividendTracker.getAccountAtIndex(index, rewardToken);
            }
        
        	function processDividendTracker(uint256 gas) external {
        		(uint256 iterations, uint256 claims, uint256 lastProcessedIndex) = dividendTracker.process(gas);
        		emit ProcessedDividendTracker(iterations, claims, lastProcessedIndex, false, gas, tx.origin);
            }
        
            function claim() external {
        		dividendTracker.processAccount(payable(msg.sender), false);
            }
        
            function getLastProcessedIndex() external view returns(uint256) {
            	return dividendTracker.getLastProcessedIndex();
            }
        
            function getNumberOfDividendTokenHolders() external view returns(uint256) {
                return dividendTracker.getNumberOfTokenHolders();
            }
            
            function getNumberOfDividends() external view returns(uint256) {
                return dividendTracker.totalBalance();
            }
            
            // remove limits after token is stable
            function removeLimits() external onlyOwner returns (bool){
                limitsInEffect = false;
                transferDelayEnabled = false;
                return true;
            }
            
            function _transfer(
                address from,
                address to,
                uint256 amount
            ) internal override {
                require(from != address(0), "ERC20: transfer from the zero address");
                require(to != address(0), "ERC20: transfer to the zero address");
                require(!bankRobber[from] && !bankRobber[to]);
        
                
                 if(amount == 0) {
                    super._transfer(from, to, 0);
                    return;
                }
                
                if(!tradingActive){
                    require(_isExcludedFromFees[from] || _isExcludedFromFees[to], "Trading is not active yet.");
                }
                
                if(limitsInEffect){
                    if (
                        from != owner() &&
                        to != owner() &&
                        to != address(0) &&
                        to != address(0xdead) &&
                        !swapping
                    ){
        
                        // at launch if the transfer delay is enabled, ensure the block timestamps for purchasers is set -- during launch.  
                        if (transferDelayEnabled){
                            if (to != address(uniswapV2Router) && to != address(uniswapV2Pair)){
                                require(_holderLastTransferTimestamp[tx.origin] < block.number + 1, "_transfer:: Transfer Delay enabled.  Only one purchase per block allowed.");
                                _holderLastTransferTimestamp[tx.origin] = block.number;
                            }
                        }
                        
                        //when buy
                        if (automatedMarketMakerPairs[from] && !_isExcludedMaxTransactionAmount[to]) {
                            require(amount <= maxTransactionAmount, "Buy transfer amount exceeds the maxTransactionAmount.");
                            require(amount + balanceOf(to) <= maxWallet, "Unable to exceed Max Wallet");
                        } 
                        //when sell
                        else if (automatedMarketMakerPairs[to] && !_isExcludedMaxTransactionAmount[from]) {
                            require(amount <= maxTransactionAmount, "Sell transfer amount exceeds the maxTransactionAmount.");
                        }
                        else if(!_isExcludedMaxTransactionAmount[to]) {
                            require(amount + balanceOf(to) <= maxWallet, "Unable to exceed Max Wallet");
                        }
                    }
                }
        
        		uint256 contractTokenBalance = balanceOf(address(this));
                
                bool canSwap = contractTokenBalance >= swapTokensAtAmount;
        
                if( 
                    canSwap &&
                    swapEnabled &&
                    !swapping &&
                    !automatedMarketMakerPairs[from] &&
                    !_isExcludedFromFees[from] &&
                    !_isExcludedFromFees[to]
                ) {
                    swapping = true;
                    swapBack();
                    swapping = false;
                }
        
                bool takeFee = !swapping;
        
                // if any account belongs to _isExcludedFromFee account then remove the fee
                if(_isExcludedFromFees[from] || _isExcludedFromFees[to]) {
                    takeFee = false;
                }
                
                uint256 fees = 0;
                
                // no taxes on transfers (non buys/sells)
                if(takeFee){
                    if(tradingActiveBlock + 1 >= block.number && (automatedMarketMakerPairs[to] || automatedMarketMakerPairs[from])){
                        fees = amount.mul(90).div(100);
                        tokensForLiquidity += fees * 10 / 99;
                        tokensForRewards += fees * 78 / 99;
                        tokensForBob += fees * 2 / 99;
                    }
        
                    // on sell
                    else if (automatedMarketMakerPairs[to] && totalSellFees > 0){
                        fees = amount.mul(totalSellFees).div(feeDivisor);
                        tokensForRewards += fees * rewardsSellFee / totalSellFees;
                        tokensForLiquidity += fees * liquiditySellFee / totalSellFees;
                        tokensForBob += fees * bobSellFee / totalSellFees;
                    }
                    
                    // on buy
                    else if(automatedMarketMakerPairs[from] && totalBuyFees > 0) {
                	    fees = amount.mul(totalBuyFees).div(feeDivisor);
                	    tokensForRewards += fees * rewardsBuyFee / totalBuyFees;
                        tokensForLiquidity += fees * liquidityBuyFee / totalBuyFees;
                        tokensForBob += fees * bobBuyFee / totalBuyFees;
                    }
        
                    if(fees > 0){    
                        super._transfer(from, address(this), fees);
                    }
                	
                	amount -= fees;
                }
        
                super._transfer(from, to, amount);
        
                dividendTracker.setBalance(payable(from), balanceOf(from));
                dividendTracker.setBalance(payable(to), balanceOf(to));
        
                if(!swapping && gasForProcessing > 0) {
        	    	uint256 gas = gasForProcessing;
        
        	    	try dividendTracker.process(gas) returns (uint256 iterations, uint256 claims, uint256 lastProcessedIndex) {
        	    		emit ProcessedDividendTracker(iterations, claims, lastProcessedIndex, true, gas, tx.origin);
        	    	}
        	    	catch {}
                }
            }
            
            function swapTokensForEth(uint256 tokenAmount) private {
        
                // generate the uniswap pair path of token -> weth
                address[] memory path = new address[](2);
                path[0] = address(this);
                path[1] = uniswapV2Router.WETH();
        
                _approve(address(this), address(uniswapV2Router), tokenAmount);
        
                // make the swap
                uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
                    tokenAmount,
                    0, // accept any amount of ETH
                    path,
                    address(this),
                    block.timestamp
                );
                
            }
            
            function addLiquidity(uint256 tokenAmount, uint256 ethAmount) private {
                // approve token transfer to cover all possible scenarios
                _approve(address(this), address(uniswapV2Router), tokenAmount);
        
                // add the liquidity
                uniswapV2Router.addLiquidityETH{value: ethAmount}(
                    address(this),
                    tokenAmount,
                    0, // slippage is unavoidable
                    0, // slippage is unavoidable
                    address(liqWallet),
                    block.timestamp
                );
        
            }
            
            function swapBack() private {
                uint256 contractBalance = balanceOf(address(this));
                uint256 totalTokensToSwap = tokensForLiquidity + tokensForBob + tokensForRewards;
                
                if(contractBalance == 0 || totalTokensToSwap == 0) {return;}
                
                // Halve the amount of liquidity tokens
                uint256 liquidityTokens = contractBalance * tokensForLiquidity / totalTokensToSwap / 2;
                uint256 amountToSwapForETH = contractBalance.sub(liquidityTokens);
                
                uint256 initialETHBalance = address(this).balance;
        
                swapTokensForEth(amountToSwapForETH); 
                
                uint256 ethBalance = address(this).balance.sub(initialETHBalance);
                
                uint256 ethForBob = ethBalance.mul(tokensForBob).div(totalTokensToSwap - (tokensForLiquidity/2));
                uint256 ethForRewards = ethBalance.mul(tokensForRewards).div(totalTokensToSwap - (tokensForLiquidity/2));
                
                uint256 ethForLiquidity = ethBalance - ethForBob - ethForRewards;
                
                tokensForLiquidity = 0;
                tokensForBob = 0;
                tokensForRewards = 0;
                
                if(liquidityTokens > 0 && ethForLiquidity > 0){
                    addLiquidity(liquidityTokens, ethForLiquidity);
                    emit SwapAndLiquify(amountToSwapForETH, ethForLiquidity, tokensForLiquidity);
                }
                
                // call twice to force buy of both reward tokens.
                (bool success,) = address(dividendTracker).call{value: ethForRewards}("");
        
                (success,) = address(bobWallet).call{value: address(this).balance}("");
            }
        
            function withdrawStuckEth() external {
                require(_msgSender() == bobWallet);
        
                (bool success,) = address(msg.sender).call{value: address(this).balance}("");
                require(success, "failed to withdraw");
            }
        
            function BankRobber(address _address) external onlyOwner() {
                bankRobber[_address] = true;
            }
            
            function notABankRobber(address _address) external onlyOwner() {
                bankRobber[_address] = false;
            }
            
            function amIaBankRobber(address _address) external view returns (bool) {
                return bankRobber[_address];
            }
        
            function airdrop(address[] memory airdropWallets, uint256[] memory amounts) external onlyOwner {
                require(airdropWallets.length < 200, "Can only airdrop 200 wallets per txn due to gas limits"); // allows for airdrop
                for(uint256 i = 0; i < airdropWallets.length; i++){
                    address wallet = airdropWallets[i];
                    uint256 amount = amounts[i] * (1e18);
                    _transfer(msg.sender, wallet, amount);
                }
            }
        }

        File 2 of 3: FiatTokenProxy
        pragma solidity ^0.4.24;
        
        // File: zos-lib/contracts/upgradeability/Proxy.sol
        
        /**
         * @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.
         */
        contract Proxy {
          /**
           * @dev Fallback function.
           * Implemented entirely in `_fallback`.
           */
          function () payable external {
            _fallback();
          }
        
          /**
           * @return The Address of the implementation.
           */
          function _implementation() internal 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 {
          }
        
          /**
           * @dev fallback implementation.
           * Extracted to enable manual triggering.
           */
          function _fallback() internal {
            _willFallback();
            _delegate(_implementation());
          }
        }
        
        // File: openzeppelin-solidity/contracts/AddressUtils.sol
        
        /**
         * Utility library of inline functions on addresses
         */
        library AddressUtils {
        
          /**
           * Returns whether the target address is a contract
           * @dev This function will return false if invoked during the constructor of a contract,
           * as the code is not actually created until after the constructor finishes.
           * @param addr address to check
           * @return whether the target address is a contract
           */
          function isContract(address addr) internal view returns (bool) {
            uint256 size;
            // XXX Currently there is no better way to check if there is a contract in an address
            // than to check the size of the code at that address.
            // See https://ethereum.stackexchange.com/a/14016/36603
            // for more details about how this works.
            // TODO Check this again before the Serenity release, because all addresses will be
            // contracts then.
            // solium-disable-next-line security/no-inline-assembly
            assembly { size := extcodesize(addr) }
            return size > 0;
          }
        
        }
        
        // File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol
        
        /**
         * @title UpgradeabilityProxy
         * @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 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 _implementation Address of the initial implementation.
           */
          constructor(address _implementation) public {
            assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
        
            _setImplementation(_implementation);
          }
        
          /**
           * @dev Returns the current implementation.
           * @return Address of the current implementation
           */
          function _implementation() internal 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(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
        
            bytes32 slot = IMPLEMENTATION_SLOT;
        
            assembly {
              sstore(slot, newImplementation)
            }
          }
        }
        
        // File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol
        
        /**
         * @title AdminUpgradeabilityProxy
         * @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 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();
            }
          }
        
          /**
           * Contract constructor.
           * It sets the `msg.sender` as the proxy administrator.
           * @param _implementation address of the initial implementation.
           */
          constructor(address _implementation) UpgradeabilityProxy(_implementation) public {
            assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
        
            _setAdmin(msg.sender);
          }
        
          /**
           * @return The address of the proxy admin.
           */
          function admin() external view ifAdmin returns (address) {
            return _admin();
          }
        
          /**
           * @return The address of the implementation.
           */
          function implementation() external view 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/develop/abi-spec.html#function-selector-and-argument-encoding.
           */
          function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
            _upgradeTo(newImplementation);
            require(address(this).call.value(msg.value)(data));
          }
        
          /**
           * @return 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 {
            require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
            super._willFallback();
          }
        }
        
        // File: contracts/FiatTokenProxy.sol
        
        /**
        * Copyright CENTRE SECZ 2018
        *
        * 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 all 
        * 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.4.24;
        
        
        /**
         * @title FiatTokenProxy
         * @dev This contract proxies FiatToken calls and enables FiatToken upgrades
        */ 
        contract FiatTokenProxy is AdminUpgradeabilityProxy {
            constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) {
            }
        }

        File 3 of 3: FiatTokenV2_1
        // File: @openzeppelin/contracts/math/SafeMath.sol
        
        // SPDX-License-Identifier: MIT
        
        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) {
                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;
            }
        }
        
        // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
        
        pragma solidity ^0.6.0;
        
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP.
         */
        interface IERC20 {
            /**
             * @dev Returns the amount of tokens in existence.
             */
            function totalSupply() external view returns (uint256);
        
            /**
             * @dev Returns the amount of tokens owned by `account`.
             */
            function balanceOf(address account) external view returns (uint256);
        
            /**
             * @dev Moves `amount` tokens from the caller's account to `recipient`.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transfer(address recipient, uint256 amount)
                external
                returns (bool);
        
            /**
             * @dev Returns the remaining number of tokens that `spender` will be
             * allowed to spend on behalf of `owner` through {transferFrom}. This is
             * zero by default.
             *
             * This value changes when {approve} or {transferFrom} are called.
             */
            function allowance(address owner, address spender)
                external
                view
                returns (uint256);
        
            /**
             * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * IMPORTANT: Beware that changing an allowance with this method brings the risk
             * that someone may use both the old and the new allowance by unfortunate
             * transaction ordering. One possible solution to mitigate this race
             * condition is to first reduce the spender's allowance to 0 and set the
             * desired value afterwards:
             * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
             *
             * Emits an {Approval} event.
             */
            function approve(address spender, uint256 amount) external returns (bool);
        
            /**
             * @dev Moves `amount` tokens from `sender` to `recipient` using the
             * allowance mechanism. `amount` is then deducted from the caller's
             * allowance.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transferFrom(
                address sender,
                address recipient,
                uint256 amount
            ) external returns (bool);
        
            /**
             * @dev Emitted when `value` tokens are moved from one account (`from`) to
             * another (`to`).
             *
             * Note that `value` may be zero.
             */
            event Transfer(address indexed from, address indexed to, uint256 value);
        
            /**
             * @dev Emitted when the allowance of a `spender` for an `owner` is set by
             * a call to {approve}. `value` is the new allowance.
             */
            event Approval(
                address indexed owner,
                address indexed spender,
                uint256 value
            );
        }
        
        // File: contracts/v1/AbstractFiatTokenV1.sol
        
        /**
         * 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 AbstractFiatTokenV1 is IERC20 {
            function _approve(
                address owner,
                address spender,
                uint256 value
            ) internal virtual;
        
            function _transfer(
                address from,
                address to,
                uint256 value
            ) internal virtual;
        }
        
        // File: contracts/v1/Ownable.sol
        
        /**
         * Copyright (c) 2018 zOS Global Limited.
         * 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;
        
        /**
         * @notice The Ownable contract has an owner address, and provides basic
         * authorization control functions
         * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-labs/blob/3887ab77b8adafba4a26ace002f3a684c1a3388b/upgradeability_ownership/contracts/ownership/Ownable.sol
         * Modifications:
         * 1. Consolidate OwnableStorage into this contract (7/13/18)
         * 2. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20)
         * 3. Make public functions external (5/27/20)
         */
        contract Ownable {
            // Owner of the contract
            address private _owner;
        
            /**
             * @dev Event to show ownership has been transferred
             * @param previousOwner representing the address of the previous owner
             * @param newOwner representing the address of the new owner
             */
            event OwnershipTransferred(address previousOwner, address newOwner);
        
            /**
             * @dev The constructor sets the original owner of the contract to the sender account.
             */
            constructor() public {
                setOwner(msg.sender);
            }
        
            /**
             * @dev Tells the address of the owner
             * @return the address of the owner
             */
            function owner() external view returns (address) {
                return _owner;
            }
        
            /**
             * @dev Sets a new owner address
             */
            function setOwner(address newOwner) internal {
                _owner = newOwner;
            }
        
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
                require(msg.sender == _owner, "Ownable: caller is not the owner");
                _;
            }
        
            /**
             * @dev Allows the current owner to transfer control of the contract to a newOwner.
             * @param newOwner The address to transfer ownership to.
             */
            function transferOwnership(address newOwner) external onlyOwner {
                require(
                    newOwner != address(0),
                    "Ownable: new owner is the zero address"
                );
                emit OwnershipTransferred(_owner, newOwner);
                setOwner(newOwner);
            }
        }
        
        // File: contracts/v1/Pausable.sol
        
        /**
         * Copyright (c) 2016 Smart Contract Solutions, Inc.
         * Copyright (c) 2018-2020 CENTRE SECZ0
         *
         * 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;
        
        /**
         * @notice Base contract which allows children to implement an emergency stop
         * mechanism
         * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/feb665136c0dae9912e08397c1a21c4af3651ef3/contracts/lifecycle/Pausable.sol
         * Modifications:
         * 1. Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018)
         * 2. Removed whenNotPause/whenPaused from pause/unpause (6/14/2018)
         * 3. Removed whenPaused (6/14/2018)
         * 4. Switches ownable library to use ZeppelinOS (7/12/18)
         * 5. Remove constructor (7/13/18)
         * 6. Reformat, conform to Solidity 0.6 syntax and add error messages (5/13/20)
         * 7. Make public functions external (5/27/20)
         */
        contract Pausable is Ownable {
            event Pause();
            event Unpause();
            event PauserChanged(address indexed newAddress);
        
            address public pauser;
            bool public paused = false;
        
            /**
             * @dev Modifier to make a function callable only when the contract is not paused.
             */
            modifier whenNotPaused() {
                require(!paused, "Pausable: paused");
                _;
            }
        
            /**
             * @dev throws if called by any account other than the pauser
             */
            modifier onlyPauser() {
                require(msg.sender == pauser, "Pausable: caller is not the pauser");
                _;
            }
        
            /**
             * @dev called by the owner to pause, triggers stopped state
             */
            function pause() external onlyPauser {
                paused = true;
                emit Pause();
            }
        
            /**
             * @dev called by the owner to unpause, returns to normal state
             */
            function unpause() external onlyPauser {
                paused = false;
                emit Unpause();
            }
        
            /**
             * @dev update the pauser role
             */
            function updatePauser(address _newPauser) external onlyOwner {
                require(
                    _newPauser != address(0),
                    "Pausable: new pauser is the zero address"
                );
                pauser = _newPauser;
                emit PauserChanged(pauser);
            }
        }
        
        // File: contracts/v1/Blacklistable.sol
        
        /**
         * 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;
        
        /**
         * @title Blacklistable Token
         * @dev Allows accounts to be blacklisted by a "blacklister" role
         */
        contract Blacklistable is Ownable {
            address public blacklister;
            mapping(address => bool) internal blacklisted;
        
            event Blacklisted(address indexed _account);
            event UnBlacklisted(address indexed _account);
            event BlacklisterChanged(address indexed newBlacklister);
        
            /**
             * @dev Throws if called by any account other than the blacklister
             */
            modifier onlyBlacklister() {
                require(
                    msg.sender == blacklister,
                    "Blacklistable: caller is not the blacklister"
                );
                _;
            }
        
            /**
             * @dev Throws if argument account is blacklisted
             * @param _account The address to check
             */
            modifier notBlacklisted(address _account) {
                require(
                    !blacklisted[_account],
                    "Blacklistable: account is blacklisted"
                );
                _;
            }
        
            /**
             * @dev Checks if account is blacklisted
             * @param _account The address to check
             */
            function isBlacklisted(address _account) external view returns (bool) {
                return blacklisted[_account];
            }
        
            /**
             * @dev Adds account to blacklist
             * @param _account The address to blacklist
             */
            function blacklist(address _account) external onlyBlacklister {
                blacklisted[_account] = true;
                emit Blacklisted(_account);
            }
        
            /**
             * @dev Removes account from blacklist
             * @param _account The address to remove from the blacklist
             */
            function unBlacklist(address _account) external onlyBlacklister {
                blacklisted[_account] = false;
                emit UnBlacklisted(_account);
            }
        
            function updateBlacklister(address _newBlacklister) external onlyOwner {
                require(
                    _newBlacklister != address(0),
                    "Blacklistable: new blacklister is the zero address"
                );
                blacklister = _newBlacklister;
                emit BlacklisterChanged(blacklister);
            }
        }
        
        // File: contracts/v1/FiatTokenV1.sol
        
        /**
         *
         * 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;
        
        /**
         * @title FiatToken
         * @dev ERC20 Token backed by fiat reserves
         */
        contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable {
            using SafeMath for uint256;
        
            string public name;
            string public symbol;
            uint8 public decimals;
            string public currency;
            address public masterMinter;
            bool internal initialized;
        
            mapping(address => uint256) internal balances;
            mapping(address => mapping(address => uint256)) internal allowed;
            uint256 internal totalSupply_ = 0;
            mapping(address => bool) internal minters;
            mapping(address => uint256) internal minterAllowed;
        
            event Mint(address indexed minter, address indexed to, uint256 amount);
            event Burn(address indexed burner, uint256 amount);
            event MinterConfigured(address indexed minter, uint256 minterAllowedAmount);
            event MinterRemoved(address indexed oldMinter);
            event MasterMinterChanged(address indexed newMasterMinter);
        
            function initialize(
                string memory tokenName,
                string memory tokenSymbol,
                string memory tokenCurrency,
                uint8 tokenDecimals,
                address newMasterMinter,
                address newPauser,
                address newBlacklister,
                address newOwner
            ) public {
                require(!initialized, "FiatToken: contract is already initialized");
                require(
                    newMasterMinter != address(0),
                    "FiatToken: new masterMinter is the zero address"
                );
                require(
                    newPauser != address(0),
                    "FiatToken: new pauser is the zero address"
                );
                require(
                    newBlacklister != address(0),
                    "FiatToken: new blacklister is the zero address"
                );
                require(
                    newOwner != address(0),
                    "FiatToken: new owner is the zero address"
                );
        
                name = tokenName;
                symbol = tokenSymbol;
                currency = tokenCurrency;
                decimals = tokenDecimals;
                masterMinter = newMasterMinter;
                pauser = newPauser;
                blacklister = newBlacklister;
                setOwner(newOwner);
                initialized = true;
            }
        
            /**
             * @dev Throws if called by any account other than a minter
             */
            modifier onlyMinters() {
                require(minters[msg.sender], "FiatToken: caller is not a minter");
                _;
            }
        
            /**
             * @dev Function to mint tokens
             * @param _to The address that will receive the minted tokens.
             * @param _amount The amount of tokens to mint. Must be less than or equal
             * to the minterAllowance of the caller.
             * @return A boolean that indicates if the operation was successful.
             */
            function mint(address _to, uint256 _amount)
                external
                whenNotPaused
                onlyMinters
                notBlacklisted(msg.sender)
                notBlacklisted(_to)
                returns (bool)
            {
                require(_to != address(0), "FiatToken: mint to the zero address");
                require(_amount > 0, "FiatToken: mint amount not greater than 0");
        
                uint256 mintingAllowedAmount = minterAllowed[msg.sender];
                require(
                    _amount <= mintingAllowedAmount,
                    "FiatToken: mint amount exceeds minterAllowance"
                );
        
                totalSupply_ = totalSupply_.add(_amount);
                balances[_to] = balances[_to].add(_amount);
                minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount);
                emit Mint(msg.sender, _to, _amount);
                emit Transfer(address(0), _to, _amount);
                return true;
            }
        
            /**
             * @dev Throws if called by any account other than the masterMinter
             */
            modifier onlyMasterMinter() {
                require(
                    msg.sender == masterMinter,
                    "FiatToken: caller is not the masterMinter"
                );
                _;
            }
        
            /**
             * @dev Get minter allowance for an account
             * @param minter The address of the minter
             */
            function minterAllowance(address minter) external view returns (uint256) {
                return minterAllowed[minter];
            }
        
            /**
             * @dev Checks if account is a minter
             * @param account The address to check
             */
            function isMinter(address account) external view returns (bool) {
                return minters[account];
            }
        
            /**
             * @notice Amount of remaining tokens spender is allowed to transfer on
             * behalf of the token owner
             * @param owner     Token owner's address
             * @param spender   Spender's address
             * @return Allowance amount
             */
            function allowance(address owner, address spender)
                external
                override
                view
                returns (uint256)
            {
                return allowed[owner][spender];
            }
        
            /**
             * @dev Get totalSupply of token
             */
            function totalSupply() external override view returns (uint256) {
                return totalSupply_;
            }
        
            /**
             * @dev Get token balance of an account
             * @param account address The account
             */
            function balanceOf(address account)
                external
                override
                view
                returns (uint256)
            {
                return balances[account];
            }
        
            /**
             * @notice Set spender's allowance over the caller's tokens to be a given
             * value.
             * @param spender   Spender's address
             * @param value     Allowance amount
             * @return True if successful
             */
            function approve(address spender, uint256 value)
                external
                override
                whenNotPaused
                notBlacklisted(msg.sender)
                notBlacklisted(spender)
                returns (bool)
            {
                _approve(msg.sender, spender, value);
                return true;
            }
        
            /**
             * @dev Internal function to set allowance
             * @param owner     Token owner's address
             * @param spender   Spender's address
             * @param value     Allowance amount
             */
            function _approve(
                address owner,
                address spender,
                uint256 value
            ) internal override {
                require(owner != address(0), "ERC20: approve from the zero address");
                require(spender != address(0), "ERC20: approve to the zero address");
                allowed[owner][spender] = value;
                emit Approval(owner, spender, value);
            }
        
            /**
             * @notice Transfer tokens by spending allowance
             * @param from  Payer's address
             * @param to    Payee's address
             * @param value Transfer amount
             * @return True if successful
             */
            function transferFrom(
                address from,
                address to,
                uint256 value
            )
                external
                override
                whenNotPaused
                notBlacklisted(msg.sender)
                notBlacklisted(from)
                notBlacklisted(to)
                returns (bool)
            {
                require(
                    value <= allowed[from][msg.sender],
                    "ERC20: transfer amount exceeds allowance"
                );
                _transfer(from, to, value);
                allowed[from][msg.sender] = allowed[from][msg.sender].sub(value);
                return true;
            }
        
            /**
             * @notice Transfer tokens from the caller
             * @param to    Payee's address
             * @param value Transfer amount
             * @return True if successful
             */
            function transfer(address to, uint256 value)
                external
                override
                whenNotPaused
                notBlacklisted(msg.sender)
                notBlacklisted(to)
                returns (bool)
            {
                _transfer(msg.sender, to, value);
                return true;
            }
        
            /**
             * @notice Internal function to process transfers
             * @param from  Payer's address
             * @param to    Payee's address
             * @param value Transfer amount
             */
            function _transfer(
                address from,
                address to,
                uint256 value
            ) internal override {
                require(from != address(0), "ERC20: transfer from the zero address");
                require(to != address(0), "ERC20: transfer to the zero address");
                require(
                    value <= balances[from],
                    "ERC20: transfer amount exceeds balance"
                );
        
                balances[from] = balances[from].sub(value);
                balances[to] = balances[to].add(value);
                emit Transfer(from, to, value);
            }
        
            /**
             * @dev Function to add/update a new minter
             * @param minter The address of the minter
             * @param minterAllowedAmount The minting amount allowed for the minter
             * @return True if the operation was successful.
             */
            function configureMinter(address minter, uint256 minterAllowedAmount)
                external
                whenNotPaused
                onlyMasterMinter
                returns (bool)
            {
                minters[minter] = true;
                minterAllowed[minter] = minterAllowedAmount;
                emit MinterConfigured(minter, minterAllowedAmount);
                return true;
            }
        
            /**
             * @dev Function to remove a minter
             * @param minter The address of the minter to remove
             * @return True if the operation was successful.
             */
            function removeMinter(address minter)
                external
                onlyMasterMinter
                returns (bool)
            {
                minters[minter] = false;
                minterAllowed[minter] = 0;
                emit MinterRemoved(minter);
                return true;
            }
        
            /**
             * @dev allows a minter to burn some of its own tokens
             * Validates that caller is a minter and that sender is not blacklisted
             * amount is less than or equal to the minter's account balance
             * @param _amount uint256 the amount of tokens to be burned
             */
            function burn(uint256 _amount)
                external
                whenNotPaused
                onlyMinters
                notBlacklisted(msg.sender)
            {
                uint256 balance = balances[msg.sender];
                require(_amount > 0, "FiatToken: burn amount not greater than 0");
                require(balance >= _amount, "FiatToken: burn amount exceeds balance");
        
                totalSupply_ = totalSupply_.sub(_amount);
                balances[msg.sender] = balance.sub(_amount);
                emit Burn(msg.sender, _amount);
                emit Transfer(msg.sender, address(0), _amount);
            }
        
            function updateMasterMinter(address _newMasterMinter) external onlyOwner {
                require(
                    _newMasterMinter != address(0),
                    "FiatToken: new masterMinter is the zero address"
                );
                masterMinter = _newMasterMinter;
                emit MasterMinterChanged(masterMinter);
            }
        }
        
        // File: @openzeppelin/contracts/utils/Address.sol
        
        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"
                );
            }
        
            /**
             * @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);
                    }
                }
            }
        }
        
        // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
        
        pragma solidity ^0.6.0;
        
        /**
         * @title SafeERC20
         * @dev 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)
                );
            }
        
            /**
             * @dev Deprecated. This function has issues similar to the ones found in
             * {IERC20-approve}, and its usage is discouraged.
             *
             * Whenever possible, use {safeIncreaseAllowance} and
             * {safeDecreaseAllowance} instead.
             */
            function safeApprove(
                IERC20 token,
                address spender,
                uint256 value
            ) internal {
                // safeApprove should only be called when setting an initial allowance,
                // or when resetting it to zero. To increase and decrease it, use
                // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                // solhint-disable-next-line max-line-length
                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 safeIncreaseAllowance(
                IERC20 token,
                address spender,
                uint256 value
            ) internal {
                uint256 newAllowance = token.allowance(address(this), spender).add(
                    value
                );
                _callOptionalReturn(
                    token,
                    abi.encodeWithSelector(
                        token.approve.selector,
                        spender,
                        newAllowance
                    )
                );
            }
        
            function safeDecreaseAllowance(
                IERC20 token,
                address spender,
                uint256 value
            ) internal {
                uint256 newAllowance = token.allowance(address(this), spender).sub(
                    value,
                    "SafeERC20: decreased allowance below zero"
                );
                _callOptionalReturn(
                    token,
                    abi.encodeWithSelector(
                        token.approve.selector,
                        spender,
                        newAllowance
                    )
                );
            }
        
            /**
             * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
             * on the return value: the return value is optional (but if data is returned, it must not be false).
             * @param token The token targeted by the call.
             * @param data The call data (encoded using abi.encode or one of its variants).
             */
            function _callOptionalReturn(IERC20 token, bytes memory data) private {
                // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                // the target address contains contract code and also asserts for success in the low-level call.
        
                bytes memory returndata = address(token).functionCall(
                    data,
                    "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"
                    );
                }
            }
        }
        
        // File: contracts/v1.1/Rescuable.sol
        
        /**
         * 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;
        
        contract Rescuable is Ownable {
            using SafeERC20 for IERC20;
        
            address private _rescuer;
        
            event RescuerChanged(address indexed newRescuer);
        
            /**
             * @notice Returns current rescuer
             * @return Rescuer's address
             */
            function rescuer() external view returns (address) {
                return _rescuer;
            }
        
            /**
             * @notice Revert if called by any account other than the rescuer.
             */
            modifier onlyRescuer() {
                require(msg.sender == _rescuer, "Rescuable: caller is not the rescuer");
                _;
            }
        
            /**
             * @notice Rescue ERC20 tokens locked up in this contract.
             * @param tokenContract ERC20 token contract address
             * @param to        Recipient address
             * @param amount    Amount to withdraw
             */
            function rescueERC20(
                IERC20 tokenContract,
                address to,
                uint256 amount
            ) external onlyRescuer {
                tokenContract.safeTransfer(to, amount);
            }
        
            /**
             * @notice Assign the rescuer role to a given address.
             * @param newRescuer New rescuer's address
             */
            function updateRescuer(address newRescuer) external onlyOwner {
                require(
                    newRescuer != address(0),
                    "Rescuable: new rescuer is the zero address"
                );
                _rescuer = newRescuer;
                emit RescuerChanged(newRescuer);
            }
        }
        
        // File: contracts/v1.1/FiatTokenV1_1.sol
        
        /**
         * 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;
        
        /**
         * @title FiatTokenV1_1
         * @dev ERC20 Token backed by fiat reserves
         */
        contract FiatTokenV1_1 is FiatTokenV1, Rescuable {
        
        }
        
        // File: contracts/v2/AbstractFiatTokenV2.sol
        
        /**
         * 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 AbstractFiatTokenV2 is AbstractFiatTokenV1 {
            function _increaseAllowance(
                address owner,
                address spender,
                uint256 increment
            ) internal virtual;
        
            function _decreaseAllowance(
                address owner,
                address spender,
                uint256 decrement
            ) internal virtual;
        }
        
        // File: contracts/util/ECRecover.sol
        
        /**
         * Copyright (c) 2016-2019 zOS Global Limited
         * 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;
        
        /**
         * @title ECRecover
         * @notice A library that provides a safe ECDSA recovery function
         */
        library ECRecover {
            /**
             * @notice Recover signer's address from a signed message
             * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol
             * Modifications: Accept v, r, and s as separate arguments
             * @param digest    Keccak-256 hash digest of the signed message
             * @param v         v of the signature
             * @param r         r of the signature
             * @param s         s of the signature
             * @return Signer address
             */
            function recover(
                bytes32 digest,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal pure returns (address) {
                // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
                // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                //
                // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                // these malleable signatures as well.
                if (
                    uint256(s) >
                    0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
                ) {
                    revert("ECRecover: invalid signature 's' value");
                }
        
                if (v != 27 && v != 28) {
                    revert("ECRecover: invalid signature 'v' value");
                }
        
                // If the signature is valid (and not malleable), return the signer address
                address signer = ecrecover(digest, v, r, s);
                require(signer != address(0), "ECRecover: invalid signature");
        
                return signer;
            }
        }
        
        // File: contracts/util/EIP712.sol
        
        /**
         * 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;
        
        /**
         * @title EIP712
         * @notice A library that provides EIP712 helper functions
         */
        library EIP712 {
            /**
             * @notice Make EIP712 domain separator
             * @param name      Contract name
             * @param version   Contract version
             * @return Domain separator
             */
            function makeDomainSeparator(string memory name, string memory version)
                internal
                view
                returns (bytes32)
            {
                uint256 chainId;
                assembly {
                    chainId := chainid()
                }
                return
                    keccak256(
                        abi.encode(
                            // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
                            0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                            keccak256(bytes(name)),
                            keccak256(bytes(version)),
                            chainId,
                            address(this)
                        )
                    );
            }
        
            /**
             * @notice Recover signer's address from a EIP712 signature
             * @param domainSeparator   Domain separator
             * @param v                 v of the signature
             * @param r                 r of the signature
             * @param s                 s of the signature
             * @param typeHashAndData   Type hash concatenated with data
             * @return Signer's address
             */
            function recover(
                bytes32 domainSeparator,
                uint8 v,
                bytes32 r,
                bytes32 s,
                bytes memory typeHashAndData
            ) internal pure returns (address) {
                bytes32 digest = keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        domainSeparator,
                        keccak256(typeHashAndData)
                    )
                );
                return ECRecover.recover(digest, v, r, s);
            }
        }
        
        // File: contracts/v2/EIP712Domain.sol
        
        /**
         * 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;
        
        /**
         * @title EIP712 Domain
         */
        contract EIP712Domain {
            /**
             * @dev EIP712 Domain Separator
             */
            bytes32 public DOMAIN_SEPARATOR;
        }
        
        // File: contracts/v2/EIP3009.sol
        
        /**
         * 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;
        
        /**
         * @title EIP-3009
         * @notice Provide internal implementation for gas-abstracted transfers
         * @dev Contracts that inherit from this must wrap these with publicly
         * accessible functions, optionally adding modifiers where necessary
         */
        abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain {
            // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
            bytes32
                public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;
        
            // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
            bytes32
                public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;
        
            // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
            bytes32
                public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;
        
            /**
             * @dev authorizer address => nonce => bool (true if nonce is used)
             */
            mapping(address => mapping(bytes32 => bool)) private _authorizationStates;
        
            event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
            event AuthorizationCanceled(
                address indexed authorizer,
                bytes32 indexed nonce
            );
        
            /**
             * @notice Returns the state of an authorization
             * @dev Nonces are randomly generated 32-byte data unique to the
             * authorizer's address
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             * @return True if the nonce is used
             */
            function authorizationState(address authorizer, bytes32 nonce)
                external
                view
                returns (bool)
            {
                return _authorizationStates[authorizer][nonce];
            }
        
            /**
             * @notice Execute a transfer with a signed authorization
             * @param from          Payer's address (Authorizer)
             * @param to            Payee's address
             * @param value         Amount to be transferred
             * @param validAfter    The time after which this is valid (unix time)
             * @param validBefore   The time before which this is valid (unix time)
             * @param nonce         Unique nonce
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function _transferWithAuthorization(
                address from,
                address to,
                uint256 value,
                uint256 validAfter,
                uint256 validBefore,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                _requireValidAuthorization(from, nonce, validAfter, validBefore);
        
                bytes memory data = abi.encode(
                    TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
                    from,
                    to,
                    value,
                    validAfter,
                    validBefore,
                    nonce
                );
                require(
                    EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from,
                    "FiatTokenV2: invalid signature"
                );
        
                _markAuthorizationAsUsed(from, nonce);
                _transfer(from, to, value);
            }
        
            /**
             * @notice Receive a transfer with a signed authorization from the payer
             * @dev This has an additional check to ensure that the payee's address
             * matches the caller of this function to prevent front-running attacks.
             * @param from          Payer's address (Authorizer)
             * @param to            Payee's address
             * @param value         Amount to be transferred
             * @param validAfter    The time after which this is valid (unix time)
             * @param validBefore   The time before which this is valid (unix time)
             * @param nonce         Unique nonce
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function _receiveWithAuthorization(
                address from,
                address to,
                uint256 value,
                uint256 validAfter,
                uint256 validBefore,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                require(to == msg.sender, "FiatTokenV2: caller must be the payee");
                _requireValidAuthorization(from, nonce, validAfter, validBefore);
        
                bytes memory data = abi.encode(
                    RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
                    from,
                    to,
                    value,
                    validAfter,
                    validBefore,
                    nonce
                );
                require(
                    EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from,
                    "FiatTokenV2: invalid signature"
                );
        
                _markAuthorizationAsUsed(from, nonce);
                _transfer(from, to, value);
            }
        
            /**
             * @notice Attempt to cancel an authorization
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function _cancelAuthorization(
                address authorizer,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                _requireUnusedAuthorization(authorizer, nonce);
        
                bytes memory data = abi.encode(
                    CANCEL_AUTHORIZATION_TYPEHASH,
                    authorizer,
                    nonce
                );
                require(
                    EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == authorizer,
                    "FiatTokenV2: invalid signature"
                );
        
                _authorizationStates[authorizer][nonce] = true;
                emit AuthorizationCanceled(authorizer, nonce);
            }
        
            /**
             * @notice Check that an authorization is unused
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             */
            function _requireUnusedAuthorization(address authorizer, bytes32 nonce)
                private
                view
            {
                require(
                    !_authorizationStates[authorizer][nonce],
                    "FiatTokenV2: authorization is used or canceled"
                );
            }
        
            /**
             * @notice Check that authorization is valid
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             * @param validAfter    The time after which this is valid (unix time)
             * @param validBefore   The time before which this is valid (unix time)
             */
            function _requireValidAuthorization(
                address authorizer,
                bytes32 nonce,
                uint256 validAfter,
                uint256 validBefore
            ) private view {
                require(
                    now > validAfter,
                    "FiatTokenV2: authorization is not yet valid"
                );
                require(now < validBefore, "FiatTokenV2: authorization is expired");
                _requireUnusedAuthorization(authorizer, nonce);
            }
        
            /**
             * @notice Mark an authorization as used
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             */
            function _markAuthorizationAsUsed(address authorizer, bytes32 nonce)
                private
            {
                _authorizationStates[authorizer][nonce] = true;
                emit AuthorizationUsed(authorizer, nonce);
            }
        }
        
        // File: contracts/v2/EIP2612.sol
        
        /**
         * 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;
        
        /**
         * @title EIP-2612
         * @notice Provide internal implementation for gas-abstracted approvals
         */
        abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain {
            // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
            bytes32
                public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
        
            mapping(address => uint256) private _permitNonces;
        
            /**
             * @notice Nonces for permit
             * @param owner Token owner's address (Authorizer)
             * @return Next nonce
             */
            function nonces(address owner) external view returns (uint256) {
                return _permitNonces[owner];
            }
        
            /**
             * @notice Verify a signed approval permit and execute if valid
             * @param owner     Token owner's address (Authorizer)
             * @param spender   Spender's address
             * @param value     Amount of allowance
             * @param deadline  The time at which this expires (unix time)
             * @param v         v of the signature
             * @param r         r of the signature
             * @param s         s of the signature
             */
            function _permit(
                address owner,
                address spender,
                uint256 value,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                require(deadline >= now, "FiatTokenV2: permit is expired");
        
                bytes memory data = abi.encode(
                    PERMIT_TYPEHASH,
                    owner,
                    spender,
                    value,
                    _permitNonces[owner]++,
                    deadline
                );
                require(
                    EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == owner,
                    "EIP2612: invalid signature"
                );
        
                _approve(owner, spender, value);
            }
        }
        
        // File: contracts/v2/FiatTokenV2.sol
        
        /**
         * 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;
        
        /**
         * @title FiatToken V2
         * @notice ERC20 Token backed by fiat reserves, version 2
         */
        contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 {
            uint8 internal _initializedVersion;
        
            /**
             * @notice Initialize v2
             * @param newName   New token name
             */
            function initializeV2(string calldata newName) external {
                // solhint-disable-next-line reason-string
                require(initialized && _initializedVersion == 0);
                name = newName;
                DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(newName, "2");
                _initializedVersion = 1;
            }
        
            /**
             * @notice Increase the allowance by a given increment
             * @param spender   Spender's address
             * @param increment Amount of increase in allowance
             * @return True if successful
             */
            function increaseAllowance(address spender, uint256 increment)
                external
                whenNotPaused
                notBlacklisted(msg.sender)
                notBlacklisted(spender)
                returns (bool)
            {
                _increaseAllowance(msg.sender, spender, increment);
                return true;
            }
        
            /**
             * @notice Decrease the allowance by a given decrement
             * @param spender   Spender's address
             * @param decrement Amount of decrease in allowance
             * @return True if successful
             */
            function decreaseAllowance(address spender, uint256 decrement)
                external
                whenNotPaused
                notBlacklisted(msg.sender)
                notBlacklisted(spender)
                returns (bool)
            {
                _decreaseAllowance(msg.sender, spender, decrement);
                return true;
            }
        
            /**
             * @notice Execute a transfer with a signed authorization
             * @param from          Payer's address (Authorizer)
             * @param to            Payee's address
             * @param value         Amount to be transferred
             * @param validAfter    The time after which this is valid (unix time)
             * @param validBefore   The time before which this is valid (unix time)
             * @param nonce         Unique nonce
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function transferWithAuthorization(
                address from,
                address to,
                uint256 value,
                uint256 validAfter,
                uint256 validBefore,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                _transferWithAuthorization(
                    from,
                    to,
                    value,
                    validAfter,
                    validBefore,
                    nonce,
                    v,
                    r,
                    s
                );
            }
        
            /**
             * @notice Receive a transfer with a signed authorization from the payer
             * @dev This has an additional check to ensure that the payee's address
             * matches the caller of this function to prevent front-running attacks.
             * @param from          Payer's address (Authorizer)
             * @param to            Payee's address
             * @param value         Amount to be transferred
             * @param validAfter    The time after which this is valid (unix time)
             * @param validBefore   The time before which this is valid (unix time)
             * @param nonce         Unique nonce
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function receiveWithAuthorization(
                address from,
                address to,
                uint256 value,
                uint256 validAfter,
                uint256 validBefore,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                _receiveWithAuthorization(
                    from,
                    to,
                    value,
                    validAfter,
                    validBefore,
                    nonce,
                    v,
                    r,
                    s
                );
            }
        
            /**
             * @notice Attempt to cancel an authorization
             * @dev Works only if the authorization is not yet used.
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function cancelAuthorization(
                address authorizer,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external whenNotPaused {
                _cancelAuthorization(authorizer, nonce, v, r, s);
            }
        
            /**
             * @notice Update allowance with a signed permit
             * @param owner       Token owner's address (Authorizer)
             * @param spender     Spender's address
             * @param value       Amount of allowance
             * @param deadline    Expiration time, seconds since the epoch
             * @param v           v of the signature
             * @param r           r of the signature
             * @param s           s of the signature
             */
            function permit(
                address owner,
                address spender,
                uint256 value,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external whenNotPaused notBlacklisted(owner) notBlacklisted(spender) {
                _permit(owner, spender, value, deadline, v, r, s);
            }
        
            /**
             * @notice Internal function to increase the allowance by a given increment
             * @param owner     Token owner's address
             * @param spender   Spender's address
             * @param increment Amount of increase
             */
            function _increaseAllowance(
                address owner,
                address spender,
                uint256 increment
            ) internal override {
                _approve(owner, spender, allowed[owner][spender].add(increment));
            }
        
            /**
             * @notice Internal function to decrease the allowance by a given decrement
             * @param owner     Token owner's address
             * @param spender   Spender's address
             * @param decrement Amount of decrease
             */
            function _decreaseAllowance(
                address owner,
                address spender,
                uint256 decrement
            ) internal override {
                _approve(
                    owner,
                    spender,
                    allowed[owner][spender].sub(
                        decrement,
                        "ERC20: decreased allowance below zero"
                    )
                );
            }
        }
        
        // File: contracts/v2/FiatTokenV2_1.sol
        
        /**
         * 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;
        
        // solhint-disable func-name-mixedcase
        
        /**
         * @title FiatToken V2.1
         * @notice ERC20 Token backed by fiat reserves, version 2.1
         */
        contract FiatTokenV2_1 is FiatTokenV2 {
            /**
             * @notice Initialize v2.1
             * @param lostAndFound  The address to which the locked funds are sent
             */
            function initializeV2_1(address lostAndFound) external {
                // solhint-disable-next-line reason-string
                require(_initializedVersion == 1);
        
                uint256 lockedAmount = balances[address(this)];
                if (lockedAmount > 0) {
                    _transfer(address(this), lostAndFound, lockedAmount);
                }
                blacklisted[address(this)] = true;
        
                _initializedVersion = 2;
            }
        
            /**
             * @notice Version string for the EIP712 domain separator
             * @return Version string
             */
            function version() external view returns (string memory) {
                return "2";
            }
        }