0

My Answer:

Ok, the problem is really with Ganache ... If I use geth, everything is alright.

... ... sorry for your time ...

---------------- The Original Question --------------------

My understanding is that ERC20 Token balances are stored in the storage trie which will change over time. Also, token.balanceOf(accountHash, {}, blockNumber) seems to return only the most recent balance no matter what that blockNumber is. I am wondering if the Ethereum chain rolls back and then grows along a new chain, how are ERC20 token balances treated?

I want to clarify a bit regarding the Rollback here. Consider a simplified chain with only 8 blocks, labeled by the order they are appended to the chain, (not the block number which defines the height of block.)

# blocks are labeled by the order they are added to the chain
               /--[6]<--[7]<--[8]
              /
[1]<--[2]<--[3]<--[4]<--[5]

Say there are transactions about a token (ERC20) in all these blocks.

When [5] is added to chain, it is in the longest chain and token.balanceOf['0xAA'] gives me balance1.

Now assume [6][7][8] are appended to [3], this branch becomes the longest, and all transactions in [4] and [5] become invalid.

Now assume I want to be safe so I only care about balance of a ERC20 token (not ETH) at the (N-4)th block. How can I get this balance.

I did my search and the only way is to use etherscan's API, which is not ok for heavy use. There are other people here and there asking similar questions and I totally feel their frustration.

Thank you.


Update on Apr 5.

I want to give more details about my attempts so far.

1) I am using Ubuntu 16.04 and geth 1.8.2-stable.

2) I started with running Ganache, the app with graphical UI.

3) My token is almost the same as the one in the official doc https://ethereum.org/token.

4) I deployed the token using eth.accounts[0] in geth (ganache generates 10 accounts with each holding 100 initial ETH for me)

$ geth attach http://localhost:8545

var tokenABI = [...];
var tokenFactory = eth.conract(tokenABI);
var byteCode = "0x" + "606..."

var token = tokenFactory.new(
    100,
    "TestCoin",
    "TSC",
    {
        from: eth.accounts[0],  // owner
        data: byteCode,
        gas: 4700000
    }, function(e, contract) {});

5) I did the following transactions:

// 3 ETH transactions
eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(5, "ether") });
eth.sendTransaction({from: eth.accounts[1], to: eth.accounts[2], value: web3.toWei(3, "ether") });
eth.sendTransaction({from: eth.accounts[2], to: eth.accounts[3], value: web3.toWei(1, "ether") });

// 3 token transfers
token.transfer(eth.accounts[1], web3.toWei(5, 'ether'), { from: eth.accounts[0] }, function(err, contract) {})
token.transfer(eth.accounts[2], web3.toWei(3, 'ether'), { from: eth.accounts[1] })
token.transfer(eth.accounts[3], web3.toWei(1, 'ether'), { from: eth.accounts[2] })

For the token, I expect the ending balance to be

account 0: 95
account 1: 2
account 2: 2
account 3: 1

My eth.blockNumber shows 7 blocks, which is correct (3 eth transfer, 1 contract creating, 3 token transfer). Any balance of account 3 is definitely less than 1 in ealier blocks. However, the following command returns 1 ether (in unit of Wei) for me:

> token.balanceOf(eth.accounts[3], 0, function(err, res) {console.log(err, res};)
null 1000000000000000000
undefined

No matter how I change that number, from 0 to 7, I always get the same output.

BTW, I have no trouble retrieving ETH holding in earlier blocks. This only happens with the Token. One thing I did notice is that my eth.syncing returns false, maybe because I am using Ganache?

6
  • If the Ethereum chain rolls back then the previous storage is recovered. Let's say we are at block 100 and someone buys 30 tokens of token A, now he has 40 tokens. Apparently this chain is a fork - we want to roll back to block 99, hence we do so. Now this person has 10 A tokens, his previous ETH is still his old amount of ETH and his transaction is still in queue (depending on wether the new fork has mined his transaction or not). TL;DR at rollback we recover an old version of complete blockchain.
    – JBrouwer
    Mar 31, 2018 at 10:12
  • Never hear of any rollback in blockchains. You may be referring to chain split, when someone mines 2 forks of a chain of blocks. The longer chain wins, and the node just deletes the shortest chain , and switches to the longest chain. Tokens are variables in the state , so the variables on the longest chain are chosen, that's it.
    – Nulik
    Mar 31, 2018 at 18:34
  • @MathematicalRain I understand how ETH are recovered. Here I am talking about the ERC20 tokens, which store balances in the smart contract as a mapping.
    – KurtZ
    Apr 2, 2018 at 3:35
  • Where did you find this token.balanceOf(accountHash, {}, blockNumber)? It makes no sense to me that this function should exist on-chain. Also, all storage is reverted when the chain is forked; thus mappings also. Ethereum chain rollbacks should never happen, this ruins the entire idea behind Ethereum, since now a centralized organisation can choose to do a "hard-fork" of the entire chain.
    – JBrouwer
    Apr 2, 2018 at 12:37
  • @MathematicalRain maybe i m not phrasing it correctly. but all I want is to find the ERC20 token balance of an address at a specific block. Is this possible? For that balanceOf function, I read it here ethereum.stackexchange.com/questions/42783/…
    – KurtZ
    Apr 4, 2018 at 23:30

1 Answer 1

0

Let's get this rollback clear first. In the example you provide, two blocks (4 and 5) get added to the chain. However, also a seperate chain gets mined, which makes (6,7,8) and thus takes over the chain with 4 and 5. It might happen if the network functions normally that at about the same time two miners create a block and send them in the network. At that point it is unclear what the 'real' block is and we thus are in a fork of the network.

In the provided example another block 5 is added, but suddenly a miner has generated 5,6 and 7 and publishes those, which is now the longest chain. This implies that this miner has >50% of the network hashrate. However, due to "luck" this might sometimes happen and therefore if you want your transactions to be checked securely you have to wait for a few blocks before you confirm this transaction "for real". (Like if you send someone ETH, wait 10 blocks and at that point it is really hard for "evil miners" to pick up again).

Now the second question, which basically comes down to "how can I get the balance of X on a certain block?". Basically you want to run a function on a certain block. This can be done via web3. First you generate a contract object and then you can use (javascript)

ERC20Contract.balanceOf(address, BlockNumber, function(){})

(Example above uses ERC20Contract as contract address, balanceOf is the contract function returning the balance of address, BlockNumber is the actual block you want to look in and the empty function is a callback function (some web3 implementations like MetaMask need those))

(See: https://github.com/ethereum/wiki/wiki/JavaScript-API at Contract Methods)

4
  • This is exactly my question. When I call ERC20Contract.balanceOf(address, BlockNumber, function(){}), no matter what BlockNumber i specify, I always get the same number - the latest balance. My test environment is Ganache + geth + javascript web3.
    – KurtZ
    Apr 5, 2018 at 2:21
  • What web3 version are you using?
    – JBrouwer
    Apr 5, 2018 at 8:51
  • I really appreciate your patience. I've edited the question. I am actually not using nodejs, i am using geth 1.8.2-stable.
    – KurtZ
    Apr 5, 2018 at 10:12
  • I don't really know the geth syntax, but it should be the same. Check their documentation on what block they hook to.
    – JBrouwer
    Apr 5, 2018 at 22:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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