Do you want to know how to create cryptocurrency tokens but you don’t know where to start in this information overload? It may seem hard and maybe even intimidating, but it doesn’t have to be! In this video, we’ll show you the different data types that are key to understanding the process of cryptocurrency creation.
We’ll get to know the integers, booleans, fixed-point numbers, bytes, and strings. We’ll even introduce you to special data types like addresses. You’ll also know more in depth about the reference types. To find out more, watch our explanatory video and read the article, which is a transcription of the video embedded below.
Welcome to lesson 4. In this lesson, I will discuss all the available data types in the Solidity language.
Value Types
First, I will look at the value types, and they are called value types because the variables of these types are always passed by value, meaning that they are always copied when they are used as function arguments or in assignments.
Booleans
The first most basic value type is booleans. The possible values for a Boolean are true and false like in any programming language. To declare it, simply type bool, then the variable name, and then equals either true or false.
Integers
Another really common data type is integers, which are declared by using the keywords “int” and “uint.” Int stands for signed integer, where 255 bits are used to represent the value, and one bit is used to denote a sign. “Uint” stands for unsigned integer, where all 256 bits are used to represent the value. Keywords “uint” and “int” go up in increments of 8, from the lowest value of 8 to the maximum value of 256; for example, int8, int16, uint16, etc. “Uint” and int, by default, are aliases for uint256 and int256, respectively.
Now, let’s have a look at a short example of how integers behave. Let’s copy this code and go to: remix.ethereum.org. Paste the code; you can see I have already done that before, and let’s explore what it does. As you can see, there are three functions: “getFoobar, getMaxUint, and getMaxInt.”
“GetFoobar” simply adds up two integer numbers, 42 and 50, and returns them. Let’s compile the contract again by clicking on create, and click here: “getFoobar” and execute the function, and we get the answer 92. These two functions, “getMaxUint” and “getMaxInt”, are nothing special. They just declare the highest possible numbers for an unsigned integer here and a signed integer here.
We don’t really have to know the highest values; it’s just for fun. You can see that the highest value for an unsigned integer is a lot larger than for a signed integer. Also, I am showing you this just to show that it’s possible to declare integer values by using hexadecimal notation rather than just typing, let’s say, 255. Sometimes it’s more convenient, but you don’t have to use it.
Fixed Point Numbers
Now, let’s have a look at another data type: fixed point numbers. Fixed point numbers are denoted by keywords “fixed” and “ufixed.” Fixed stands for signed “fixed” point number, and “ufix” stands for unsigned fixed point number.
However, keep in mind that fixed point numbers are not fully supported yet by Solidity. They can be declared but cannot be assigned to or from. I am only showing you this so that you are aware that fixed point numbers are coming to Solidity and that you can declare them, but you cannot use them yet. If you want to use float type division in Solidity, different size units have to be used.
Bytes
Bytes is another familiar data type, which can be used to store raw byte data. Bytes is essentially a dynamic array of bytes, where byte is an alias for bytes 1, which is an array of one byte.
The maximum bytes array is of bytes 32 size, and you can see the example of it here, where I am declaring the maximum available bytes array, and instead of what we saw in the example for integers where we got a huge number, in this case, if we return this variable, we will just get FFFF 64 times.
Strings
Strings is another very common data type. String literals are written divider double or single quotes. As the integer literals, the type can vary, but they are implicitly convertible to bytes type. Under the hood, strings are basically an arbitrary-length byte array.
Strings is another very common data type. String literals are written inside double or single quotes. As with the integer literals, the type can vary, but they are implicitly convertible to bytes type. Under the hood, strings are basically an arbitrary-length byte array.
Now, let’s have a look at this example. So, let’s copy the code and go to remix IDE, open a new file, and paste it right here. Now we need to select JavaScript virtual machine here as an environment and compile the contract.
Okay, that was done successfully. Now, let’s have a look at what these functions do. A setString function just sets a new string variable “foo,” and the getString function just returns that variable “foo.” I will not delve into much more detail into how this works; we will have a look at how functions work in an upcoming lesson.
So let’s set the new string by typing a, b, c. Don’t forget to use double quotes or single quotes around it, and as we can see, if we retrieve a string, we retrieve a, b, c.
Now, let’s just for fun, let’s have a look at how much this transaction costs. It costs 42,826 gas. Now remember that a string is a byte array under the hood, so if we set this string to be a lot larger, the bytes array will automatically be a lot larger, and remember that everything in the Ethereum virtual machine costs gas to execute, and memory takes gas as well.
So, the larger the memory, the larger the bytes array, the more gas it will take to execute. So, let’s set this string and let’s see how much this transaction costs, and we can see that this transaction costs 70,693 gas. So that’s a lot more than if we set a short string.
Address
Now, let’s have a look at addresses. Address is a special data type that holds a 20-byte value, which is the size of an Ethereum address. Address types also have members and serve as a base for all contracts. Address comes with type members which are balance and transfer, and the balance makes it possible to query the balance of an address, and the transfer member makes it possible to send Ether in units of wei to another address.
Now, let’s have a look at this example: copy the code, go to remix IDE, paste the code, then select JavaScript virtual machine, if it hasn’t been done already, and let’s deploy this contract. Now let’s have a look briefly at what this does. The get balance function takes a parameter address of data type address and it returns the balance of that address.
So, here we can see our account which was used to deploy the contract, let’s copy the address using the clipboard, and let’s copy the address here. Don’t forget to use double quotes. Now, if we get the balance, we get the balance in units of wei, so that’s almost 100 Ether. Now, if we go to another account, copy the address, and go back to our previous original account and query the balance, we get 100 Ether, which is correct. So, you can see how this works.
Enums
Now, let’s have a look at enums. Enums is just one way to create a user-defined type in Solidity where a keyword can be mapped to an integer value. It’s better to explain enums by looking at this example, so copy the code again, go to remix IDE, paste the code, and then compile this contract using the JavaScript virtual machine.
So we have the contract compiled and deployed, and now let’s have a look at what this does. Here we have an enum of action choices, and it has four choices: go left, go right, go straight, and sit still. Then we have this choice which hasn’t been declared yet, and then we have a default choice which we set as go straight.
Now, if we look at this function, getDefaultChoice, it returns the default choice which we set as go straight, and the answer which we get is two. So the default choice is integer two, and in this case, the enum, since it’s indexed from 0 to 1, does refer to the index two.
Now we can set the choice by ourselves by using this function: for example, set sit still and we set our choice to actionChoices.sitStill. If we do so and then retrieve the choice which hasn’t been declared yet, and here we use this function, getChoice, we get the uint8 of 3. So, sit still is at index three in action choices enum, and this is what it is. So that’s how enums work. They’re really useful for making code more readable. So we had a look at all the value types that exist in Solidity.
Reference Types
Now, let’s have a look at the reference types. Reference types are more complex types than value types because they do not always fit into 256 bits and thus have to be handled more carefully than value types. Since copying them can be quite expensive, we have to think about whether we want them to be stored in memory, which is not persistent, or storage, where the state variables are held.
Before we look at the reference types in more detail, we need to have a look at the data locations that exist in Solidity, and that’s memory and storage. Memory and storage are analogous to memory and hard drive storage in a computer. The contract can use any amount of memory as long as it pays for it during execution of its code, but when execution stops, the entire content of the memory is wiped, and the next execution will start fresh.
The storage, on the other hand, is persistent on the blockchain itself, so the next time the contract executes some code, it has access to all the data it previously stored in its storage area, so be careful about which one you use. If you do not specify whether memory or storage should be used, Solidity by default will use storage for structs and arrays even when they are declared as local function variables.
This is because arrays and structs are complex data structures and could be of variable length. This is ideal for storage as it operates as a key-value data structure, which can always be expanded. On the other hand, memory dedicates a fixed-length space in memory, and thus it’s less expensive to use, but it cannot be resized. This is ideal for simpler data types such as integers, booleans, and so on. Remember as a rule of thumb, state variables are always in storage, function arguments are always in memory, and local variables are always in reference storage.
Arrays
Arrays are the first reference type we will have a look at. Creating new arrays is possible by using the keyword “new.” So let’s have a look at how this works: copy the code, make a new file, paste the code, make sure that we are on the JavaScript virtual machine, and let’s compile and deploy this code. So now, once that’s compiled, let’s briefly have a look at this “newArr” function. This takes an argument “len” of data type “uint,” and it declares a new array in memory first of uint data type and of length 7, and then a new bytes array of whatever length we give to this function.
So let’s try to do that by giving it a length of 5. So here we go. Now, once that’s done, we have two new arrays. The first array, A, is of length 7, and B is of length “len.” So now it’s possible to make A[6] element in array A to a value of 8. However, since the array B is only of length 5, if we did this B[6] = 8, that would be invalid. So anyway, this is in short detail how arrays work.
Structs
Let’s have a look at structs. Structs is simply a way to define new data types. Let’s have a look at this short example where we are declaring a new struct Book. What this will allow us to do is to define new variables of data type book or make an array of books where each instance in the array is a book, and each one of these would contain a title, an author, and a cost. What this allows us to do is to make the code more readable and maintainable, and also a lot clearer in most cases.
Mappings
Mappings is the last reference type we will have a look at. Mappings can be seen as hash tables that are virtually initialized such that every possible key exists and is mapped to a value whose byte-representation is all zeroes by default. But once initialized, it becomes equal to the newly set value. Mappings are only allowed for state variables or storage reference types in internal functions.
Now, let’s have a look at how mappings work by copying this code, going to remix IDE, making a new file, making sure that we are on the JavaScript virtual machine, as always, and then let’s deploy this contract. Now, let’s have a look at what this does. So at the top, we have a mapping from address to uint, so basically the key is going to be an address of an Ethereum wallet, and uint is going to be the value that will get returned. This mapping is called balancesMapping.
So this is really similar to dictionaries in Python or JSON objects or just any kind of objects in JavaScript, for example. Now this function: update, takes a parameter “new balance” and then it sets the balance of whoever called this function, so that’s message.sender, to the new balance that we set. In the get balance function, we’ll retrieve the balance of whoever called this function.
nSo let’s say if we are on our default Ethereum wallet and we set a balance to 10 and we retrieve it, we get the balance of 10. If we go to another account and we select 42 and we update the balance and then retrieve the balance, we get 42, and then if we come back to our previous account and we get the balance, we get 10 again.
So this is how mapping works, in short detail. I hope you enjoyed this lesson. Of course, all the explanations are not in full detail, so if you want to read more about it, I would recommend going to the official Solidity documentation, specifically the types section, and read more about it here.
I’ll see you next!