contract PooledDSDCoupons is ReentrancyGuard {

  using SafeMath for uint;

  IDSDS public DSDS = IDSDS(0x6Bf977ED1A09214E6209F4EA5f525261f1A2690a);
  address public owner;

  uint public totalSupply;
  string public constant name = "DSD Coupon Pool 3";
  string public constant symbol = "DPOOL3";
  uint8 public constant decimals = 18;
  uint public constant WRAP_FEE_BPS = 200;

  mapping (address => uint) public balanceOf;
  mapping (address => mapping (address => uint)) public allowance;

  event Transfer(address indexed from, address indexed to, uint value);
  event Approval(address indexed owner, address indexed spender, uint value);

  constructor() public {
    owner = msg.sender;
  }

  function wrap(uint _epoch, uint _amount) public nonReentrant {
    uint expiryEpoch = DSDS.couponsExpiration(_epoch);  //safe to assume this is 360 more epochs for all old coupons but we call this, anyway
    uint expiresIn = 0; //assume already expired as default
    if (DSDS.epoch() < expiryEpoch) expiresIn = expiryEpoch.sub(DSDS.epoch()); // calcuate epochs remaining for non-expired (must be expiring in the next 12 epochs)
    
    require(expiresIn <= 12, "PooledDSDCoupons: coupon must be expired or expiring in less than 12 epochs");

    //fee 2% fixed
    uint fee = _amount.mul(WRAP_FEE_BPS).div(10000);
    _mint(owner, fee);
    _mint(msg.sender, _amount.sub(fee));
    DSDS.transferCoupons(msg.sender, address(this), _epoch, _amount);
  }

  function unwrap(uint _epoch, uint _amount) public nonReentrant {
    _burn(msg.sender, _amount);
    DSDS.transferCoupons(address(this), msg.sender, _epoch, _amount);
  }

  // ERC20 functions

  function approve(address _spender, uint _amount) public returns (bool) {
    _approve(msg.sender, _spender, _amount);
    return true;
  }

  function transfer(address _recipient, uint _amount) public returns (bool) {
    _transfer(msg.sender, _recipient, _amount);
    return true;
  }

  function transferFrom(address _sender, address _recipient, uint _amount) public returns (bool) {
    _transfer(_sender, _recipient, _amount);
    _approve(_sender, msg.sender, allowance[_sender][msg.sender].sub(_amount, "ERC20: transfer amount exceeds allowance"));
    return true;
  }

  function _transfer(
    address _sender,
    address _recipient,
    uint _amount
  ) internal {
    require(_sender != address(0), "ERC20: transfer from the zero address");
    require(_recipient != address(0), "ERC20: transfer to the zero address");

    balanceOf[_sender] = balanceOf[_sender].sub(_amount, "ERC20: transfer amount exceeds balance");
    balanceOf[_recipient] = balanceOf[_recipient].add(_amount);

    emit Transfer(_sender, _recipient, _amount);
  }

  function _mint(address _account, uint _amount) internal {
    require(_account != address(0), "ERC20: mint to the zero address");

    totalSupply = totalSupply.add(_amount);
    balanceOf[_account] = balanceOf[_account].add(_amount);
    emit Transfer(address(0), _account, _amount);
  }

  function _burn(address _account, uint _amount) internal {
    require(_account != address(0), "ERC20: burn to the zero address");

    totalSupply = totalSupply.sub(_amount);
    balanceOf[_account] = balanceOf[_account].sub(_amount);
    emit Transfer(_account, address(0), _amount);
  }

  function _approve(address _owner, address _spender, uint _amount) internal {
    require(_owner != address(0), "ERC20: approve from the zero address");
    require(_spender != address(0), "ERC20: approve to the zero address");

    allowance[_owner][_spender] = _amount;
    emit Approval(_owner, _spender, _amount);
  }
}
function approve(address _spender, uint _amount) public returns (bool) {
  _approve(msg.sender, _spender, _amount);
  return true;
}

// ERC20 functions