// Contract: OneEUR
// Target function: function exec(address _to,bytes calldata _data) payable external onlyOwner returns (bool, bytes memory)
 (6 decimal places), 1000000=1EUR
    uint40 private _bondBuyStart; // start of subscription
    uint40 private _bondBuyEnd; // end of subscription
    uint40 private _bondMature; // maturity date
    string private _www; // web page for more info on how to buy and sell 1EUR

    mapping (address => uint256) private _minterAllowances;
    address[] private _bonds;

    constructor () public {
        _name = "1EUR stablecoin";
        _symbol = "1EUR";
        _decimals = 6;
        _serial =  -1;
    }
    function init(string calldata _newname,string calldata _newsymbol,uint8 _newdecimals,int32 _newserial,uint256 _limit,uint32 _price,uint40 _start, uint40 _end,uint40 _mature) external returns (bool) {
	require(bytes(_name).length == 0);
	_name=_newname;
	_symbol=_newsymbol;
	_decimals=_newdecimals;
	_serial=_newserial;
	_bondPrice=_price;
	_bondBuyStart=_start;
	_bondBuyEnd=_end;
	_bondMature=_mature;
	_addOwner(_msgSender());
	_addMinter(_msgSender());
	_minterApprove(_msgSender(),_limit);
	return true;
    }
    function name() public view returns (string memory) {
        return _name;
    }
    function symbol() public view returns (string memory) {
        return _symbol;
    }
    function decimals() public view returns (uint) {
        return _decimals;
    }
    /**
     * @dev Bond serial number, starts with 0, -1 for 1EUR base token (no bond)
     * @return int, -1 for base token (1EUR)
     */
    function serialNumber() public view returns (int) {
        return _serial;
    }
    /**
     * @dev Circulating supply = totalSupply - funds in bonds
     * @return uint
     */
    function circulatingSupply() public view returns (uint) {
        return totalSupply().sub(_bondedSupply);
    }
    /**
     * @dev Bond price with 6 decimals (1000000 = 1.000000 EUR base token)
     * @return uint
     */
    function bondPrice() public view returns (uint) {
        return _bondPrice;
    }
    /**
     * @dev Bond can be purchased after this date
     * @return uint
     */
    function bondBuyStart() public view returns (uint) {
        return _bondBuyStart;
    }
    /**
     * @dev Bond can be purchased before this date
     * @return uint
     */
    function bondBuyEnd() public view returns (uint) {
        return _bondBuyEnd;
    }
    /**
     * @dev Bond is mature after this date, funds can be retreived with redeemBond
     * @return uint
     */
    function bondMature() public view returns (uint) {
        return _bondMature;
    }
    /**
     * @dev Web page with info on how to buy and sell 1EUR via SEPA
     * @return string
     */
    function www() public view returns (string memory) {
        return _www;
    }
    /**
     * @dev Returns contract address of the bond[n]
     * @param n serial number of bond, starting with 0
     * @return address
     */
    function bondAddress(uint256 n) public view returns (address) {
        return _bonds[n];
    }
    /**
     * @dev View first Bond with serial number >= n with invested funds owned by provided address
     * @param n minium serial number of bond, >= 0
     * @param owner address of the owner
     * @return int serial number of bond
     */
    function myBond(uint256 n,address owner) public view returns (int) {
	for (uint i=n; i < _bonds.length; i++) {
          OneEUR nbond = OneEUR(address(_bonds[i]));
          if(nbond.balanceOf(owner)>0){
            return int(i);}}
        return -1; // no bonds with assets
    }
    /**
     * @dev View first mature Bond with serial number >= n with invested funds owned by provided address
     * @param n minium serial number of bond, >= 0
     * @param owner address of the owner
     * @return int serial number of bond
     */
    function myMatureBond(uint256 n,address owner) public view returns (int) {
	for (uint i=n; i < _bonds.length; i++) {
          OneEUR nbond = OneEUR(address(_bonds[i]));
          if(nbond.balanceOf(owner)>0 && nbond.bondMature()<block.timestamp){
            return int(i);}}
        return -1; // no mature bond available
    }
    /**
     * @dev view first active Bond with serial number >= n (bond can be purchased now)
     * @param n minium serial number of bond, >= 0
     * @return int serial number of bond
     */
    function activeBond(uint256 n) public view returns (int) {
	for (uint i=n; i < _bonds.length; i++) {
          OneEUR nbond = OneEUR(address(_bonds[i]));
          if(nbond.bondBuyStart()<block.timestamp && block.timestamp<nbond.bondBuyEnd()){
            return int(i);}}
        return -1;
    }
    /**
     * @dev view number of bonds and total outstanding balance
     * @return uint number of bonds
     * @return uint sum of totalSupply
     */
    function bondTotalSupply() public view returns (uint256 num,uint256 total) {
	for (num=0; num < _bonds.length; num++) {
          OneEUR nbond = OneEUR(address(_bonds[num]));
          total+=nbond.totalSupply();}
    }
    /**
     * @dev view parameters of Bond with serial number n
     * @return address address of bonds
     * @return uint256 bonds bought (totalSupply)
     * @return uint256 bonds available (if bond active)
     * @return uint256 bond price (6 decimals)
     * @return uint256 bond buy start
     * @return uint256 bond buy end
     * @return uint256 bond maturity
     * @return string status [error,waiting,active,immature,mature]
     */
    function bondDetails(uint256 n) public view returns (
          address bondaddress,
          uint256 bondsbought,
          uint256 bondsavailable,
          uint256 bondprice,
          uint256 bondbuystart,
          uint256 bondbuyend,
          uint256 bondmature,
          string memory status ) {
        bondaddress = _bonds[n];
        OneEUR nbond = OneEUR(bondaddress);
        bondsbought = nbond.totalSupply(); 
        bondsavailable = nbond.minterAllowance(address(this)); 
        bondprice = nbond.bondPrice();
        bondbuystart = nbond.bondBuyStart();
        bondbuyend = nbond.bondBuyEnd();
        bondmature = nbond.bondMature();
        if(bondbuystart==0){
          status='error';}
        else if(bondbuystart>block.timestamp){
          status='waiting';}
        else if(bondbuyend>block.timestamp){
          status='active';}
        else if(bondmature>block.timestamp){
          status='immature';}
        else{
          status='mature';}
    }
    /**
     * @dev show size of available subscription for Bond with serial number n
     * @param n serial number of bond, starts with zero
     * @return uint size of bond (number of bonds) still available (if bond active)
     */
    function bondAvailable(uint256 n) public view returns (uint256) {
        OneEUR nbond = OneEUR(address(_bonds[n]));
        if(nbond.bondBuyStart()<block.timestamp && block.timestamp<nbond.bondBuyEnd()){
          return nbond.minterAllowance(address(this)); }
        return 0;
    }
    /**
     * @dev buys bond
     * @param n serial number of bond, starts with zero
     * @param amount size of bond (number of bonds) to buy in 1EUR (6 decimal places)
     */
    function buyBond(uint256 n, uint256 amount) external whenNotPaused {
        OneEUR nbond = OneEUR(address(_bonds[n]));
        bool success = nbond.wrapTo(_msgSender(),amount,0);
        require(success);
        uint256 price = amount.mul(nbond.bondPrice()).div(1000000); 
        _tmpburn(_msgSender(),price,amount);
        _bondedSupply+=amount;
    }
    /**
     * @dev redeem bond
     * @param n serial number of bond, starts with zero
     */
    function redeemBond(uint256 n) external whenNotPaused {
        OneEUR nbond = OneEUR(address(_bonds[n]));
        uint256 amount = nbond.unwrapAll(_msgSender());
        require(amount>0);
        _tmpmint(_msgSender(), amount);
        _bondedSupply-= amount;
    }
    /**
     * @dev Set www address, onlyOwner
     * @param newwww URL
     */
    function setWww(string calldata newwww) external onlyOwner returns (bool) {
    	_www=newwww;
    	return true;
    }
    /**
     * @dev Deploy new bond, onlyMinter
     * Bond will wait 1 week before subscription starts.
     * Subscription ends in 2 weeks.
     * @param limit bond size with 6 decimals (1000000 = 1EUR)
     * @param price price of a single 1EUR bond (1000000 = 1EUR), should be < 1000000 && > 100000, 900000 means 10% discount
     * @param numweeks number of weeks (after subscription end) for bond to mature (1 year ~ 52)
     * @return address new bond address
     */
    function deployBond(uint limit,uint price,uint numweeks) external onlyMinter returns (address bondaddress) {
	require(_bondBuyStart==0, "OneEUR: no bonds on bond");
	require(limit>0, "OneEUR: bond size must be > 0");
        require(price<1000000 && price>100000, "OneEUR: discount must be < 90%");
        require(numweeks>3, "OneEUR: bond must mature at least 4 weeks");
        uint start=block.timestamp+1 weeks;
        uint end=start+1 weeks;
        uint mature=end+numweeks*1 weeks;
        _minterApprove(_msgSender(), _minterAllowances[_msgSender()].sub(limit.mul(1000000-price).div(1000000), "OneEUR: bond parameters exceed minterAllowance"));
	bytes20 targetBytes = bytes20(address(this));
	assembly {
	  let clone := mload(0x40)
	  mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
	  mstore(add(clone, 0x14), targetBytes)
	  mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
	  bondaddress := create(0, clone, 0x37)
	}
	emit newBond(address(bondaddress));
	OneEUR nbond = OneEUR(address(bondaddress));
	bool success = nbond.init(string(abi.encodePacked("1EUR Zero-Coupon Bond [",uint2str(_bonds.length),"]")),string(abi.encodePacked("1EURb",uint2str(_bonds.length))),uint8(_decimals),int32(_bonds.length),limit,uint32(price),uint40(start),uint40(end),uint40(mature));
	require(success);
	_bonds.push(address(bondaddress));
    }
    function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
        if (_i == 0) {
            return "0";
        }
        uint j = _i;
        uint len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len - 1;
        while (_i != 0) {
            bstr[k--] = byte(uint8(48 + _i % 10));
            _i /= 10;
        }
        return string(bstr);
    }
    /**
     * @dev wraps received fiat and mint wrapped 1EUR. Logs native txid from SEPA.
     * Or wraps declared 1EUR and mints bond (then txid=0).
     * onlyMinter
     * @param owner owner of the wrapped token or bond
     * @param amount amount of token or bonds
     * @param txid of SEPA transaction
     * @return bool true on success
     */
    function wrapTo(address owner, uint256 amount, uint256 txid) external onlyMinter whenNotPaused returns (bool) {
        require(_bondBuyStart==0 || (_bondBuyStart<block.timestamp && block.timestamp<_bondBuyEnd), "OneEUR: bond subscription not active");
        emit Wrap(owner, txid, amount);
        _mint(owner, amount);
        _minterApprove(_msgSender(), _minterAllowances[_msgSender()].sub(amount, "OneEUR: minted amount exceeds minterAllowance"));
        return true;
    }
    /**
     * @dev Unwrap and destroy amount of 1EUR from the caller. Logs ibanhash to receive fiat over SEPA.
     * @param amount amount of token or bonds
     * @param ibanhash hash of SEPA destination, check web page to obtain correct ibanhash parameter
     */
    function unwrap(uint256 amount, uint256 ibanhash) external whenNotPaused {
        require(_bondBuyStart==0, "OneEUR: no unwrap on bond");
        emit Unwrap(_msgSender(), ibanhash, amount);
        _burn(_msgSender(), amount);
    }
    /**
     * @dev Unwrap and destroy amount of 1EUR from owner. Logs ibanhash to receive fiat over SEPA.
     * @param owner address of the owner
     * @param amount amount of token or bonds
     * @param ibanhash hash of SEPA destination, check web page to obtain correct ibanhash parameter
     */
    function unwrapFrom(address owner, uint256 amount, uint256 ibanhash) external whenNotPaused {
        require(_bondBuyStart==0, "OneEUR: no unwrap on bond");
        emit Unwrap(owner, ibanhash, amount);
        _burnFrom(owner, amount);
    }
    /**
     * @dev Unwrap and destroy all tokens from owner. onlyMinter
     * @param owner address of the owner
     */
    function unwrapAll(address owner) external onlyMinter whenNotPaused returns (uint256 amount) {
        require(_bondBuyStart>0, "OneEUR: this is not a bond");
        require(_bondMature<block.timestamp, "OneEUR: bond not mature yet");
        amount=balanceOf(owner);
        require(amount>0);
        emit Unwrap(owner, 0, amount);
        _burn(owner, amount);
    }
    /**
     * @dev Return allowence of the minter
     * @param minter address of the minter
     */
    function minterAllowance(address minter) public view returns (uint256) {
        return _minterAllowances[minter];
    }
    /**
     * @dev Set the minterAllowance granted to minter
     * @param minter address of the minter
     * @param amount allowence in tokens (6 decimals)
     */
    function minterApprove(address minter, uint256 amount) external onlyOwner {
        _minterApprove(minter, amount);
    }
    /**
     * @dev Increases the minterAllowance granted to minter.
     * @param minter address of the minter
     * @param addedValue allowence in tokens (6 decimals) added to current value
     */
    function increaseMinterAllowance(address minter, uint256 addedValue) external onlyOwner {
        _minterApprove(minter, _minterAllowances[minter].add(addedValue));
    }
    /**
     * @dev Decreases the minterAllowance granted to minter.
     * @param minter address of the minter
     * @param subtractedValue allowence in tokens (6 decimals) subtracted from current value
     */
    function decreaseMinterAllowance(address minter, uint256 subtractedValue) external onlyOwner returns (bool) {
        _minterApprove(minter, _minterAllowances[minter].sub(subtractedValue, "OneEUR: decreased minterAllowance below zero"));
        return true;
    }
    function _minterApprove(address minter, uint256 amount) internal {
        require(isMinter(minter), "OneEUR: minter approve for non-minting address");
        _minterAllowances[minter] = amount;
        emit MinterApproval(minter, amount);
    }
    function isMinter(address account) public view returns (bool) {
        return MinterRole.isMinter(account) || isOwner(account);
    }
    function removeMinter(address account) external onlyOwner {
        _minterApprove(account, 0);
        _removeMinter(account);
    }
    function isPauser(address account) public view returns (bool) {
        return PauserRole.isPauser(account) || isOwner(account);
    }
    function removePauser(address account) external onlyOwner {
        _removePauser(account);
    }