I have an ERC20 smart contract that allocates tokens to each user based on the amount of Ether it receives. I have added explicit checks to ensure any transaction with msg.value < the minimum token price are rejected.

My minimum token price is 1 finney. Now when I send an amount of 0.0001 to the smart contract. I expect the transaction to fail. However this doesn't seem to happen. Is this a bug with Solidity precision? Or am I doing something wrong?

My code is as follows:

pragma solidity ^0.4.11;

import './TeamAllocation.sol';
import './UnsoldAllocation.sol';
import './zeppelin/SafeMath.sol';
import './zeppelin/token/StandardToken.sol';
import './zeppelin/ownership/Ownable.sol';

contract Token is StandardToken, Ownable {

    using SafeMath for uint;
    string public constant name = "SAMPLE";
    string public constant symbol = "SMP";
    uint public constant decimals = 18;

    TeamAllocation teamAllocation;
    UnsoldAllocation unsoldTokens;

    // Funding amount in Finney
    uint public constant tokenPrice  = 1 finney;

    // Multisigwallet where the proceeds will be stored.
    address public tokenFactory;
    // Multisigwallet to unsold tokens
    address public futureSale;

    // Sale Period
    uint public salePeriod;

    uint fundingStartBlock;
    uint fundingStopBlock;

    // flags whether ICO is afoot.
    bool fundingMode = true;

    //total used tokens
    uint totalUsedTokens;

    event Refund(address indexed _from,uint256 _value);
    event Migrate(address indexed _from, address indexed _to, uint256 _value);

    modifier isNotFundable() {
        if (fundingMode) throw;

    modifier isFundable() {
        if (!fundingMode) throw;

    function Token(address _tokenFactory, uint256 _fundingStartBlock, uint256 _fundingStopBlock, address _icedWallet) {
      salePeriod = now.add(60 hours);
      tokenFactory = _tokenFactory;
      fundingStartBlock = _fundingStartBlock;
      fundingStopBlock = _fundingStopBlock;
      totalUsedTokens = 0;
      totalSupply = 800000000;
      futureSale = _icedWallet;

    function pause() onlyOwner isFundable external returns (bool) {
      fundingMode = false;

    function() payable isFundable external {
      if(now > salePeriod) throw;
      if(block.number < fundingStartBlock) throw;
      if(block.number > fundingStopBlock) throw;
      if(totalUsedTokens >= totalSupply) throw;

      if (msg.value < tokenPrice) throw;

      //transfer money to tokenFactory MultisigWallet
      if(!tokenFactory.send(msg.value)) throw;

      uint numTokens = msg.value.div(tokenPrice);
      totalUsedTokens = totalUsedTokens.add(numTokens);
      if (totalUsedTokens > totalSupply) throw;

      balances[msg.sender] = balances[msg.sender].add(numTokens);

      Transfer(0, msg.sender, numTokens);

    //@notice Function that reports how long the sale is active
    function checkSalePeriod() external constant returns (uint) {
      return salePeriod;

    //@notice Function that reports whether funding is active.
    function fundingActive() constant isFundable external returns (bool){
      if(block.number < fundingStartBlock || block.number > fundingStopBlock || totalUsedTokens > totalSupply){
        return false;
      return true;

    //@notice Function reports the number of tokens available for sale
    function numberOfTokensLeft() constant returns (uint256) {
      if (block.number > fundingStopBlock) {
        return 0;
      uint tokensAvailableForSale = totalSupply - totalUsedTokens;
      return tokensAvailableForSale;

    function finalize() isFundable onlyOwner external {
      if ((block.number <= fundingStopBlock ||
        totalUsedTokens < minTokensForSale) &&
        totalUsedTokens < totalSupply) throw;

      if(futureSale == address(0)) throw;

        // switch funding mode off
        fundingMode = false;

        //Allot team tokens to a smart contract which will frozen for 9 months

        //transfer any balance available to Pillar Multisig Wallet
        if (!tokenFactory.send(this.balance)) throw;

    function refund() isFundable external {
      if(block.number <= fundingStopBlock) throw;
      if(totalUsedTokens >= minTokensForSale) throw;

      uint plrValue = balances[msg.sender];
      if(plrValue == 0) throw;

      balances[msg.sender] = 0;

      uint ethValue = plrValue.mul(tokenPrice);
      if(!msg.sender.send(ethValue)) throw;
      Refund(msg.sender, ethValue);

Any help will be truly appreciated.

  • Updated the question with full code. Deployed this contract and sent 0.0001 ether to it. I expect the contract to fail. But it doesn't! – Parthasarathy Ramanujam Jun 29 '17 at 17:33
  • Deployed contract on Rinkeby address: 0x256edad64ad7b0ddd2a6bd397bb248dcddcf2796, If I send anything less than 1 finney to this contract I expect the transaction to fail. But this doesn't seem to happen. rinkeby.etherscan.io/tx/… – Parthasarathy Ramanujam Jun 29 '17 at 17:47
  • My understand is that msg.value will be reported in wei, while the tokenPrice is in finney. The comparisons should be valid anyway, won't it? – Parthasarathy Ramanujam Jun 29 '17 at 18:25

Your transaction is indeed throwing and working as expected. The issue is the blockchain explorer. Unfortunately etherscan is falsely reporting it as having succeeded.

Look here: https://rinkeby.etherscan.io/tx/0xd1a1fbe2f094a90e94eebe04d5005518a5359297c711641dbde46881dee7c1e9

You can in fact see that all gas was consumed by the transaction, which should probably only use a 100k at most. It is fair to presume from this that there was an exception. But let's have a closer look by opening the Geth DebugTrace offered by etherscan.


On the VM trace we indeed do see that the expected exception was encountered, and the exception occurs on the msg.value < tokenPrice check, so no need to worry.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.