contract WTON is DSMath, ReentrancyGuard, Ownable, ERC20Mintable, ERC20Burnable, ERC20Detailed, SeigToken, OnApprove {
  using SafeERC20 for ERC20Mintable;

  ERC20Mintable public ton;

  constructor (
    ERC20Mintable _ton
  )
    public
    ERC20Detailed("Wrapped TON", "WTON", 27)
  {
    require(ERC20Detailed(address(_ton)).decimals() == 18, "WTON: decimals of TON must be 18");
    ton = _ton;
  }

  //////////////////////
  // TON Approve callback
  //////////////////////

  function onApprove(
    address owner,
    address spender,
    uint256 tonAmount,
    bytes calldata data
  ) external returns (bool) {
    require(msg.sender == address(ton), "WTON: only accept TON approve callback");

    // swap owner's TON to WTON
    _swapFromTON(owner, owner, tonAmount);

    uint256 wtonAmount = _toRAY(tonAmount);
    (address depositManager, address rootchain) = _decodeTONApproveData(data);

    // approve WTON to DepositManager
    _approve(owner, depositManager, wtonAmount);

    // call DepositManager.onApprove to deposit WTON
    bytes memory depositManagerOnApproveData = _encodeDepositManagerOnApproveData(rootchain);
    _callOnApprove(owner, depositManager, wtonAmount, depositManagerOnApproveData);

    return true;
  }

  /**
   * @dev data is 64 bytes of 2 addresses in left-padded 32 bytes
   */
  function _decodeTONApproveData(
    bytes memory data
  ) internal pure returns (address depositManager, address rootchain) {
    require(data.length == 0x40);

    assembly {
      depositManager := mload(add(data, 0x20))
      rootchain := mload(add(data, 0x40))
    }
  }

  function _encodeDepositManagerOnApproveData(
    address rootchain
  ) internal pure returns (bytes memory data) {
    data = new bytes(0x20);

    assembly {
      mstore(add(data, 0x20), rootchain)
    }
  }


  //////////////////////
  // Override ERC20 functions
  //////////////////////

  function burnFrom(address account, uint256 amount) public {
    if (isMinter(msg.sender)) {
      _burn(account, amount);
      return;
    }

    super.burnFrom(account, amount);
  }

  //////////////////////
  // Swap functions
  //////////////////////

  /**
   * @dev swap WTON to TON
   */
  function swapToTON(uint256 wtonAmount) public nonReentrant returns (bool) {
    return _swapToTON(msg.sender, msg.sender, wtonAmount);
  }

  /**
   * @dev swap TON to WTON
   */
  function swapFromTON(uint256 tonAmount) public nonReentrant returns (bool) {
    return _swapFromTON(msg.sender, msg.sender, tonAmount);
  }

  /**
   * @dev swap WTON to TON, and transfer TON
   * NOTE: TON's transfer event's `from` argument is not `msg.sender` but `WTON` address.
   */
  function swapToTONAndTransfer(address to, uint256 wtonAmount) public nonReentrant returns (bool) {
    return _swapToTON(to, msg.sender, wtonAmount);
  }

  /**
   * @dev swap TON to WTON, and transfer WTON
   */
  function swapFromTONAndTransfer(address to, uint256 tonAmount) public nonReentrant returns (bool) {
    return _swapFromTON(msg.sender, to, tonAmount);
  }


  //////////////////////
  // Internal functions
  //////////////////////

  function _swapToTON(address tonAccount, address wtonAccount, uint256 wtonAmount) internal returns (bool) {
    _burn(wtonAccount, wtonAmount);

    // mint TON if WTON contract has not enough TON to transfer
    uint256 tonAmount = _toWAD(wtonAmount);
    uint256 tonBalance = ton.balanceOf(address(this));
    if (tonBalance < tonAmount) {
      ton.mint(address(this), tonAmount.sub(tonBalance));
    }

    ton.safeTransfer(tonAccount, tonAmount);
    return true;
  }

  function _swapFromTON(address tonAccount, address wtonAccount, uint256 tonAmount) internal returns (bool) {
    _mint(wtonAccount, _toRAY(tonAmount));
    ton.safeTransferFrom(tonAccount, address(this), tonAmount);
    return true;
  }

  /**
   * @dev transform WAD to RAY
   */
  function _toRAY(uint256 v) internal pure returns (uint256) {
    return v * 10 ** 9;
  }

  /**
   * @dev transform RAY to WAD
   */
  function _toWAD(uint256 v) internal pure returns (uint256) {
    return v / 10 ** 9;
  }
}
function burnFrom(address account, uint256 amount) public {
  if (isMinter(msg.sender)) {
    _burn(account, amount);
    return;
  }

  super.burnFrom(account, amount);
}

//////////////////////