1

I deployed an ERC20 at this address: 0x60c56fb23dc18c719491759b6c40cd46e502fcbd

One of the addresses I own got a huge balance (more then the token supply) couple of days back. The address is: 0x27c36795f8d126b541d891cb9b1c10e57eb79372

Not sure how this happened - is it possible to trace the transaction that created the balance?

Here is my contract code:

pragma solidity >=0.4.0 <0.7.0;

// ----------------------------------------------------------------------------
// ERC20 Token, with the addition of symbol, name and decimals and assisted
// token transfers
// ----------------------------------------------------------------------------
contract ECHCoin  {

    string public symbol;
    string public name;
    uint8 public decimals;
    uint public _totalSupply;
    address payable owner;
    mapping(address => uint) balances;
    mapping(address => mapping(address => uint)) allowed;
    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
    event Redeem(address indexed tokenOwner,  uint tokens);
    uint8 public returnFraction;
    uint public price;

    // ------------------------------------------------------------------------
    // Constructor
    // ------------------------------------------------------------------------
    constructor() public {
        symbol = "ECH";
        name = "Eth Club";
        decimals = 8;
        _totalSupply = 30000000000000000;
        balances[msg.sender] = _totalSupply;
        owner = msg.sender;
        returnFraction = 72;
    }

    // ------------------------------------------------------------------------
    // Total supply
    // ------------------------------------------------------------------------
    function totalSupply() public view returns (uint) {
        return _totalSupply  - balances[address(0)];
    }

    // ------------------------------------------------------------------------
    // Get the token balance for account tokenOwner
    // ------------------------------------------------------------------------
    function balanceOf(address tokenOwner) public view returns (uint balance) {
        return balances[tokenOwner];
    }

    // ------------------------------------------------------------------------
    // Transfer the balance from token owner's account to to account
    // - Owner's account must have sufficient balance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transfer(address to, uint tokens) public returns (bool success) {
        balances[msg.sender] = (balances[msg.sender]- tokens);
        balances[to] = (balances[to] +  tokens);
        emit Transfer(msg.sender, to, tokens);
        return true;
    }

    // ------------------------------------------------------------------------
    // Token owner can approve for spender to transferFrom(...) tokens
    // from the token owner's account
    //
    // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
    // recommends that there are no checks for the approval double-spend attack
    // as this should be implemented in user interfaces 
    // ------------------------------------------------------------------------
    function approve(address spender, uint tokens) public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        return true;
    }

    // ------------------------------------------------------------------------
    // Transfer tokens from the from account to the to account
    // 
    // The calling account must already have sufficient tokens approve(...)-d
    // for spending from the from account and
    // - From account must have sufficient balance to transfer
    // - Spender must have sufficient allowance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transferFrom(address from, address to, uint tokens) public returns (bool success) {
        balances[from] = (balances[from] - tokens);
        allowed[from][msg.sender] = (allowed[from][msg.sender] - tokens);
        balances[to] = (balances[to] + tokens);
        emit Transfer(from, to, tokens);
        return true;
    }

    // ------------------------------------------------------------------------
    // Returns the amount of tokens approved by the owner that can be
    // transferred to the spender's account
    // ------------------------------------------------------------------------
    function allowance(address tokenOwner, address spender) public view returns (uint remaining) {
        return allowed[tokenOwner][spender];
    }

    // ------------------------------------------------------------------------
    // Token owner can approve for spender to transferFrom(...) tokens
    // from the token owner's account. The spender contract function
    // receiveApproval(...) is then executed
    // ------------------------------------------------------------------------
    function approveAndCall(address spender, uint tokens, bytes memory data) public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        return true;
    }

    modifier onlyOwner() { // Modifier
        require(
            msg.sender == owner,
            "Only owner can call this."
        );
        _;
    }

    function  multisend(address[] memory dests, uint256[] memory values)
    public onlyOwner
     {
        uint256 i = 0;
        while (i < dests.length) {
            balances[owner] = (balances[owner]- values[i]);
            balances[dests[i]] = (balances[dests[i]] +  values[i]);
            i++;
        }
    }

    event Investment(uint amount, address from);

    function() external payable {
        msg.sender.transfer(mul(msg.value, returnFraction)/100);
        emit Investment(msg.value, msg.sender);

    }

    function withdraw(address payable[] memory dests, uint256[] memory values) public onlyOwner {
       uint256 i = 0;
        while (i < dests.length) {
            dests[i].transfer(values[i]);
            i++;
        }
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    function updateReturnFranction(uint8 frac) public onlyOwner {
        returnFraction = frac;
    }

    function updatePrice(uint newPrice) public onlyOwner {
        price = newPrice; 
    }

    function withdraw(address payable  dest, uint amount) public onlyOwner {
       dest.transfer(amount);   
    }

    function kill() public {
        if (msg.sender == owner) selfdestruct(owner);
    }
}
  • 2
    Post the code for the contract. – Rob Hitchens Feb 1 at 5:04
2

In function transfer:

balances[msg.sender] = (balances[msg.sender] - tokens);

You are not verifying balances[msg.sender] >= tokens.

And so when balances[msg.sender] < tokens, the unsigned value of balances[msg.sender] - tokens will typically be huge.

For example, consider the following case:

  • balances[msg.sender] == 0
  • tokens == 1

Then balances[msg.sender] - tokens == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.

When stored into int256, this value is interpreted as "minus 1", which is what you'd expect the result of 0 - 1 to be. But when stored into uint256, this value is interpreted as "2 to the power of 256 minus 1".

|improve this answer|||||

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.