blockchain-technology
May 18, 2018

Journey of Learning Solidity — Tips And Tricks From Java Developer’s Perspective

Dominik Hys

During the last year alone all of the cryptocurrencies’ market cap boomed from $17B in January 2017 up to a whopping $830B in January 2018. It was a very sudden surge in interest and adoption taking into account that it took almost four years to reach $17B mark (May 2013 — January 2017). But cryptocurrencies are not only about coins and money. There’s also a blockchain part which can be even more interesting in terms of technology. Seeing the growing interest in the community, I decided to take a closer look at it, learn how to develop decentralised applications and see how does it differ from developing in Java and Kotlin in which I had been writing code every day.

Ethereum blockchain applications

With a launch of the Ethereum platform creating your own blockchain applications (so called dapps) and writing smart contracts (in short: pieces of code residing on the blockchain) became much simpler. The most popular way of doing so became via Solidity — a new programming language created specifically for this purpose. After simplicity, adoption followed — people started creating hundreds of decentralised applications. The most popular of them all is CryptoKitties, which lets you buy and breed virtual kittens. At one point it was so widely used that it slowed down the whole Ethereum network. People were buying and selling their virtual pets so quickly that all of the transactions amassed to over $24M. The most expensive kitten was sold for over $100K. Seeing how fast this environment grows, I tried to learn Solidity. To do this, I dropped Android development for a few months and started writing smart contracts full time.

Object-oriented programming vs Design by contract

Solidity is a programming language created to follow “design by contract” approach to creating software. It is not a new concept — it was first created by Bertrand Meyer in 1986 while working on a new programming language: Eiffel. It encourages developers to define formal preconditions, postconditions and invariants for every software component. These formal specifications are called “contracts”. In Solidity they are named that as well and are very similar to Java Classes. They both can have constructors, private and public methods, global and local variables and can be instantiated. However, Solidity contracts also have public addresses in the blockchain (after being deployed) and can store and send value.

Similarities and differences between Solidity and Java

The biggest difference in how the code looks in these two languages are modifiers and events. Both are usually declared at the beginning of a contract.

  • Modifiers

Modifiers are used to restrict who can make modifications to the contract’s state or call the contract’s functions and are reusable (can be used in multiple functions). They are also a tool to enforce function’s pre- and post-conditions.

  • Events

Events allow the usage of the Ethereum Virtual Machine (EVM) logging facilities and can be used to call callbacks which listen for them.

Events are inheritable members of contracts. When they are called, they cause the arguments to be stored in the transaction’s log — a special data structure in the blockchain. These logs are associated with the address of the contract and will be incorporated into the blockchain and stay there as long as a block is accessible. Log and event data is not accessible from within contracts (not even from the contract that created them).

  • Multiple inheritance

Solidity supports multiple inheritance by copying code including polymorphism. All function calls are virtual, which means that the most derived function is called, except when the contract name is explicitly given. When a contract inherits from multiple contracts, only a single contract is created on the blockchain, and the code from all the base contracts is copied into the created contract. The general inheritance system is very similar to Python’s, especially concerning multiple inheritance.

  • Function visibility: external

While in Solidity almost all types of function visibility (private, public and internal) are intuitive and similar to these in Java, one is different. A function can be declared as external which means it can be called only from other contracts and by transactions. Calling it internally is impossible.

  • Function modifiers: pure, view, payable

When a function is declared as pure, it cannot modify or even access the state (variables, mappings, arrays, etc.). It’s the most restrictive modifier but it is the most secure and saves the most gas when applied.

View is a slightly more allowing modifier. It basically acts the same as pure but allows access to the state (but it still cannot modify it though).

When we want a function to be able to receive Ether together with a call, we declare it as payable. It allows for money transfers, deposits and basically handling money in every way needed.

  • Additional data

When a function is called via a transaction, it can access additional information about the caller that is passed automatically, e.g. sender’s address (msg.sender), amount of Ether send (msg.value) or remaining gas (msg.gas) (see the gist above for the usage examples).

  • Data structures

While Java has many complex and simple data structures built in, in Solidity there are only two of them: mapping and array. The first one is a simplified version of Java map, meaning it can store key-value pairs and retrieve value stored under a given key, but not much else. It cannot be iterated over and both key and value sets are not retrievable. The latter will be talked about in the next paragraph (see the gist above for the example of a mapping).

  • Optimisations

