ETH Price: $2,618.63 (-3.62%)

Transaction Decoder

Block:
12147348 at Mar-31-2021 01:19:51 PM +UTC
Transaction Fee:
0.00900218 ETH $23.57
Gas Used:
62,084 Gas / 145 Gwei

Emitted Events:

275 ENJToken.Transfer( _from=[Sender] 0x7aeab7c231c88611a2ad434d3a2715ab3c91f406, _to=[Receiver] AdminUpgradeabilityProxy, _value=10000000000000000000 )
276 AdminUpgradeabilityProxy.0xa1144a9ee937d96489bd5084ca2d72792545c4f293959fa7db1b8f85e9e254c2( 0xa1144a9ee937d96489bd5084ca2d72792545c4f293959fa7db1b8f85e9e254c2, 0x7911704bc67c2bf3ca6f8050d633e7f54b4be9335e3069912c3bc3ea9c7232af, 0x000000000000000000000000000000000000000000000000000000000000000c, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000007aeab7c231c88611a2ad434d3a2715ab3c91f406, 0000000000000000000000000000000000000000000000008ac7230489e80000 )

Account State Difference:

  Address   Before After State Difference Code
(Mining Express)
180.381776320601483753 Eth180.390778500601483753 Eth0.00900218
0x1dd0C7cB...30842E3b2
0x7AEAB7C2...b3C91F406
1.930708937970979383 Eth
Nonce: 1110
1.921706757970979383 Eth
Nonce: 1111
0.00900218
0xF629cBd9...80E2a3B9c

Execution Trace

