ETH Price: $2,280.42 (+4.82%)

Transaction Decoder

Block:
6690217 at Nov-12-2018 10:41:29 AM +UTC
Transaction Fee:
0.000850355 ETH $1.94
Gas Used:
77,305 Gas / 11 Gwei

Emitted Events:

85 PolicyPalNetworkToken.Transfer( from=[Sender] 0xc64dee63dde48588f3061c5724e19a30a471df46, to=[Receiver] Dex2, value=9150000000000000000000 )
86 Dex2.DepositEvent( trader=0xF056D004...F360945ea, tokenCode=107, symbol=PAL, amountE8=915000000000, depositIndex=13921 )

Account State Difference:

  Address   Before After State Difference Code
0x7600977E...E11566341
(DEx.top)
(MiningPoolHub: Old Address)
17,021.485286969231088876 Eth17,021.486137324231088876 Eth0.000850355
0xC64deE63...0a471Df46
0.4351671457 Eth
Nonce: 9870
0.4343167907 Eth
Nonce: 9871
0.000850355
0xfeDAE564...215F942EE

Execution Trace

Dex2.depositToken( traderAddr=0xF056D00411cc826E6fCa7D2D280A8AEF360945ea, tokenCode=107, originalAmount=9150000000000000000000 )
  • PolicyPalNetworkToken.transferFrom( _from=0xC64deE63DdE48588F3061c5724E19a30a471Df46, _to=0x7600977Eb9eFFA627D6BD0DA2E5be35E11566341, _value=9150000000000000000000 ) => ( True )
    File 1 of 2: Dex2
    // DEx.top - Instant Trading on Chain
    //
    // Author: DEx.top Team
    
    pragma solidity 0.4.21;
    pragma experimental "v0.5.0";
    
    interface Token {
      function transfer(address to, uint256 value) external returns (bool success);
      function transferFrom(address from, address to, uint256 value) external returns (bool success);
    }
    
    contract Dex2 {
      //------------------------------ Struct Definitions: ---------------------------------------------
    
      struct TokenInfo {
        string  symbol;       // e.g., "ETH", "ADX"
        address tokenAddr;    // ERC20 token address
        uint64  scaleFactor;  // <original token amount> = <scaleFactor> x <DEx amountE8> / 1e8
        uint    minDeposit;   // mininum deposit (original token amount) allowed for this token
      }
    
      struct TraderInfo {
        address withdrawAddr;
        uint8   feeRebatePercent;  // range: [0, 100]
      }
    
      struct TokenAccount {
        uint64 balanceE8;          // available amount for trading
        uint64 pendingWithdrawE8;  // the amount to be transferred out from this contract to the trader
      }
    
      struct Order {
        uint32 pairId;  // <cashId>(16) <stockId>(16)
        uint8  action;  // 0 means BUY; 1 means SELL
        uint8  ioc;     // 0 means a regular order; 1 means an immediate-or-cancel (IOC) order
        uint64 priceE8;
        uint64 amountE8;
        uint64 expireTimeSec;
      }
    
      struct Deposit {
        address traderAddr;
        uint16  tokenCode;
        uint64  pendingAmountE8;   // amount to be confirmed for trading purpose
      }
    
      struct DealInfo {
        uint16 stockCode;          // stock token code
        uint16 cashCode;           // cash token code
        uint64 stockDealAmountE8;
        uint64 cashDealAmountE8;
      }
    
      struct ExeStatus {
        uint64 logicTimeSec;       // logic timestamp for checking order expiration
        uint64 lastOperationIndex; // index of the last executed operation
      }
    
      //----------------- Constants: -------------------------------------------------------------------
    
      uint constant MAX_UINT256 = 2**256 - 1;
      uint16 constant MAX_FEE_RATE_E4 = 60;  // upper limit of fee rate is 0.6% (60 / 1e4)
    
      // <original ETH amount in Wei> = <DEx amountE8> * <ETH_SCALE_FACTOR> / 1e8
      uint64 constant ETH_SCALE_FACTOR = 10**18;
    
      uint8 constant ACTIVE = 0;
      uint8 constant CLOSED = 2;
    
      bytes32 constant HASHTYPES =
          keccak256('string title', 'address market_address', 'uint64 nonce', 'uint64 expire_time_sec',
                    'uint64 amount_e8', 'uint64 price_e8', 'uint8 immediate_or_cancel', 'uint8 action',
                    'uint16 cash_token_code', 'uint16 stock_token_code');
    
      //----------------- States that cannot be changed once set: --------------------------------------
    
      address public admin;                         // admin address, and it cannot be changed
      mapping (uint16 => TokenInfo) public tokens;  // mapping of token code to token information
    
      //----------------- Other states: ----------------------------------------------------------------
    
      uint8 public marketStatus;        // market status: 0 - Active; 1 - Suspended; 2 - Closed
    
      uint16 public makerFeeRateE4;     // maker fee rate (* 10**4)
      uint16 public takerFeeRateE4;     // taker fee rate (* 10**4)
      uint16 public withdrawFeeRateE4;  // withdraw fee rate (* 10**4)
    
      uint64 public lastDepositIndex;   // index of the last deposit operation
    
      ExeStatus public exeStatus;       // status of operation execution
    
      mapping (address => TraderInfo) public traders;     // mapping of trade address to trader information
      mapping (uint176 => TokenAccount) public accounts;  // mapping of trader token key to its account information
      mapping (uint224 => Order) public orders;           // mapping of order key to order information
      mapping (uint64  => Deposit) public deposits;       // mapping of deposit index to deposit information
    
      //------------------------------ Dex2 Events: ----------------------------------------------------
    
      event DeployMarketEvent();
      event ChangeMarketStatusEvent(uint8 status);
      event SetTokenInfoEvent(uint16 tokenCode, string symbol, address tokenAddr, uint64 scaleFactor, uint minDeposit);
      event SetWithdrawAddrEvent(address trader, address withdrawAddr);
    
      event DepositEvent(address trader, uint16 tokenCode, string symbol, uint64 amountE8, uint64 depositIndex);
      event WithdrawEvent(address trader, uint16 tokenCode, string symbol, uint64 amountE8, uint64 lastOpIndex);
      event TransferFeeEvent(uint16 tokenCode, uint64 amountE8, address toAddr);
    
      // `balanceE8` is the total balance after this deposit confirmation
      event ConfirmDepositEvent(address trader, uint16 tokenCode, uint64 balanceE8);
      // `amountE8` is the post-fee initiated withdraw amount
      // `pendingWithdrawE8` is the total pending withdraw amount after this withdraw initiation
      event InitiateWithdrawEvent(address trader, uint16 tokenCode, uint64 amountE8, uint64 pendingWithdrawE8);
      event MatchOrdersEvent(address trader1, uint64 nonce1, address trader2, uint64 nonce2);
      event HardCancelOrderEvent(address trader, uint64 nonce);
      event SetFeeRatesEvent(uint16 makerFeeRateE4, uint16 takerFeeRateE4, uint16 withdrawFeeRateE4);
      event SetFeeRebatePercentEvent(address trader, uint8 feeRebatePercent);
    
      //------------------------------ Contract Initialization: ----------------------------------------
    
      function Dex2(address admin_) public {
        admin = admin_;
        setTokenInfo(0 /*tokenCode*/, "ETH", 0 /*tokenAddr*/, ETH_SCALE_FACTOR, 0 /*minDeposit*/);
        emit DeployMarketEvent();
      }
    
      //------------------------------ External Functions: ---------------------------------------------
    
      function() external {
        revert();
      }
    
      // Change the market status of DEX.
      function changeMarketStatus(uint8 status_) external {
        if (msg.sender != admin) revert();
        if (marketStatus == CLOSED) revert();  // closed is forever
    
        marketStatus = status_;
        emit ChangeMarketStatusEvent(status_);
      }
    
      // Each trader can specify a withdraw address (but cannot change it later). Once a trader's
      // withdraw address is set, following withdrawals of this trader will go to the withdraw address
      // instead of the trader's address.
      function setWithdrawAddr(address withdrawAddr) external {
        if (withdrawAddr == 0) revert();
        if (traders[msg.sender].withdrawAddr != 0) revert();  // cannot change withdrawAddr once set
        traders[msg.sender].withdrawAddr = withdrawAddr;
        emit SetWithdrawAddrEvent(msg.sender, withdrawAddr);
      }
    
      // Deposit ETH from msg.sender for the given trader.
      function depositEth(address traderAddr) external payable {
        if (marketStatus != ACTIVE) revert();
        if (traderAddr == 0) revert();
        if (msg.value < tokens[0].minDeposit) revert();
        if (msg.data.length != 4 + 32) revert();  // length condition of param count
    
        uint64 pendingAmountE8 = uint64(msg.value / (ETH_SCALE_FACTOR / 10**8));  // msg.value is in Wei
        if (pendingAmountE8 == 0) revert();
    
        uint64 depositIndex = ++lastDepositIndex;
        setDeposits(depositIndex, traderAddr, 0, pendingAmountE8);
        emit DepositEvent(traderAddr, 0, "ETH", pendingAmountE8, depositIndex);
      }
    
      // Deposit token (other than ETH) from msg.sender for a specified trader.
      //
      // After the deposit has been confirmed enough times on the blockchain, it will be added to the
      // trader's token account for trading.
      function depositToken(address traderAddr, uint16 tokenCode, uint originalAmount) external {
        if (marketStatus != ACTIVE) revert();
        if (traderAddr == 0) revert();
        if (tokenCode == 0) revert();  // this function does not handle ETH
        if (msg.data.length != 4 + 32 + 32 + 32) revert();  // length condition of param count
    
        TokenInfo memory tokenInfo = tokens[tokenCode];
        if (originalAmount < tokenInfo.minDeposit) revert();
        if (tokenInfo.scaleFactor == 0) revert();  // unsupported token
    
        // Need to make approval by calling Token(address).approve() in advance for ERC-20 Tokens.
        if (!Token(tokenInfo.tokenAddr).transferFrom(msg.sender, this, originalAmount)) revert();
    
        if (originalAmount > MAX_UINT256 / 10**8) revert();  // avoid overflow
        uint amountE8 = originalAmount * 10**8 / uint(tokenInfo.scaleFactor);
        if (amountE8 >= 2**64 || amountE8 == 0) revert();
    
        uint64 depositIndex = ++lastDepositIndex;
        setDeposits(depositIndex, traderAddr, tokenCode, uint64(amountE8));
        emit DepositEvent(traderAddr, tokenCode, tokens[tokenCode].symbol, uint64(amountE8), depositIndex);
      }
    
      // Withdraw ETH from the contract.
      function withdrawEth(address traderAddr) external {
        if (traderAddr == 0) revert();
        if (msg.data.length != 4 + 32) revert();  // length condition of param count
    
        uint176 accountKey = uint176(traderAddr);
        uint amountE8 = accounts[accountKey].pendingWithdrawE8;
        if (amountE8 == 0) return;
    
        // Write back to storage before making the transfer.
        accounts[accountKey].pendingWithdrawE8 = 0;
    
        uint truncatedWei = amountE8 * (ETH_SCALE_FACTOR / 10**8);
        address withdrawAddr = traders[traderAddr].withdrawAddr;
        if (withdrawAddr == 0) withdrawAddr = traderAddr;
        withdrawAddr.transfer(truncatedWei);
        emit WithdrawEvent(traderAddr, 0, "ETH", uint64(amountE8), exeStatus.lastOperationIndex);
      }
    
      // Withdraw token (other than ETH) from the contract.
      function withdrawToken(address traderAddr, uint16 tokenCode) external {
        if (traderAddr == 0) revert();
        if (tokenCode == 0) revert();  // this function does not handle ETH
        if (msg.data.length != 4 + 32 + 32) revert();  // length condition of param count
    
        TokenInfo memory tokenInfo = tokens[tokenCode];
        if (tokenInfo.scaleFactor == 0) revert();  // unsupported token
    
        uint176 accountKey = uint176(tokenCode) << 160 | uint176(traderAddr);
        uint amountE8 = accounts[accountKey].pendingWithdrawE8;
        if (amountE8 == 0) return;
    
        // Write back to storage before making the transfer.
        accounts[accountKey].pendingWithdrawE8 = 0;
    
        uint truncatedAmount = amountE8 * uint(tokenInfo.scaleFactor) / 10**8;
        address withdrawAddr = traders[traderAddr].withdrawAddr;
        if (withdrawAddr == 0) withdrawAddr = traderAddr;
        if (!Token(tokenInfo.tokenAddr).transfer(withdrawAddr, truncatedAmount)) revert();
        emit WithdrawEvent(traderAddr, tokenCode, tokens[tokenCode].symbol, uint64(amountE8),
                           exeStatus.lastOperationIndex);
      }
    
      // Transfer the collected fee out of the contract.
      function transferFee(uint16 tokenCode, uint64 amountE8, address toAddr) external {
        if (msg.sender != admin) revert();
        if (toAddr == 0) revert();
        if (msg.data.length != 4 + 32 + 32 + 32) revert();
    
        TokenAccount memory feeAccount = accounts[uint176(tokenCode) << 160];
        uint64 withdrawE8 = feeAccount.pendingWithdrawE8;
        if (amountE8 < withdrawE8) {
          withdrawE8 = amountE8;
        }
        feeAccount.pendingWithdrawE8 -= withdrawE8;
        accounts[uint176(tokenCode) << 160] = feeAccount;
    
        TokenInfo memory tokenInfo = tokens[tokenCode];
        uint originalAmount = uint(withdrawE8) * uint(tokenInfo.scaleFactor) / 10**8;
        if (tokenCode == 0) {  // ETH
          toAddr.transfer(originalAmount);
        } else {
          if (!Token(tokenInfo.tokenAddr).transfer(toAddr, originalAmount)) revert();
        }
        emit TransferFeeEvent(tokenCode, withdrawE8, toAddr);
      }
    
      // Replay the trading sequence from the off-chain ledger exactly onto the on-chain ledger.
      function exeSequence(uint header, uint[] body) external {
        if (msg.sender != admin) revert();
    
        uint64 nextOperationIndex = uint64(header);
        if (nextOperationIndex != exeStatus.lastOperationIndex + 1) revert();  // check sequence index
    
        uint64 newLogicTimeSec = uint64(header >> 64);
        if (newLogicTimeSec < exeStatus.logicTimeSec) revert();
    
        for (uint i = 0; i < body.length; nextOperationIndex++) {
          uint bits = body[i];
          uint opcode = bits & 0xFFFF;
          bits >>= 16;
          if ((opcode >> 8) != 0xDE) revert();  // check the magic number
    
          // ConfirmDeposit: <depositIndex>(64)
          if (opcode == 0xDE01) {
            confirmDeposit(uint64(bits));
            i += 1;
            continue;
          }
    
          // InitiateWithdraw: <amountE8>(64) <tokenCode>(16) <traderAddr>(160)
          if (opcode == 0xDE02) {
            initiateWithdraw(uint176(bits), uint64(bits >> 176));
            i += 1;
            continue;
          }
    
          //-------- The rest operation types are allowed only when the market is active ---------
          if (marketStatus != ACTIVE) revert();
    
          // MatchOrders
          if (opcode == 0xDE03) {
            uint8 v1 = uint8(bits);
            bits >>= 8;            // bits is now the key of the maker order
    
            Order memory makerOrder;
            if (v1 == 0) {         // order already in storage
              if (i + 1 >= body.length) revert();  // at least 1 body element left
              makerOrder = orders[uint224(bits)];
              i += 1;
            } else {
              if (orders[uint224(bits)].pairId != 0) revert();  // order must not be already in storage
              if (i + 4 >= body.length) revert();  // at least 4 body elements left
              makerOrder = parseNewOrder(uint224(bits) /*makerOrderKey*/, v1, body, i);
              i += 4;
            }
    
            uint8 v2 = uint8(body[i]);
            uint224 takerOrderKey = uint224(body[i] >> 8);
            Order memory takerOrder;
            if (v2 == 0) {         // order already in storage
              takerOrder = orders[takerOrderKey];
              i += 1;
            } else {
              if (orders[takerOrderKey].pairId != 0) revert();  // order must not be already in storage
              if (i + 3 >= body.length) revert();  // at least 3 body elements left
              takerOrder = parseNewOrder(takerOrderKey, v2, body, i);
              i += 4;
            }
    
            matchOrder(uint224(bits) /*makerOrderKey*/, makerOrder, takerOrderKey, takerOrder);
            continue;
          }
    
          // HardCancelOrder: <nonce>(64) <traderAddr>(160)
          if (opcode == 0xDE04) {
            hardCancelOrder(uint224(bits) /*orderKey*/);
            i += 1;
            continue;
          }
    
          // SetFeeRates: <withdrawFeeRateE4>(16) <takerFeeRateE4>(16) <makerFeeRateE4>(16)
          if (opcode == 0xDE05) {
            setFeeRates(uint16(bits), uint16(bits >> 16), uint16(bits >> 32));
            i += 1;
            continue;
          }
    
          // SetFeeRebatePercent: <rebatePercent>(8) <traderAddr>(160)
          if (opcode == 0xDE06) {
            setFeeRebatePercent(address(bits) /*traderAddr*/, uint8(bits >> 160) /*rebatePercent*/);
            i += 1;
            continue;
          }
        } // for loop
    
        setExeStatus(newLogicTimeSec, nextOperationIndex - 1);
      } // function exeSequence
    
      //------------------------------ Public Functions: -----------------------------------------------
    
      // Set information of a token.
      function setTokenInfo(uint16 tokenCode, string symbol, address tokenAddr, uint64 scaleFactor,
                            uint minDeposit) public {
        if (msg.sender != admin) revert();
        if (marketStatus != ACTIVE) revert();
        if (scaleFactor == 0) revert();
    
        TokenInfo memory info = tokens[tokenCode];
        if (info.scaleFactor != 0) {  // this token already exists
          // For an existing token only the minDeposit field can be updated.
          tokens[tokenCode].minDeposit = minDeposit;
          emit SetTokenInfoEvent(tokenCode, info.symbol, info.tokenAddr, info.scaleFactor, minDeposit);
          return;
        }
    
        tokens[tokenCode].symbol = symbol;
        tokens[tokenCode].tokenAddr = tokenAddr;
        tokens[tokenCode].scaleFactor = scaleFactor;
        tokens[tokenCode].minDeposit = minDeposit;
        emit SetTokenInfoEvent(tokenCode, symbol, tokenAddr, scaleFactor, minDeposit);
      }
    
      //------------------------------ Private Functions: ----------------------------------------------
    
      function setDeposits(uint64 depositIndex, address traderAddr, uint16 tokenCode, uint64 amountE8) private {
        deposits[depositIndex].traderAddr = traderAddr;
        deposits[depositIndex].tokenCode = tokenCode;
        deposits[depositIndex].pendingAmountE8 = amountE8;
      }
    
      function setExeStatus(uint64 logicTimeSec, uint64 lastOperationIndex) private {
        exeStatus.logicTimeSec = logicTimeSec;
        exeStatus.lastOperationIndex = lastOperationIndex;
      }
    
      function confirmDeposit(uint64 depositIndex) private {
        Deposit memory deposit = deposits[depositIndex];
        uint176 accountKey = (uint176(deposit.tokenCode) << 160) | uint176(deposit.traderAddr);
        TokenAccount memory account = accounts[accountKey];
    
        // Check that pending amount is non-zero and no overflow would happen.
        if (account.balanceE8 + deposit.pendingAmountE8 <= account.balanceE8) revert();
        account.balanceE8 += deposit.pendingAmountE8;
    
        deposits[depositIndex].pendingAmountE8 = 0;
        accounts[accountKey].balanceE8 += deposit.pendingAmountE8;
        emit ConfirmDepositEvent(deposit.traderAddr, deposit.tokenCode, account.balanceE8);
      }
    
      function initiateWithdraw(uint176 tokenAccountKey, uint64 amountE8) private {
        uint64 balanceE8 = accounts[tokenAccountKey].balanceE8;
        uint64 pendingWithdrawE8 = accounts[tokenAccountKey].pendingWithdrawE8;
    
        if (balanceE8 < amountE8 || amountE8 == 0) revert();
        balanceE8 -= amountE8;
    
        uint64 feeE8 = calcFeeE8(amountE8, withdrawFeeRateE4, address(tokenAccountKey));
        amountE8 -= feeE8;
    
        if (pendingWithdrawE8 + amountE8 < amountE8) revert();  // check overflow
        pendingWithdrawE8 += amountE8;
    
        accounts[tokenAccountKey].balanceE8 = balanceE8;
        accounts[tokenAccountKey].pendingWithdrawE8 = pendingWithdrawE8;
    
        // Note that the fee account has a dummy trader address of 0.
        if (accounts[tokenAccountKey & (0xffff << 160)].pendingWithdrawE8 + feeE8 >= feeE8) {  // no overflow
          accounts[tokenAccountKey & (0xffff << 160)].pendingWithdrawE8 += feeE8;
        }
    
        emit InitiateWithdrawEvent(address(tokenAccountKey), uint16(tokenAccountKey >> 160) /*tokenCode*/,
                                   amountE8, pendingWithdrawE8);
      }
    
      function getDealInfo(uint32 pairId, uint64 priceE8, uint64 amount1E8, uint64 amount2E8)
          private pure returns (DealInfo deal) {
        deal.stockCode = uint16(pairId);
        deal.cashCode = uint16(pairId >> 16);
        if (deal.stockCode == deal.cashCode) revert();  // we disallow homogeneous trading
    
        deal.stockDealAmountE8 = amount1E8 < amount2E8 ? amount1E8 : amount2E8;
    
        uint cashDealAmountE8 = uint(priceE8) * uint(deal.stockDealAmountE8) / 10**8;
        if (cashDealAmountE8 >= 2**64) revert();
        deal.cashDealAmountE8 = uint64(cashDealAmountE8);
      }
    
      function calcFeeE8(uint64 amountE8, uint feeRateE4, address traderAddr)
          private view returns (uint64) {
        uint feeE8 = uint(amountE8) * feeRateE4 / 10000;
        feeE8 -= feeE8 * uint(traders[traderAddr].feeRebatePercent) / 100;
        return uint64(feeE8);
      }
    
      function settleAccounts(DealInfo deal, address traderAddr, uint feeRateE4, bool isBuyer) private {
        uint16 giveTokenCode = isBuyer ? deal.cashCode : deal.stockCode;
        uint16 getTokenCode = isBuyer ? deal.stockCode : deal.cashCode;
    
        uint64 giveAmountE8 = isBuyer ? deal.cashDealAmountE8 : deal.stockDealAmountE8;
        uint64 getAmountE8 = isBuyer ? deal.stockDealAmountE8 : deal.cashDealAmountE8;
    
        uint176 giveAccountKey = uint176(giveTokenCode) << 160 | uint176(traderAddr);
        uint176 getAccountKey = uint176(getTokenCode) << 160 | uint176(traderAddr);
    
        uint64 feeE8 = calcFeeE8(getAmountE8, feeRateE4, traderAddr);
        getAmountE8 -= feeE8;
    
        // Check overflow.
        if (accounts[giveAccountKey].balanceE8 < giveAmountE8) revert();
        if (accounts[getAccountKey].balanceE8 + getAmountE8 < getAmountE8) revert();
    
        // Write storage.
        accounts[giveAccountKey].balanceE8 -= giveAmountE8;
        accounts[getAccountKey].balanceE8 += getAmountE8;
    
        if (accounts[uint176(getTokenCode) << 160].pendingWithdrawE8 + feeE8 >= feeE8) {  // no overflow
          accounts[uint176(getTokenCode) << 160].pendingWithdrawE8 += feeE8;
        }
      }
    
      function setOrders(uint224 orderKey, uint32 pairId, uint8 action, uint8 ioc,
                         uint64 priceE8, uint64 amountE8, uint64 expireTimeSec) private {
        orders[orderKey].pairId = pairId;
        orders[orderKey].action = action;
        orders[orderKey].ioc = ioc;
        orders[orderKey].priceE8 = priceE8;
        orders[orderKey].amountE8 = amountE8;
        orders[orderKey].expireTimeSec = expireTimeSec;
      }
    
      function matchOrder(uint224 makerOrderKey, Order makerOrder,
                          uint224 takerOrderKey, Order takerOrder) private {
        // Check trading conditions.
        if (marketStatus != ACTIVE) revert();
        if (makerOrderKey == takerOrderKey) revert();  // the two orders must not have the same key
        if (makerOrder.pairId != takerOrder.pairId) revert();
        if (makerOrder.action == takerOrder.action) revert();
        if (makerOrder.priceE8 == 0 || takerOrder.priceE8 == 0) revert();
        if (makerOrder.action == 0 && makerOrder.priceE8 < takerOrder.priceE8) revert();
        if (takerOrder.action == 0 && takerOrder.priceE8 < makerOrder.priceE8) revert();
        if (makerOrder.amountE8 == 0 || takerOrder.amountE8 == 0) revert();
        if (makerOrder.expireTimeSec <= exeStatus.logicTimeSec) revert();
        if (takerOrder.expireTimeSec <= exeStatus.logicTimeSec) revert();
    
        DealInfo memory deal = getDealInfo(
            makerOrder.pairId, makerOrder.priceE8, makerOrder.amountE8, takerOrder.amountE8);
    
        // Update accounts.
        settleAccounts(deal, address(makerOrderKey), makerFeeRateE4, (makerOrder.action == 0));
        settleAccounts(deal, address(takerOrderKey), takerFeeRateE4, (takerOrder.action == 0));
    
        // Update orders.
        if (makerOrder.ioc == 1) {  // IOC order
          makerOrder.amountE8 = 0;
        } else {
          makerOrder.amountE8 -= deal.stockDealAmountE8;
        }
        if (takerOrder.ioc == 1) {  // IOC order
          takerOrder.amountE8 = 0;
        } else {
          takerOrder.amountE8 -= deal.stockDealAmountE8;
        }
    
        // Write orders back to storage.
        setOrders(makerOrderKey, makerOrder.pairId, makerOrder.action, makerOrder.ioc,
                  makerOrder.priceE8, makerOrder.amountE8, makerOrder.expireTimeSec);
        setOrders(takerOrderKey, takerOrder.pairId, takerOrder.action, takerOrder.ioc,
                  takerOrder.priceE8, takerOrder.amountE8, takerOrder.expireTimeSec);
    
        emit MatchOrdersEvent(address(makerOrderKey), uint64(makerOrderKey >> 160) /*nonce*/,
                              address(takerOrderKey), uint64(takerOrderKey >> 160) /*nonce*/);
      }
    
      function hardCancelOrder(uint224 orderKey) private {
        orders[orderKey].pairId = 0xFFFFFFFF;
        orders[orderKey].amountE8 = 0;
        emit HardCancelOrderEvent(address(orderKey) /*traderAddr*/, uint64(orderKey >> 160) /*nonce*/);
      }
    
      function setFeeRates(uint16 makerE4, uint16 takerE4, uint16 withdrawE4) private {
        if (makerE4 > MAX_FEE_RATE_E4) revert();
        if (takerE4 > MAX_FEE_RATE_E4) revert();
        if (withdrawE4 > MAX_FEE_RATE_E4) revert();
    
        makerFeeRateE4 = makerE4;
        takerFeeRateE4 = takerE4;
        withdrawFeeRateE4 = withdrawE4;
        emit SetFeeRatesEvent(makerE4, takerE4, withdrawE4);
      }
    
      function setFeeRebatePercent(address traderAddr, uint8 feeRebatePercent) private {
        if (feeRebatePercent > 100) revert();
    
        traders[traderAddr].feeRebatePercent = feeRebatePercent;
        emit SetFeeRebatePercentEvent(traderAddr, feeRebatePercent);
      }
    
      function parseNewOrder(uint224 orderKey, uint8 v, uint[] body, uint i) private view returns (Order) {
        // bits: <expireTimeSec>(64) <amountE8>(64) <priceE8>(64) <ioc>(8) <action>(8) <pairId>(32)
        uint240 bits = uint240(body[i + 1]);
        uint64 nonce = uint64(orderKey >> 160);
        address traderAddr = address(orderKey);
        if (traderAddr == 0) revert();  // check zero addr early since `ecrecover` returns 0 on error
    
        // verify the signature of the trader
        bytes32 hash1 = keccak256("\x19Ethereum Signed Message:\n70DEx2 Order: ", address(this), nonce, bits);
        if (traderAddr != ecrecover(hash1, v, bytes32(body[i + 2]), bytes32(body[i + 3]))) {
          bytes32 hashValues = keccak256("DEx2 Order", address(this), nonce, bits);
          bytes32 hash2 = keccak256(HASHTYPES, hashValues);
          if (traderAddr != ecrecover(hash2, v, bytes32(body[i + 2]), bytes32(body[i + 3]))) revert();
        }
    
        Order memory order;
        order.pairId = uint32(bits); bits >>= 32;
        order.action = uint8(bits); bits >>= 8;
        order.ioc = uint8(bits); bits >>= 8;
        order.priceE8 = uint64(bits); bits >>= 64;
        order.amountE8 = uint64(bits); bits >>= 64;
        order.expireTimeSec = uint64(bits);
        return order;
      }
    
    }  // contract

    File 2 of 2: PolicyPalNetworkToken
    pragma solidity ^0.4.18;
    
    // File: contracts/zeppelin/math/SafeMath.sol
    
    /**
     * @title SafeMath
     * @dev Math operations with safety checks that throw on error
     */
    library SafeMath {
    
      /**
      * @dev Multiplies two numbers, throws on overflow.
      */
      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
          return 0;
        }
        uint256 c = a * b;
        assert(c / a == b);
        return c;
      }
    
      /**
      * @dev Integer division of two numbers, truncating the quotient.
      */
      function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // assert(b > 0); // Solidity automatically throws when dividing by 0
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return c;
      }
    
      /**
      * @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
      */
      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
      }
    
      /**
      * @dev Adds two numbers, throws on overflow.
      */
      function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
      }
    }
    
    // File: contracts/CrowdsaleAuthorizer.sol
    
    /**
     * @title CrowdsaleAuthorizer
     * @dev Crowd Sale Authorizer
     */
    contract CrowdsaleAuthorizer {
        mapping(address => uint256)    public participated;
        mapping(address => bool)       public whitelistAddresses;
    
        address                        public admin;
        uint256                        public saleStartTime;
        uint256                        public saleEndTime;
        uint256                        public increaseMaxContribTime;
        uint256                        public minContribution;
        uint256                        public maxContribution;
    
        using SafeMath for uint256;
    
        /**
        * @dev Modifier for only admin
        */
        modifier onlyAdmin() {
          require(msg.sender == admin);
          _;
        }
    
        /**
        * @dev Modifier for valid address
        */
        modifier validAddress(address _addr) {
          require(_addr != address(0x0));
          require(_addr != address(this));
          _;
        }
    
        /**
         * @dev Contract Constructor
         * @param _saleStartTime - The Start Time of the Token Sale
         * @param _saleEndTime - The End Time of the Token Sale
         * @param _increaseMaxContribTime - Time to increase Max Contribution of the Token Sale
         * @param _minContribution - Minimum ETH contribution per contributor
         * @param _maxContribution - Maximum ETH contribution per contributor
         */
        function CrowdsaleAuthorizer(
            address _admin,
            uint256 _saleStartTime,
            uint256 _saleEndTime,
            uint256 _increaseMaxContribTime,
            uint256 _minContribution,
            uint256 _maxContribution
        )
            validAddress(_admin)
            public
        {
            require(_saleStartTime > now);
            require(_saleEndTime > now);
            require(_increaseMaxContribTime > now);
            require(_saleStartTime < _saleEndTime);
            require(_increaseMaxContribTime > _saleStartTime);
            require(_maxContribution > 0);
            require(_minContribution < _maxContribution);
    
            admin = _admin;
            saleStartTime = _saleStartTime;
            saleEndTime = _saleEndTime;
            increaseMaxContribTime = _increaseMaxContribTime;
    
            minContribution = _minContribution;
            maxContribution = _maxContribution;
        }
    
        event UpdateWhitelist(address _user, bool _allow, uint _time);
    
        /**
         * @dev Update Whitelist Address
         * @param _user - Whitelist address
         * @param _allow - eligibility
         */
        function updateWhitelist(address _user, bool _allow)
            public
            onlyAdmin
        {
            whitelistAddresses[_user] = _allow;
            UpdateWhitelist(_user, _allow, now);
        }
    
        /**
         * @dev Batch Update Whitelist Address
         * @param _users - Array of Whitelist addresses
         * @param _allows - Array of eligibilities
         */
        function updateWhitelists(address[] _users, bool[] _allows)
            external
            onlyAdmin
        {
            require(_users.length == _allows.length);
            for (uint i = 0 ; i < _users.length ; i++) {
                address _user = _users[i];
                bool _allow = _allows[i];
                whitelistAddresses[_user] = _allow;
                UpdateWhitelist(_user, _allow, now);
            }
        }
    
        /**
         * @dev Get Eligible Amount
         * @param _contributor - Contributor address
         * @param _amount - Intended contribution amount
         */
        function eligibleAmount(address _contributor, uint256 _amount)
            public
            view
            returns(uint256)
        {
            // If sales has not started or sale ended, there's no allocation
            if (!saleStarted() || saleEnded()) {
                return 0;
            }
    
            // Amount lesser than minimum contribution will be rejected
            if (_amount < minContribution) {
                return 0;
            }
    
            uint256 userMaxContribution = maxContribution;
            // If sale has past 24hrs, increase max cap
            if (now >= increaseMaxContribTime) {
                userMaxContribution = maxContribution.mul(10);
            }
    
            // Calculate remaining contribution for the contributor
            uint256 remainingCap = userMaxContribution.sub(participated[_contributor]);
    
            // Return either the amount contributed or cap whichever is lower
            return (remainingCap > _amount) ? _amount : remainingCap;
        }
    
        /**
         * @dev Get if sale has started
         */
        function saleStarted() public view returns(bool) {
            return now >= saleStartTime;
        }
    
        /**
         * @dev Get if sale has ended
         */
        function saleEnded() public view returns(bool) {
            return now > saleEndTime;
        }
    
        /**
         * @dev Check for eligible amount and modify participation map
         * @param _contributor - Contributor address
         * @param _amount - Intended contribution amount
         */
        function eligibleAmountCheck(address _contributor, uint256 _amount)
            internal
            returns(uint256)
        {
            // Check if contributor is whitelisted
            if (!whitelistAddresses[_contributor]) {
                return 0;
            }
    
            uint256 result = eligibleAmount(_contributor, _amount);
            participated[_contributor] = participated[_contributor].add(result);
    
            return result;
        }
    }
    
    // File: contracts/zeppelin/ownership/Ownable.sol
    
    /**
     * @title Ownable
     * @dev The Ownable contract has an owner address, and provides basic authorization control
     * functions, this simplifies the implementation of "user permissions".
     */
    contract Ownable {
      address public owner;
    
    
      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    
      /**
       * @dev The Ownable constructor sets the original `owner` of the contract to the sender
       * account.
       */
      function Ownable() public {
        owner = msg.sender;
      }
    
      /**
       * @dev Throws if called by any account other than the owner.
       */
      modifier onlyOwner() {
        require(msg.sender == owner);
        _;
      }
    
      /**
       * @dev Allows the current owner to transfer control of the contract to a newOwner.
       * @param newOwner The address to transfer ownership to.
       */
      function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0));
        OwnershipTransferred(owner, newOwner);
        owner = newOwner;
      }
    
    }
    
    // File: contracts/zeppelin/token/ERC20Basic.sol
    
    /**
     * @title ERC20Basic
     * @dev Simpler version of ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/179
     */
    contract ERC20Basic {
      function totalSupply() public view returns (uint256);
      function balanceOf(address who) public view returns (uint256);
      function transfer(address to, uint256 value) public returns (bool);
      event Transfer(address indexed from, address indexed to, uint256 value);
    }
    
    // File: contracts/zeppelin/token/BasicToken.sol
    
    /**
     * @title Basic token
     * @dev Basic version of StandardToken, with no allowances.
     */
    contract BasicToken is ERC20Basic {
      using SafeMath for uint256;
    
      mapping(address => uint256) balances;
    
      uint256 totalSupply_;
    
      /**
      * @dev total number of tokens in existence
      */
      function totalSupply() public view returns (uint256) {
        return totalSupply_;
      }
    
      /**
      * @dev transfer token for a specified address
      * @param _to The address to transfer to.
      * @param _value The amount to be transferred.
      */
      function transfer(address _to, uint256 _value) public returns (bool) {
        require(_to != address(0));
        require(_value <= balances[msg.sender]);
    
        // SafeMath.sub will throw if there is not enough balance.
        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        Transfer(msg.sender, _to, _value);
        return true;
      }
    
      /**
      * @dev Gets the balance of the specified address.
      * @param _owner The address to query the the balance of.
      * @return An uint256 representing the amount owned by the passed address.
      */
      function balanceOf(address _owner) public view returns (uint256 balance) {
        return balances[_owner];
      }
    
    }
    
    // File: contracts/zeppelin/token/BurnableToken.sol
    
    /**
     * @title Burnable Token
     * @dev Token that can be irreversibly burned (destroyed).
     */
    contract BurnableToken is BasicToken {
    
      event Burn(address indexed burner, uint256 value);
    
      /**
       * @dev Burns a specific amount of tokens.
       * @param _value The amount of token to be burned.
       */
      function burn(uint256 _value) public {
        require(_value <= balances[msg.sender]);
        // no need to require value <= totalSupply, since that would imply the
        // sender's balance is greater than the totalSupply, which *should* be an assertion failure
    
        address burner = msg.sender;
        balances[burner] = balances[burner].sub(_value);
        totalSupply_ = totalSupply_.sub(_value);
        Burn(burner, _value);
      }
    }
    
    // File: contracts/zeppelin/token/ERC20.sol
    
    /**
     * @title ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    contract ERC20 is ERC20Basic {
      function allowance(address owner, address spender) public view returns (uint256);
      function transferFrom(address from, address to, uint256 value) public returns (bool);
      function approve(address spender, uint256 value) public returns (bool);
      event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    // File: contracts/zeppelin/token/StandardToken.sol
    
    /**
     * @title Standard ERC20 token
     *
     * @dev Implementation of the basic standard token.
     * @dev https://github.com/ethereum/EIPs/issues/20
     * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
     */
    contract StandardToken is ERC20, BasicToken {
    
      mapping (address => mapping (address => uint256)) internal allowed;
    
    
      /**
       * @dev Transfer tokens from one address to another
       * @param _from address The address which you want to send tokens from
       * @param _to address The address which you want to transfer to
       * @param _value uint256 the amount of tokens to be transferred
       */
      function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        require(_to != address(0));
        require(_value <= balances[_from]);
        require(_value <= allowed[_from][msg.sender]);
    
        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        Transfer(_from, _to, _value);
        return true;
      }
    
      /**
       * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
       *
       * Beware that changing an allowance with this method brings the risk that someone may use both the old
       * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
       * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
       * @param _spender The address which will spend the funds.
       * @param _value The amount of tokens to be spent.
       */
      function approve(address _spender, uint256 _value) public returns (bool) {
        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
      }
    
      /**
       * @dev Function to check the amount of tokens that an owner allowed to a spender.
       * @param _owner address The address which owns the funds.
       * @param _spender address The address which will spend the funds.
       * @return A uint256 specifying the amount of tokens still available for the spender.
       */
      function allowance(address _owner, address _spender) public view returns (uint256) {
        return allowed[_owner][_spender];
      }
    
      /**
       * @dev Increase the amount of tokens that an owner allowed to a spender.
       *
       * approve should be called when allowed[_spender] == 0. To increment
       * allowed value is better to use this function to avoid 2 calls (and wait until
       * the first transaction is mined)
       * From MonolithDAO Token.sol
       * @param _spender The address which will spend the funds.
       * @param _addedValue The amount of tokens to increase the allowance by.
       */
      function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
        allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
        Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
      }
    
      /**
       * @dev Decrease the amount of tokens that an owner allowed to a spender.
       *
       * approve should be called when allowed[_spender] == 0. To decrement
       * allowed value is better to use this function to avoid 2 calls (and wait until
       * the first transaction is mined)
       * From MonolithDAO Token.sol
       * @param _spender The address which will spend the funds.
       * @param _subtractedValue The amount of tokens to decrease the allowance by.
       */
      function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
        uint oldValue = allowed[msg.sender][_spender];
        if (_subtractedValue > oldValue) {
          allowed[msg.sender][_spender] = 0;
        } else {
          allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
        }
        Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
      }
    
    }
    
    // File: contracts/PolicyPalNetworkToken.sol
    
    /**
     * @title PolicyPalNetwork Token
     * @dev A standard ownable token
     */
    contract PolicyPalNetworkToken is StandardToken, BurnableToken, Ownable {
        /**
        * @dev Token Contract Constants
        */
        string    public constant name     = "PolicyPal Network Token";
        string    public constant symbol   = "PAL";
        uint8     public constant decimals = 18;
    
        /**
        * @dev Token Contract Public Variables
        */
        address public  tokenSaleContract;
        bool    public  isTokenTransferable = false;
    
    
        /**
        * @dev   Token Contract Modifier
        *
        * Check if a transfer is allowed
        * Transfers are restricted to token creator & owner(admin) during token sale duration
        * Transfers after token sale is limited by `isTokenTransferable` toggle
        *
        */
        modifier onlyWhenTransferAllowed() {
            require(isTokenTransferable || msg.sender == owner || msg.sender == tokenSaleContract);
            _;
        }
    
        /**
         * @dev Token Contract Modifier
         * @param _to - Address to check if valid
         *
         *  Check if an address is valid
         *  A valid address is as follows,
         *    1. Not zero address
         *    2. Not token address
         *
         */
        modifier isValidDestination(address _to) {
            require(_to != address(0x0));
            require(_to != address(this));
            _;
        }
    
        /**
         * @dev Enable Transfers (Only Owner)
         */
        function toggleTransferable(bool _toggle) external
            onlyOwner
        {
            isTokenTransferable = _toggle;
        }
        
    
        /**
        * @dev Token Contract Constructor
        * @param _adminAddr - Address of the Admin
        */
        function PolicyPalNetworkToken(
            uint _tokenTotalAmount,
            address _adminAddr
        ) 
            public
            isValidDestination(_adminAddr)
        {
            require(_tokenTotalAmount > 0);
    
            totalSupply_ = _tokenTotalAmount;
    
            // Mint all token
            balances[msg.sender] = _tokenTotalAmount;
            Transfer(address(0x0), msg.sender, _tokenTotalAmount);
    
            // Assign token sale contract to creator
            tokenSaleContract = msg.sender;
    
            // Transfer contract ownership to admin
            transferOwnership(_adminAddr);
        }
    
        /**
        * @dev Token Contract transfer
        * @param _to - Address to transfer to
        * @param _value - Value to transfer
        * @return bool - Result of transfer
        * "Overloaded" Function of ERC20Basic's transfer
        *
        */
        function transfer(address _to, uint256 _value) public
            onlyWhenTransferAllowed
            isValidDestination(_to)
            returns (bool)
        {
            return super.transfer(_to, _value);
        }
    
        /**
        * @dev Token Contract transferFrom
        * @param _from - Address to transfer from
        * @param _to - Address to transfer to
        * @param _value - Value to transfer
        * @return bool - Result of transferFrom
        *
        * "Overloaded" Function of ERC20's transferFrom
        * Added with modifiers,
        *    1. onlyWhenTransferAllowed
        *    2. isValidDestination
        *
        */
        function transferFrom(address _from, address _to, uint256 _value) public
            onlyWhenTransferAllowed
            isValidDestination(_to)
            returns (bool)
        {
            return super.transferFrom(_from, _to, _value);
        }
    
        /**
        * @dev Token Contract burn
        * @param _value - Value to burn
        * "Overloaded" Function of BurnableToken's burn
        */
        function burn(uint256 _value)
            public
        {
            super.burn(_value);
            Transfer(msg.sender, address(0x0), _value);
        }
    
        /**
        * @dev Token Contract Emergency Drain
        * @param _token - Token to drain
        * @param _amount - Amount to drain
        */
        function emergencyERC20Drain(ERC20 _token, uint256 _amount) public
            onlyOwner
        {
            _token.transfer(owner, _amount);
        }
    }
    
    // File: contracts/PolicyPalNetworkCrowdsale.sol
    
    /**
     * @title PPN Crowdsale
     * @dev Crowd Sale Contract
     */
    contract PolicyPalNetworkCrowdsale is CrowdsaleAuthorizer {
        /**
        * @dev Token Crowd Sale Contract Public Variables
        */
        address                 public multiSigWallet;
        PolicyPalNetworkToken   public token;
        uint256                 public raisedWei;
        bool                    public haltSale;
        uint                    public rate;
    
        /**
        * @dev Modifier for valid sale
        */
        modifier validSale() {
          require(!haltSale);
          require(saleStarted());
          require(!saleEnded());
          _;
        }
    
        /**
         * @dev Buy Event
         */
        event Buy(address _buyer, uint256 _tokens, uint256 _payedWei);
    
        /**
         * @dev Token Crowd Sale Contract Constructor
         * @param _admin - Address of the Admin
         * @param _multiSigWallet - Address of Multisig wallet
         * @param _totalTokenSupply - Total Token Supply
         * @param _premintedTokenSupply - Total preminted token supply
         * @param _saleStartTime - The Start Time of the Token Sale
         * @param _saleEndTime - The End Time of the Token Sale
         * @param _increaseMaxContribTime - Time to increase max contribution
         * @param _rate - Rate of ETH to PAL
         * @param _minContribution - Minimum ETH contribution per contributor
         * @param _maxContribution - Maximum ETH contribution per contributor
         */
        function PolicyPalNetworkCrowdsale(
            address _admin,
            address _multiSigWallet,
            uint256 _totalTokenSupply,
            uint256 _premintedTokenSupply,
            uint256 _presaleTokenSupply,
            uint256 _saleStartTime,
            uint256 _saleEndTime,
            uint256 _increaseMaxContribTime,
            uint    _rate,
            uint256 _minContribution,
            uint256 _maxContribution
        )
        CrowdsaleAuthorizer(
            _admin,
            _saleStartTime,
            _saleEndTime,
            _increaseMaxContribTime,
            _minContribution,
            _maxContribution
        )
            validAddress(_multiSigWallet)
            public
        {
            require(_totalTokenSupply > 0);
            require(_premintedTokenSupply > 0);
            require(_presaleTokenSupply > 0);
            require(_rate > 0);
            
            require(_premintedTokenSupply < _totalTokenSupply);
            require(_presaleTokenSupply < _totalTokenSupply);
    
            multiSigWallet = _multiSigWallet;
            rate = _rate;
    
            token = new PolicyPalNetworkToken(
                _totalTokenSupply,
                _admin
            );
    
            // transfer preminted tokens to company wallet
            token.transfer(multiSigWallet, _premintedTokenSupply);
            // transfer presale tokens to admin
            token.transfer(_admin, _presaleTokenSupply);
        }
    
        /**
         * @dev Token Crowd Sale Contract Halter
         * @param _halt - Flag to halt sale
         */
        function setHaltSale(bool _halt)
            onlyAdmin
            public
        {
            haltSale = _halt;
        }
    
        /**
         * @dev Token Crowd Sale payable
         */
        function() public payable {
            buy(msg.sender);
        }
    
        /**
         * @dev Token Crowd Sale Buy
         * @param _recipient - Address of the recipient
         */
        function buy(address _recipient) public payable
            validSale
            validAddress(_recipient)
            returns(uint256)
        {
            // Get the contributor's eligible amount
            uint256 weiContributionAllowed = eligibleAmountCheck(_recipient, msg.value);
            require(weiContributionAllowed > 0);
    
            // Get tokens remaining for sale
            uint256 tokensRemaining = token.balanceOf(address(this));
            require(tokensRemaining > 0);
    
            // Get tokens that the contributor will receive
            uint256 receivedTokens = weiContributionAllowed.mul(rate);
    
            // Check remaining tokens
            // If lesser, update tokens to be transfer and contribution allowed
            if (receivedTokens > tokensRemaining) {
                receivedTokens = tokensRemaining;
                weiContributionAllowed = tokensRemaining.div(rate);
            }
    
            // Transfer tokens to contributor
            assert(token.transfer(_recipient, receivedTokens));
    
            // Send ETH payment to MultiSig Wallet
            sendETHToMultiSig(weiContributionAllowed);
            raisedWei = raisedWei.add(weiContributionAllowed);
    
            // Check weiContributionAllowed is larger than value sent
            // If larger, transfer the excess back to the contributor
            if (msg.value > weiContributionAllowed) {
                msg.sender.transfer(msg.value.sub(weiContributionAllowed));
            }
    
            // Broadcast event
            Buy(_recipient, receivedTokens, weiContributionAllowed);
    
            return weiContributionAllowed;
        }
    
        /**
         * @dev Token Crowd Sale Emergency Drain
         *      In case something went wrong and ETH is stuck in contract
         * @param _anyToken - Token to drain
         */
        function emergencyDrain(ERC20 _anyToken) public
            onlyAdmin
            returns(bool)
        {
            if (this.balance > 0) {
                sendETHToMultiSig(this.balance);
            }
            if (_anyToken != address(0x0)) {
                assert(_anyToken.transfer(multiSigWallet, _anyToken.balanceOf(this)));
            }
            return true;
        }
    
        /**
         * @dev Token Crowd Sale
         *      Transfer ETH to MultiSig Wallet
         * @param _value - Value of ETH to send
         */
        function sendETHToMultiSig(uint256 _value) internal {
            multiSigWallet.transfer(_value);
        }
    }