Token Muling — Or How To Transfer Many Tokens and Save Gas

Reducing operational costs for ICOs by using batched transfers

Daniel Fischmann
ChainSecurity
4 min readMay 7, 2018

--

ERC20 tokens have become a cornerstone of the Ethereum ecosystem, hence leading to frequent transfers. However, the gas costs for such ERC20 token transfers can greatly differ, especially when it comes to multiple subsequent, so called batched, transfers. Lowering the gas costs of these transfers lessens operational costs and reduces the load for the Ethereum network. In this article, we quantitatively review existing implementations and propose a solution that improves upon them.

Given that tokens are long-term projects, we can consider the gas costs during deployment negligible in comparison to the operational gas costs. More so, for many contracts and especially crowdsales with delayed token emission (read: ICOs or TGEs) the token transfers will be a significant part of the operator’s expenses.

We will consider five methods, three of which are the most commonly used ones in practice, namely the transfer functions as implemented by Ethereum Solidity, Zeppelin Solutions and ConsenSys, which are publicly available on GitHub. The other two methods take a batched approach, with the first one of these encountered during a security audit and the second one being our proposed optimized version. Let’s have a look:

  • Encountered batched method:
  • Our optimized version of a batched approach:

We deploy the above methods on Ganache with Truffle v6.1.0 (with solc v0.4.22) and then compare the gas costs incurred by using the corresponding transfer functions on a test token. Concretely, a contract with 1'000'000 tokens was initialized, sending 1000 tokens per transfer to 1000 different accounts. To come as close as possible to the main network parameters, one can look up the current gas price at [1] or [2] and initialize the test chain accordingly. We note that the number given in the batch-transfer rows refers to the fixed size of a batch. Without further ado, here are the results:

We directly observe significant differences between the implementations in terms of gas costs. Clearly, the highest cost is imposed by the Ethereum Solidity implementation. The reason for this is the fact that code is shared between different transfer-functions in the code base: both transfer() and transferFrom() call the same underlying function doTransfer(), which is useful in terms of deployment costs and avoiding duplicate code snippets, but becomes increasingly expensive, since with each token transfer an additional function call is executed.

The difference in costs incurred between the OpenZeppelin and ConsenSys methods can be explained by two observations: Firstly, OpenZeppelin relies on the usage of its SafeMath library, using custom functions for arithmetic operations which are checked for under-/overflows. These checks are important and improve security, however result in higher gas costs, once again because of the additional function calls. Secondly, the ConsenSys version doesn’t perform the check require(_to != address(0)).

Finally, let us consider the batch transfers. It can be seen that these methods use notably less gas than all other ones and more so, that an increasing batch size further reduces the costs.

Why is this the case?

For starters, using batches reduces gas costs by reducing the number of balance lookups. But even more so, when we call our batch function one transaction is triggered, whereas otherwise, we trigger as many transactions as transfers initiated (and those can be quite a few). This also explains why using a bigger batch size makes the transfers even cheaper!

But what about the last optimization? Here we use a few tricks: The sender balance, recipient address and value are saved in separate variables to enable caching. Moreover, not using transfer() and directly reducing the balances allows to save upon the invocation cost of the function, additionally dropping the cost for our optimized version.

All of these benchmarks leave us with the following takeaway: if possible, batch transfers are a good way to save gas — especially if a lot of token transfers are involved, the saving of more than half of your costs (53.4%) by adapting the optimized batched approach over the Ethereum Solidity one becomes highly relevant, with ICOs being a great use case for such optimizations.

Stay tuned for further in depth gas costs analysis soon!

Disclaimer: The above code snippets are for educational purposes only. They are provided “as is”, without representations and warranties of any kind and ChainSecurity disclaims any liability for damage arising out of, or in connection with, this blog post and its code snippets. If you want secure smart contracts, you should have your code audited.

--

--