contract SUSD is StandardToken {
    using SafeMathUint256 for uint256;

    bytes32 constant FREEZE_CODE_DEFAULT = 0x0000000000000000000000000000000000000000000000000000000000000000;

    event Freeze(address indexed from, uint256 value);
    event Unfreeze(address indexed from, uint256 value);

    event FreezeWithPurpose(address indexed from, uint256 value, bytes32 purpose);
    event UnfreezeWithPurpose(address indexed from, uint256 value, bytes32 purpose);

    string public name;
    string public symbol;
    uint8 public decimals;

    // Keep track total frozen balances
    mapping (address => uint256) public freezeOf;
    // Keep track sub total frozen balances
    mapping (address => mapping (bytes32 => uint256)) public freezes;

    constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _initialSupply) public
        BasicToken(_initialSupply)
    {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
    }

    /**
     * @dev Increase total supply (mint) to an address
     *
     * @param _value The amount of tokens to be mint
     * @param _to The address which will receive token
     */
    function increaseSupply(uint256 _value, address _to) external
        onlyOwner
        onlyActive(_to)
        returns (bool)
    {
        require(0 < _value, "StableCoin.increaseSupply: Zero value");

        totalSupply = totalSupply.add(_value);
        balances[_to] = balances[_to].add(_value);
        emit Transfer(address(0), _to, _value);
        return true;
    }

    /**
     * @dev Increase total supply (mint) to an address with deposit
     *
     * @param _value The amount of tokens to be mint
     * @param _to The address which will receive token
     * @param _deposit The amount of deposit
     */
    function increaseSupplyWithDeposit(uint256 _value, address _to, uint256 _deposit) external
        onlyOwner
        onlyActive(_to)
        returns (bool)
    {
        require(0 < _value, "StableCoin.increaseSupplyWithDeposit: Zero value");
        require(_deposit <= _value, "StableCoin.increaseSupplyWithDeposit: Insufficient deposit");

        totalSupply = totalSupply.add(_value);
        balances[_to] = balances[_to].add(_value);
        freezeWithPurposeCode(_to, _deposit, encodePacked("InitialDeposit"));
        emit Transfer(address(0), _to, _value.sub(_deposit));
        return true;
    }

    /**
     * @dev Decrease total supply (burn) from an address that gave allowance
     *
     * @param _value The amount of tokens to be burn
     * @param _from The address's token will be burn
     */
    function decreaseSupply(uint256 _value, address _from) external
        onlyOwner
        onlyActive(_from)
        returns (bool)
    {
        require(0 < _value, "StableCoin.decreaseSupply: Zero value");
        require(_value <= balances[_from], "StableCoin.decreaseSupply: Insufficient fund");
        require(_value <= allowed[_from][address(0)], "StableCoin.decreaseSupply: Insufficient allowance");

        totalSupply = totalSupply.sub(_value);
        balances[_from] = balances[_from].sub(_value);
        allowed[_from][address(0)] = allowed[_from][address(0)].sub(_value);
        emit Transfer(_from, address(0), _value);
        return true;
    }
	
    /**
    * @dev Freeze holder balance
    *
    * @param _from The address which will be freeze
    * @param _value The amount of tokens to be freeze
    */
    function freeze(address _from, uint256 _value) external
        onlyOwner
        returns (bool)
    {
        require(_value <= balances[_from], "StableCoin.freeze: Insufficient fund");

        balances[_from] = balances[_from].sub(_value);
        freezeOf[_from] = freezeOf[_from].add(_value);
        freezes[_from][FREEZE_CODE_DEFAULT] = freezes[_from][FREEZE_CODE_DEFAULT].add(_value);
        emit Freeze(_from, _value);
        emit FreezeWithPurpose(_from, _value, FREEZE_CODE_DEFAULT);
        return true;
    }
	
    /**
    * @dev Freeze holder balance with purpose code
    *
    * @param _from The address which will be freeze
    * @param _value The amount of tokens to be freeze
    * @param _purpose The purpose of freeze
    */
    function freezeWithPurpose(address _from, uint256 _value, string calldata _purpose) external
        returns (bool)
    {
        return freezeWithPurposeCode(_from, _value, encodePacked(_purpose));
    }
	
    /**
    * @dev Freeze holder balance with purpose code
    *
    * @param _from The address which will be freeze
    * @param _value The amount of tokens to be freeze
    * @param _purposeCode The purpose code of freeze
    */
    function freezeWithPurposeCode(address _from, uint256 _value, bytes32 _purposeCode) public
        onlyOwner
        returns (bool)
    {
        require(_value <= balances[_from], "StableCoin.freezeWithPurposeCode: Insufficient fund");

        balances[_from] = balances[_from].sub(_value);
        freezeOf[_from] = freezeOf[_from].add(_value);
        freezes[_from][_purposeCode] = freezes[_from][_purposeCode].add(_value);
        emit Freeze(_from, _value);
        emit FreezeWithPurpose(_from, _value, _purposeCode);
        return true;
    }
	
    /**
    * @dev Unfreeze holder balance
    *
    * @param _from The address which will be unfreeze
    * @param _value The amount of tokens to be unfreeze
    */
    function unfreeze(address _from, uint256 _value) external
        onlyOwner
        returns (bool)
    {
        require(_value <= freezes[_from][FREEZE_CODE_DEFAULT], "StableCoin.unfreeze: Insufficient fund");

        freezeOf[_from] = freezeOf[_from].sub(_value);
        freezes[_from][FREEZE_CODE_DEFAULT] = freezes[_from][FREEZE_CODE_DEFAULT].sub(_value);
        balances[_from] = balances[_from].add(_value);
        emit Unfreeze(_from, _value);
        emit UnfreezeWithPurpose(_from, _value, FREEZE_CODE_DEFAULT);
        return true;
    }

    /**
    * @dev Unfreeze holder balance with purpose code
    *
    * @param _from The address which will be unfreeze
    * @param _value The amount of tokens to be unfreeze
    * @param _purpose The purpose of unfreeze
    */
    function unfreezeWithPurpose(address _from, uint256 _value, string calldata _purpose) external
        onlyOwner
        returns (bool)
    {
        return unfreezeWithPurposeCode(_from, _value, encodePacked(_purpose));
    }

    /**
    * @dev Unfreeze holder balance with purpose code
    *
    * @param _from The address which will be unfreeze
    * @param _value The amount of tokens to be unfreeze
    * @param _purposeCode The purpose code of unfreeze
    */
    function unfreezeWithPurposeCode(address _from, uint256 _value, bytes32 _purposeCode) public
        onlyOwner
        returns (bool)
    {
        require(_value <= freezes[_from][_purposeCode], "StableCoin.unfreezeWithPurposeCode: Insufficient fund");

        freezeOf[_from] = freezeOf[_from].sub(_value);
        freezes[_from][_purposeCode] = freezes[_from][_purposeCode].sub(_value);
        balances[_from] = balances[_from].add(_value);
        emit Unfreeze(_from, _value);
        emit UnfreezeWithPurpose(_from, _value, _purposeCode);
        return true;
    }

    /**
     * @dev Allocate allowance and perform contract call
     *
     * @param _spender The spender address
     * @param _value The allowance value
     * @param _extraData The function call data
     */
    function approveAndCall(address _spender, uint256 _value, bytes calldata _extraData) external
        isUsable
        returns (bool)
    {
        // Give allowance to spender (previous approved allowances will be clear)
        approve(_spender, _value);

        ApprovalReceiver(_spender).receiveApproval(msg.sender, _value, address(this), _extraData);
        return true;
    }

    function encodePacked(string memory s) internal pure
        returns (bytes32)
    {
        return keccak256(abi.encodePacked(s));
    }

}
function unfreezeWithPurpose(address _from, uint256 _value, string calldata _purpose) external
    onlyOwner
    returns (bool)
{
    return unfreezeWithPurposeCode(_from, _value, encodePacked(_purpose));
}

/**
 * @dev Unfreeze holder balance with purpose code
 *
 * @param _from The address which will be unfreeze
 * @param _value The amount of tokens to be unfreeze
 * @param _purpose The purpose of unfreeze
 */