Solidity gotchas

As you are here, you probably already know what Solidity is. For those who don’t, – Solidity is a programming language used for development of Ethereum smart contracts. After compilation, bytecode can be deployed to given contract account and be used for interaction with other Ethereum accounts on the network.

Problems of Solidity

Solidity itself has one problem when you are newcomer from another programming language.  As smart contract code is executed on EVM, it means that eventually this code is executed on all nodes around the network (as this is a part of not only mining but also validation process). That is why Solidity is more low-level language that commonly used JavaScript, Java or C#. Also, this is what leads us to the first rule of Solidity:

KISS

KISS is a principle used in modern software development, having its origins in US Navy. Nothing refers better to smart contract development than this rule – “keep it simple, stupid”. When creating Smart Contracts for Ethereum mainnet use, one has to care about every single function or even declaration types. The more complicated is your contract, the more gas will be used to execute its methods. That means in the end,  its users will have to pay more. In smart contract, execute as least logic as you possibly can. Handling the rest of it in your backend environment will cost you much less trouble and you users much less money. As an example, we can just mention Solidity bytes32 and string  (String is a dynamically-sized byte array in Solidity!) – which one would you use to store a hash in it? The answer is bytes32, as it is equivalent to the single word in Solidity and consumes much less gas. String stores additional data about UTF-8 encoding and bytes32 is just Hex representation of it. Another example is making cryptographic operations outside Solidity as it is costly to do it inside it.

Gas usage

When using your own, private blockchain, it does not mean you don’t have to worry about gas usage. You can set its value to 0, but still, each block has got a gas limit. Imagine we have a loop, that is based on internal storage of Smart Contract. When adding some data to storage, the loop will execute more and more operations, consuming more and more “free” gas. At some point, the gas limit will be exceeded and no other operations will be able to proceed – our contract will be blocked. Remedy? If not necessary, avoid loops in your contract, especially those based on storage values.

Looping

Let’s image you create a loop like this in your Smart Contract, where a is some array of length 1000.

for (var i = 0; i < a.length; i ++)

How many times will it be executed?

The answer is forever ( until it runs out of gas ;)). var is Solidity is equivalent to uint8, which means that it can only store values up to 255. To avoid it from happening, make sure that you execute following instead, where uint is equivalent to uint256

for (uint i = 0; i < a.length; i ++)

Tx.origin vs msg. sender

In Solidity, you can easily determine who is an entity that committed a transaction. When you come across examples on the internet, you can see that both tx.origin and msg.sender are used. Both have the same purpose – determine the sender of the transaction. Never use tx.origin. Msg.sender will always provide direct sender of the message (including contract). Tx.origin will provide the user address who send the message, but it does not take contract addresses into account.  Assume you are interacting with malicious contract, without your knowledge. Then, malicious contract passes your call (with its changes) to the original contract. When using msg.sender attack is compromised as msg.sender indicates malicious contract address. When using tx.origin, your address will be exposed as the sender of the message!

Returning array of strings, returning struct

This is not possible in Solidity (at this moment). Remeber that string is a dynamically-sized array so returning array of dynamic arrays is not possible. Also, it is not possible to return Solidity internal “structs” in your backend. There is, however, a simple workaround, when using structs in mappings. Simply mark your mapping as public. Then, when getting results from your mapping, struct will be returned as an array.

Comparing strings

It is not possible to compare strings in Solidity. There are two hacks for it:

  1. Compare strings hashes. This is a simple way to do it inside your contract, but note that hashing functions use lot of gas
  2. Use this string utils contract

Iterating through mappings, deleting from array or mappings

You cannot iterate through mappings. Mapping is simply keccak hash of your key pointing to some storage value. Remeber that even when you did not initialize some key in your mapping, it is still pointing to 0 equivalent of your value (so if you have a mapping of (address =>bool), 0 equivalent is false.

When deleting some value from mappings or array, note that array is not going to automatically become smaller – deleting simply sets all included values to their 0 equivalent.

Summary

Surely when developing smart contract at the beginning, a developer can say that Solidity language is hard to work with. However, when going deeper inside it, one can understand the reason for its construction and hopefully accept and like it.

Leave a Reply

Your email address will not be published. Required fields are marked *