AdminUpgradeabilityProxy.9e2ac9fa( )
  • 0x6449ed22142a506d21bd96862b815fe17f044776.9e2ac9fa( )
    • ENJToken.transferFrom( _from=0x7AEAB7C231c88611a2ad434d3A2715Ab3C91F406, _to=0x1dd0C7cBF5b420648Fe37EDCdc64cBE30842E3b2, _value=10000000000000000000 ) => ( success=True )
      File 1 of 2: AdminUpgradeabilityProxy
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.6.0;
      import './UpgradeabilityProxy.sol';
      /**
       * @title AdminUpgradeabilityProxy
       * @dev This contract combines an upgradeability proxy with an authorization
       * mechanism for administrative tasks.
       * All external functions in this contract must be guarded by the
       * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
       * feature proposal that would enable this to be done automatically.
       */
      contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
        /**
         * Contract constructor.
         * @param _logic address of the initial implementation.
         * @param _admin Address of the proxy administrator.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable {
          assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
          _setAdmin(_admin);
        }
        /**
         * @dev Emitted when the administration has been transferred.
         * @param previousAdmin Address of the previous admin.
         * @param newAdmin Address of the new admin.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
        /**
         * @dev Storage slot with the admin of the contract.
         * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
         * validated in the constructor.
         */
        bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
        /**
         * @dev Modifier to check whether the `msg.sender` is the admin.
         * If it is, it will run the function. Otherwise, it will delegate the call
         * to the implementation.
         */
        modifier ifAdmin() {
          if (msg.sender == _admin()) {
            _;
          } else {
            _fallback();
          }
        }
        /**
         * @return The address of the proxy admin.
         */
        function admin() external ifAdmin returns (address) {
          return _admin();
        }
        /**
         * @return The address of the implementation.
         */
        function implementation() external ifAdmin returns (address) {
          return _implementation();
        }
        /**
         * @dev Changes the admin of the proxy.
         * Only the current admin can call this function.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeAdmin(address newAdmin) external ifAdmin {
          require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
          emit AdminChanged(_admin(), newAdmin);
          _setAdmin(newAdmin);
        }
        /**
         * @dev Upgrade the backing implementation of the proxy.
         * Only the admin can call this function.
         * @param newImplementation Address of the new implementation.
         */
        function upgradeTo(address newImplementation) external ifAdmin {
          _upgradeTo(newImplementation);
        }
        /**
         * @dev Upgrade the backing implementation of the proxy and call a function
         * on the new implementation.
         * This is useful to initialize the proxied contract.
         * @param newImplementation Address of the new implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
          _upgradeTo(newImplementation);
          (bool success,) = newImplementation.delegatecall(data);
          require(success);
        }
        /**
         * @return adm The admin slot.
         */
        function _admin() internal view returns (address adm) {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            adm := sload(slot)
          }
        }
        /**
         * @dev Sets the address of the proxy admin.
         * @param newAdmin Address of the new proxy admin.
         */
        function _setAdmin(address newAdmin) internal {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            sstore(slot, newAdmin)
          }
        }
        /**
         * @dev Only fall back when the sender is not the admin.
         */
        function _willFallback() internal override virtual {
          require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
          super._willFallback();
        }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.6.0;
      import './Proxy.sol';
      import '@openzeppelin/contracts/utils/Address.sol';
      /**
       * @title UpgradeabilityProxy
       * @dev This contract implements a proxy that allows to change the
       * implementation address to which it will delegate.
       * Such a change is called an implementation upgrade.
       */
      contract UpgradeabilityProxy is Proxy {
        /**
         * @dev Contract constructor.
         * @param _logic Address of the initial implementation.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, bytes memory _data) public payable {
          assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
          _setImplementation(_logic);
          if(_data.length > 0) {
            (bool success,) = _logic.delegatecall(_data);
            require(success);
          }
        }  
        /**
         * @dev Emitted when the implementation is upgraded.
         * @param implementation Address of the new implementation.
         */
        event Upgraded(address indexed implementation);
        /**
         * @dev Storage slot with the address of the current implementation.
         * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
         * validated in the constructor.
         */
        bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
        /**
         * @dev Returns the current implementation.
         * @return impl Address of the current implementation
         */
        function _implementation() internal override view returns (address impl) {
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            impl := sload(slot)
          }
        }
        /**
         * @dev Upgrades the proxy to a new implementation.
         * @param newImplementation Address of the new implementation.
         */
        function _upgradeTo(address newImplementation) internal {
          _setImplementation(newImplementation);
          emit Upgraded(newImplementation);
        }
        /**
         * @dev Sets the implementation address of the proxy.
         * @param newImplementation Address of the new implementation.
         */
        function _setImplementation(address newImplementation) internal {
          require(Address.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            sstore(slot, newImplementation)
          }
        }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.6.0;
      /**
       * @title Proxy
       * @dev Implements delegation of calls to other contracts, with proper
       * forwarding of return values and bubbling of failures.
       * It defines a fallback function that delegates all calls to the address
       * returned by the abstract _implementation() internal function.
       */
      abstract contract Proxy {
        /**
         * @dev Fallback function.
         * Implemented entirely in `_fallback`.
         */
        fallback () payable external {
          _fallback();
        }
        /**
         * @dev Receive function.
         * Implemented entirely in `_fallback`.
         */
        receive () payable external {
          _fallback();
        }
        /**
         * @return The Address of the implementation.
         */
        function _implementation() internal virtual view returns (address);
        /**
         * @dev Delegates execution to an implementation contract.
         * This is a low level function that doesn't return to its internal call site.
         * It will return to the external caller whatever the implementation returns.
         * @param implementation Address to delegate.
         */
        function _delegate(address implementation) internal {
          assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())
            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())
            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
          }
        }
        /**
         * @dev Function that is run as the first thing in the fallback function.
         * Can be redefined in derived contracts to add functionality.
         * Redefinitions must call super._willFallback().
         */
        function _willFallback() internal virtual {
        }
        /**
         * @dev fallback implementation.
         * Extracted to enable manual triggering.
         */
        function _fallback() internal {
          _willFallback();
          _delegate(_implementation());
        }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.2 <0.8.0;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
              uint256 size;
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: value }(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.staticcall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      

      File 2 of 2: ENJToken
      pragma solidity ^0.4.15;
      /*
          Utilities & Common Modifiers
      */
      contract Utils {
          /**
              constructor
          */
          function Utils() {
          }
      
          // validates an address - currently only checks that it isn't null
          modifier validAddress(address _address) {
              require(_address != 0x0);
              _;
          }
      
          // verifies that the address is different than this contract address
          modifier notThis(address _address) {
              require(_address != address(this));
              _;
          }
      
          // Overflow protected math functions
      
          /**
              @dev returns the sum of _x and _y, asserts if the calculation overflows
      
              @param _x   value 1
              @param _y   value 2
      
              @return sum
          */
          function safeAdd(uint256 _x, uint256 _y) internal returns (uint256) {
              uint256 z = _x + _y;
              assert(z >= _x);
              return z;
          }
      
          /**
              @dev returns the difference of _x minus _y, asserts if the subtraction results in a negative number
      
              @param _x   minuend
              @param _y   subtrahend
      
              @return difference
          */
          function safeSub(uint256 _x, uint256 _y) internal returns (uint256) {
              assert(_x >= _y);
              return _x - _y;
          }
      
          /**
              @dev returns the product of multiplying _x by _y, asserts if the calculation overflows
      
              @param _x   factor 1
              @param _y   factor 2
      
              @return product
          */
          function safeMul(uint256 _x, uint256 _y) internal returns (uint256) {
              uint256 z = _x * _y;
              assert(_x == 0 || z / _x == _y);
              return z;
          }
      }
      
      /*
          ERC20 Standard Token interface
      */
      contract IERC20Token {
          // these functions aren't abstract since the compiler emits automatically generated getter functions as external
          function name() public constant returns (string) { name; }
          function symbol() public constant returns (string) { symbol; }
          function decimals() public constant returns (uint8) { decimals; }
          function totalSupply() public constant returns (uint256) { totalSupply; }
          function balanceOf(address _owner) public constant returns (uint256 balance) { _owner; balance; }
          function allowance(address _owner, address _spender) public constant returns (uint256 remaining) { _owner; _spender; remaining; }
      
          function transfer(address _to, uint256 _value) public returns (bool success);
          function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
          function approve(address _spender, uint256 _value) public returns (bool success);
      }
      
      
      /**
          ERC20 Standard Token implementation
      */
      contract ERC20Token is IERC20Token, Utils {
          string public standard = "Token 0.1";
          string public name = "";
          string public symbol = "";
          uint8 public decimals = 0;
          uint256 public totalSupply = 0;
          mapping (address => uint256) public balanceOf;
          mapping (address => mapping (address => uint256)) public allowance;
      
          event Transfer(address indexed _from, address indexed _to, uint256 _value);
          event Approval(address indexed _owner, address indexed _spender, uint256 _value);
      
          /**
              @dev constructor
      
              @param _name        token name
              @param _symbol      token symbol
              @param _decimals    decimal points, for display purposes
          */
          function ERC20Token(string _name, string _symbol, uint8 _decimals) {
              require(bytes(_name).length > 0 && bytes(_symbol).length > 0); // validate input
      
              name = _name;
              symbol = _symbol;
              decimals = _decimals;
          }
      
          /**
              @dev send coins
              throws on any error rather then return a false flag to minimize user errors
      
              @param _to      target address
              @param _value   transfer amount
      
              @return true if the transfer was successful, false if it wasn't
          */
          function transfer(address _to, uint256 _value)
              public
              validAddress(_to)
              returns (bool success)
          {
              balanceOf[msg.sender] = safeSub(balanceOf[msg.sender], _value);
              balanceOf[_to] = safeAdd(balanceOf[_to], _value);
              Transfer(msg.sender, _to, _value);
              return true;
          }
      
          /**
              @dev an account/contract attempts to get the coins
              throws on any error rather then return a false flag to minimize user errors
      
              @param _from    source address
              @param _to      target address
              @param _value   transfer amount
      
              @return true if the transfer was successful, false if it wasn't
          */
          function transferFrom(address _from, address _to, uint256 _value)
              public
              validAddress(_from)
              validAddress(_to)
              returns (bool success)
          {
              allowance[_from][msg.sender] = safeSub(allowance[_from][msg.sender], _value);
              balanceOf[_from] = safeSub(balanceOf[_from], _value);
              balanceOf[_to] = safeAdd(balanceOf[_to], _value);
              Transfer(_from, _to, _value);
              return true;
          }
      
          /**
              @dev allow another account/contract to spend some tokens on your behalf
              throws on any error rather then return a false flag to minimize user errors
      
              also, to minimize the risk of the approve/transferFrom attack vector
              (see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice
              in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value
      
              @param _spender approved address
              @param _value   allowance amount
      
              @return true if the approval was successful, false if it wasn't
          */
          function approve(address _spender, uint256 _value)
              public
              validAddress(_spender)
              returns (bool success)
          {
              // if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal
              require(_value == 0 || allowance[msg.sender][_spender] == 0);
      
              allowance[msg.sender][_spender] = _value;
              Approval(msg.sender, _spender, _value);
              return true;
          }
      }
      
      /*
          Owned contract interface
      */
      contract IOwned {
          // this function isn't abstract since the compiler emits automatically generated getter functions as external
          function owner() public constant returns (address) { owner; }
      
          function transferOwnership(address _newOwner) public;
          function acceptOwnership() public;
      }
      
      /*
          Provides support and utilities for contract ownership
      */
      contract Owned is IOwned {
          address public owner;
          address public newOwner;
      
          event OwnerUpdate(address _prevOwner, address _newOwner);
      
          /**
              @dev constructor
          */
          function Owned() {
              owner = msg.sender;
          }
      
          // allows execution by the owner only
          modifier ownerOnly {
              assert(msg.sender == owner);
              _;
          }
      
          /**
              @dev allows transferring the contract ownership
              the new owner still needs to accept the transfer
              can only be called by the contract owner
      
              @param _newOwner    new contract owner
          */
          function transferOwnership(address _newOwner) public ownerOnly {
              require(_newOwner != owner);
              newOwner = _newOwner;
          }
      
          /**
              @dev used by a new owner to accept an ownership transfer
          */
          function acceptOwnership() public {
              require(msg.sender == newOwner);
              OwnerUpdate(owner, newOwner);
              owner = newOwner;
              newOwner = 0x0;
          }
      }
      
      /*
          Token Holder interface
      */
      contract ITokenHolder is IOwned {
          function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
      }
      
      /*
          We consider every contract to be a 'token holder' since it's currently not possible
          for a contract to deny receiving tokens.
      
          The TokenHolder's contract sole purpose is to provide a safety mechanism that allows
          the owner to send tokens that were sent to the contract by mistake back to their sender.
      */
      contract TokenHolder is ITokenHolder, Owned, Utils {
          /**
              @dev constructor
          */
          function TokenHolder() {
          }
      
          /**
              @dev withdraws tokens held by the contract and sends them to an account
              can only be called by the owner
      
              @param _token   ERC20 token contract address
              @param _to      account to receive the new amount
              @param _amount  amount to withdraw
          */
          function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
              public
              ownerOnly
              validAddress(_token)
              validAddress(_to)
              notThis(_to)
          {
              assert(_token.transfer(_to, _amount));
          }
      }
      
      
      contract ENJToken is ERC20Token, TokenHolder {
      
      ///////////////////////////////////////// VARIABLE INITIALIZATION /////////////////////////////////////////
      
          uint256 constant public ENJ_UNIT = 10 ** 18;
          uint256 public totalSupply = 1 * (10**9) * ENJ_UNIT;
      
          //  Constants 
          uint256 constant public maxPresaleSupply = 600 * 10**6 * ENJ_UNIT;           // Total presale supply at max bonus
          uint256 constant public minCrowdsaleAllocation = 200 * 10**6 * ENJ_UNIT;     // Min amount for crowdsale
          uint256 constant public incentivisationAllocation = 100 * 10**6 * ENJ_UNIT;  // Incentivisation Allocation
          uint256 constant public advisorsAllocation = 26 * 10**6 * ENJ_UNIT;          // Advisors Allocation
          uint256 constant public enjinTeamAllocation = 74 * 10**6 * ENJ_UNIT;         // Enjin Team allocation
      
          address public crowdFundAddress;                                             // Address of the crowdfund
          address public advisorAddress;                                               // Enjin advisor's address
          address public incentivisationFundAddress;                                   // Address that holds the incentivization funds
          address public enjinTeamAddress;                                             // Enjin Team address
      
          //  Variables
      
          uint256 public totalAllocatedToAdvisors = 0;                                 // Counter to keep track of advisor token allocation
          uint256 public totalAllocatedToTeam = 0;                                     // Counter to keep track of team token allocation
          uint256 public totalAllocated = 0;                                           // Counter to keep track of overall token allocation
          uint256 constant public endTime = 1509494340;                                // 10/31/2017 @ 11:59pm (UTC) crowdsale end time (in seconds)
      
          bool internal isReleasedToPublic = false;                         // Flag to allow transfer/transferFrom before the end of the crowdfund
      
          uint256 internal teamTranchesReleased = 0;                          // Track how many tranches (allocations of 12.5% team tokens) have been released
          uint256 internal maxTeamTranches = 8;                               // The number of tranches allowed to the team until depleted
      
      ///////////////////////////////////////// MODIFIERS /////////////////////////////////////////
      
          // Enjin Team timelock    
          modifier safeTimelock() {
              require(now >= endTime + 6 * 4 weeks);
              _;
          }
      
          // Advisor Team timelock    
          modifier advisorTimelock() {
              require(now >= endTime + 2 * 4 weeks);
              _;
          }
      
          // Function only accessible by the Crowdfund contract
          modifier crowdfundOnly() {
              require(msg.sender == crowdFundAddress);
              _;
          }
      
          ///////////////////////////////////////// CONSTRUCTOR /////////////////////////////////////////
      
          /**
              @dev constructor
              @param _crowdFundAddress   Crowdfund address
              @param _advisorAddress     Advisor address
          */
          function ENJToken(address _crowdFundAddress, address _advisorAddress, address _incentivisationFundAddress, address _enjinTeamAddress)
          ERC20Token("Enjin Coin", "ENJ", 18)
           {
              crowdFundAddress = _crowdFundAddress;
              advisorAddress = _advisorAddress;
              enjinTeamAddress = _enjinTeamAddress;
              incentivisationFundAddress = _incentivisationFundAddress;
              balanceOf[_crowdFundAddress] = minCrowdsaleAllocation + maxPresaleSupply; // Total presale + crowdfund tokens
              balanceOf[_incentivisationFundAddress] = incentivisationAllocation;       // 10% Allocated for Marketing and Incentivisation
              totalAllocated += incentivisationAllocation;                              // Add to total Allocated funds
          }
      
      ///////////////////////////////////////// ERC20 OVERRIDE /////////////////////////////////////////
      
          /**
              @dev send coins
              throws on any error rather then return a false flag to minimize user errors
              in addition to the standard checks, the function throws if transfers are disabled
      
              @param _to      target address
              @param _value   transfer amount
      
              @return true if the transfer was successful, throws if it wasn't
          */
          function transfer(address _to, uint256 _value) public returns (bool success) {
              if (isTransferAllowed() == true || msg.sender == crowdFundAddress || msg.sender == incentivisationFundAddress) {
                  assert(super.transfer(_to, _value));
                  return true;
              }
              revert();        
          }
      
          /**
              @dev an account/contract attempts to get the coins
              throws on any error rather then return a false flag to minimize user errors
              in addition to the standard checks, the function throws if transfers are disabled
      
              @param _from    source address
              @param _to      target address
              @param _value   transfer amount
      
              @return true if the transfer was successful, throws if it wasn't
          */
          function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
              if (isTransferAllowed() == true || msg.sender == crowdFundAddress || msg.sender == incentivisationFundAddress) {        
                  assert(super.transferFrom(_from, _to, _value));
                  return true;
              }
              revert();
          }
      
      ///////////////////////////////////////// ALLOCATION FUNCTIONS /////////////////////////////////////////
      
          /**
              @dev Release one single tranche of the Enjin Team Token allocation
              throws if before timelock (6 months) ends and if not initiated by the owner of the contract
              returns true if valid
              Schedule goes as follows:
              3 months: 12.5% (this tranche can only be released after the initial 6 months has passed)
              6 months: 12.5%
              9 months: 12.5%
              12 months: 12.5%
              15 months: 12.5%
              18 months: 12.5%
              21 months: 12.5%
              24 months: 12.5%
              @return true if successful, throws if not
          */
          function releaseEnjinTeamTokens() safeTimelock ownerOnly returns(bool success) {
              require(totalAllocatedToTeam < enjinTeamAllocation);
      
              uint256 enjinTeamAlloc = enjinTeamAllocation / 1000;
              uint256 currentTranche = uint256(now - endTime) / 12 weeks;     // "months" after crowdsale end time (division floored)
      
              if(teamTranchesReleased < maxTeamTranches && currentTranche > teamTranchesReleased) {
                  teamTranchesReleased++;
      
                  uint256 amount = safeMul(enjinTeamAlloc, 125);
                  balanceOf[enjinTeamAddress] = safeAdd(balanceOf[enjinTeamAddress], amount);
                  Transfer(0x0, enjinTeamAddress, amount);
                  totalAllocated = safeAdd(totalAllocated, amount);
                  totalAllocatedToTeam = safeAdd(totalAllocatedToTeam, amount);
                  return true;
              }
              revert();
          }
      
          /**
              @dev release Advisors Token allocation
              throws if before timelock (2 months) ends or if no initiated by the advisors address
              or if there is no more allocation to give out
              returns true if valid
      
              @return true if successful, throws if not
          */
          function releaseAdvisorTokens() advisorTimelock ownerOnly returns(bool success) {
              require(totalAllocatedToAdvisors == 0);
              balanceOf[advisorAddress] = safeAdd(balanceOf[advisorAddress], advisorsAllocation);
              totalAllocated = safeAdd(totalAllocated, advisorsAllocation);
              totalAllocatedToAdvisors = advisorsAllocation;
              Transfer(0x0, advisorAddress, advisorsAllocation);
              return true;
          }
      
          /**
              @dev Retrieve unsold tokens from the crowdfund
              throws if before timelock (6 months from end of Crowdfund) ends and if no initiated by the owner of the contract
              returns true if valid
      
              @return true if successful, throws if not
          */
          function retrieveUnsoldTokens() safeTimelock ownerOnly returns(bool success) {
              uint256 amountOfTokens = balanceOf[crowdFundAddress];
              balanceOf[crowdFundAddress] = 0;
              balanceOf[incentivisationFundAddress] = safeAdd(balanceOf[incentivisationFundAddress], amountOfTokens);
              totalAllocated = safeAdd(totalAllocated, amountOfTokens);
              Transfer(crowdFundAddress, incentivisationFundAddress, amountOfTokens);
              return true;
          }
      
          /**
              @dev Keep track of token allocations
              can only be called by the crowdfund contract
          */
          function addToAllocation(uint256 _amount) crowdfundOnly {
              totalAllocated = safeAdd(totalAllocated, _amount);
          }
      
          /**
              @dev Function to allow transfers
              can only be called by the owner of the contract
              Transfers will be allowed regardless after the crowdfund end time.
          */
          function allowTransfers() ownerOnly {
              isReleasedToPublic = true;
          } 
      
          /**
              @dev User transfers are allowed/rejected
              Transfers are forbidden before the end of the crowdfund
          */
          function isTransferAllowed() internal constant returns(bool) {
              if (now > endTime || isReleasedToPublic == true) {
                  return true;
              }
              return false;
          }
      }