Writing in Solidity requires paying attention to many more details than when writing in Java. Each transaction is an execution of a contract’s function which triggers operations. Each operation in turn costs gas which we need to pay in Ether (beside any Ether we want to send along). When the gas limit is exceeded, the whole function is being reverted and everything is restored to the state from before the function was called. In Java however even the code which is not optimised in any way will execute properly and the only side effect would be a longer runtime of the application.

Pitfalls to avoid

If you are used to writing code in Java, there are many different and unintuitive things in Solidity. Knowing about them is crucial if you want to avoid running into bugs that are hard to reproduce. I’ll list some of them below:

  • Overflow errors

In Solidity all operations on integers can create an overflow error. What’s more, there are no overflow-safe operations which means you have to check for it manually every time using the require command (it is basically an assert which reverts the whole function when the condition is not fulfilled)

  • ‘Byte’ and ‘Bytes’ confusion

Byte type is 256-bits wide which means that byte array (byte[]) takes 32x more space than expected (it’s very bad, considering the gas limit, and hard to debug keeping in mind that storage space is extremely limited). An actual byte array which takes as much space as expected should be declared as bytes.

  • Strings

Solidity doesn’t natively support any string manipulation. At the time of writing even basic operations like concatenation of two strings have to be done manually after casting them to byte arrays. Length can be checked also only after a conversion to a byte array. What’s more there are no methods like indexOf() which are given in Java — they have to be written by hand, copied in from a different project or reference a function from a library already on the blockchain.

  • Arrays

Array access syntax looks like in Java but declaration syntax is reversed — int8[][5] creates 5 dynamic arrays of bytes. Unfortunately only statically sized arrays can be returned by functions — there is no way to return a dynamically sized one. Also multi-dimensional dynamic arrays cannot be created which means that declaring an array of strings (which are dynamically sized arrays of chars) is not possible.

  • ‘For’ loops

Solidity was created to resemble JavaScript, but literal 0 type infers to byte, not int. That means writing:

for (var i = 0; i < a.length; i++) { a[i] = i; } 

will enter an infinite loop if the a array is longer than 255 elements (the iterator will wrap around back to 0). This is despite the underlying VM using 256 bits to store this byte. You should just know about this and declare i as uint instead of var.

  • Operator’s semantics

Operators have different semantics depending on whether the operands are literals or not. For example, 1/2 is 0.5, but x/y for x==1 and y==2 is 0. Precision of the operation is also determined in this manner — literals are arbitrary-precision, other values are constrained by their types.

  • Mapping

Mappings, unlike maps in Java, don’t throw an exception on non-existing keys. They just return the default value depending on key’s type (when keys are integers, 0 will be returned). What’s more, there is no way to check if an element exists (like contains() in Java) — when 0 is returned we don’t know if a key was added to the mapping with value 0 or is it the default value being returned because there is no such key in the mapping. There’s also no built-in method of extracting a key or value sets from a mapping which means iterating over a key set is not possible.

Summary

Solidity represents a unique and one of the first approaches to writing contracts on the blockchain. This language is still at very early stages and is nowhere near being complete and fully functioning. However, it evolves very quickly, so I’m sure that in the future many mistakes will be fixed and functionalities added. Unfortunately during the designing phase there have been some decisions made that I’m not sure are fully fixable. The biggest ones affect code security — this language should care and enforce security since all blockchain applications work with money. However, functions are public by default, integer operations can overflow, loops could never finish, updating code is problematic, etc. The most fundamental problem is that it’s optimised to be user-friendly and look like a language people are used to and should be intuitively able to deduce what’s going on in EVM. It often does a completely different thing which can cause developer’s confusion, or worse yet memory leaks and security holes. A new language which handles money transactions which is designed to be easy to learn should be intuitive. However, in its current shape it’s easy to learn how to do something wrong, but hard to do something right.

Dominik Hys is an Android Developer at inFullMobile, an international digital product design and development studio based in Warsaw, Poland.

Fell free to contact us: hello@infullmobile.com


Journey of Learning Solidity — Tips And Tricks From Java Developer’s Perspective was originally published in inFullMobile Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Written by
Dominik Hys

You may also like

Like what you read?
Get monthly business and technology insights straight to your inbox.
Contact
Email: growth@withintent.com
Location: Wilcza 46, 00-679 Warsaw
About us
.intent (formerly inFullMobile) is an international digital product design & development studio delivering software at the intersection of digital and physical.
.intent™ All rights reserved.
Terms and Privacy