contract ChaiSwap is WrappedTokenSwap
{
  using SafeMath for uint;
  using SafeTransfer for ERC20;

  IChaiToken chai;

  /**
   * @dev Contract constructor.
   * @param _chaiTokenAddress  The chai token address.
   * @param _wallet The wallet for fees.
   * @param _spread The initial spread (in millionths).
   */
  constructor(
    address _chaiTokenAddress,
    address _wallet,
    uint _spread
  )
    public WrappedTokenSwap(_wallet, _spread)
  {
    chai = IChaiToken(_chaiTokenAddress);
  }


  // --- Math (from chai contract) ---
  uint constant RAY = 10 ** 27;
  function add(uint x, uint y) internal pure returns (uint z) {
    require((z = x + y) >= x);
  }
  function sub(uint x, uint y) internal pure returns (uint z) {
    require((z = x - y) <= x);
  }
  function mul(uint x, uint y) internal pure returns (uint z) {
    require(y == 0 || (z = x * y) / y == x);
  }
  function rmul(uint x, uint y) internal pure returns (uint z) {
    // always rounds down
    z = mul(x, y) / RAY;
  }
  function rdiv(uint x, uint y) internal pure returns (uint z) {
    // always rounds down
    z = mul(x, RAY) / y;
  }
  function rdivup(uint x, uint y) internal pure returns (uint z) {
    // always rounds up
    z = add(mul(x, RAY), sub(y, 1)) / y;
  }

  // --- override abstract methods from WrappedTokenSwap

  function underlyingTokenAddress() public view returns(address) {
    return chai.daiToken();
  }

  function wrappedTokenAddress() public view returns(address) {
    return address(chai);
  }

  function wrap(uint unwrappedAmount) private returns(bool) {
    require(
      ERC20(chai.daiToken())._safeApprove(address(chai), unwrappedAmount),
      "Cannot approve underlying token for join"
    );
    chai.join(address(this), unwrappedAmount);
    return true;
  }

  function unwrap(uint wrappedAmount) private returns(bool) {
    chai.exit(address(this), wrappedAmount);
    return true;
  }

  function getExchangedAmount(uint wad, bool _isUnwrap) private view returns(uint) {
    PotLike pot = chai.pot();
    uint chi = (now > pot.rho()) ? pot.drip() : pot.chi();

    return _isUnwrap
      ? rmul(chi, wad)
      : rdiv(wad, chi);
  }
}
/**
 * @title TokenSwap.
 * @author Eidoo SAGL.
 * @dev A swap asset contract. The offerAmount and wantAmount are collected and sent into the contract itself.
 */
function underlyingTokenAddress() public view returns(address) {
  return chai.daiToken();
}

// --- override abstract methods from WrappedTokenSwap