Solidity is the computer code (programming language) that runs the Ethereum network. It allows you to create smart contracts and Dapps on the platform, expanding the capabilities of networks such as Bitcoin.
In the beginning there was Bitcoin. It was a fast and easy way to exchange money in a decentralized way - without the involvement of banks. And people can trust it because every transaction is recorded in a public ledger that cannot be changed.
But Bitcoin is limited. Blockchain technology could do much more than peer-to-peer exchanges if it had the right code. In this regard, the Ethereum network was created, backed by a new programming language called Solidity. In this article we will explore its origins and its purposes.
READ
9 Best Platforms That Support Solidity
Understanding the basics of smart contracts
As part of the development, the smart contract consists of three sections: balance, storage and codes. The balance represents how much Ethereum the smart contract has. The store contains data such as strings and arrays that are specific to each application. The code section contains the raw machine code that is compiled from what we write in Solidity.
Unlike user accounts, smart contract accounts are not external to the corresponding networks. In other words, you can use your wallet on various networks such as Kovan and Ropsten, but you cannot do so with a smart contract. Smart contracts are internal.
Each smart contract has a source, which is stored on the author’s device, and copies, which are stored on the blockchain. To create an instance (account) of a smart contract, we need to deploy it to the network. It closely resembles the relationships between classes and instances in traditional object-oriented programming (OOP) and the languages that represent it (JS, Ruby). To give you a more visual idea, let's create a Bike class and add an instance of it.
Bike class & instance
We'll write a contract definition that will then be run through a compiler, which will produce two files: bytecode and an application binary interface (ABI). The bytecode is what will actually be passed to the EVM, and the ABI is the layer between the bytecode and regular JavaScript code that allows you to create the user interface (UI).
How to study
No official books on Solidity have been published yet, so it is best to learn the language using the official release. There is also a paid educational 3-hour course on Udemy.
For those who are not yet strong in English, but really want to be in the know, there is only one way - to Habrahabr (, ,). There is also a detailed tutorial on the Voice project, although so far there is only one lesson.
In addition, the topic is attracting more and more new authors, webinars and articles on personal blogs appear quite often. Check popular platforms periodically to stay updated on language development and usage.
Choosing an IDE and Solidity version
Before we begin, we need a proper integrated development environment (IDE). In other words, we need a convenient terminal with the necessary tools to write our code. For the purposes of this tutorial, we'll choose Remix, an IDE created by the Ethereum Foundation that allows you to write, test, debug, run smart contracts, and more. You can either use it directly in your browser or download it locally if you prefer.
Once you launch Remix, you'll see a code editor in the center, a file manager on the left, and a compiler on the right.
Initial Remix window
There will be some pre-written code - we won’t need it. To create your first unique smart contract, let's click on the plus icon in the top left corner of the terminal and give it a name.
Creating a new project in Remix
Because we have an empty document. sol, we must specify the version of Solidity that the compiler will run. At the time of writing this guide, the latest version was 0.5.7. If you are not sure which version to use, you can specify a version range.
2 types of Solidity version indication
Let's give our smart contract a name followed by parentheses.
Naming the contract
Syntax
There are a couple of significant differences compared to JS:
- Solidity is a statically typed language, only return types are dynamic.
- A full-fledged version of the language has not been released (the current one is 0.4.16), so many of the functionality are still in a truncated form, and the number of bugs is quite large.
However, Solidity copes with the assigned tasks, and visually it is still the same ECMAScript:
contract GavCoin { mapping(address=>uint) balances; uint constant totalCoins = 100000000000;
/// Endows creator of contract with 1m GAV. function GavCoin(){ balances[msg.sender] = totalCoins; }
/// Send $((valueInmGAV / 1000).fixed(0,3)) GAV from the account of $(message.caller.address()), to an account accessible only by $(to.address()). function send(address to, uint256 valueInmGAV) { if (balances[msg.sender] >= valueInmGAV) { balances[to] += valueInmGAV; balances[msg.sender] -= valueInmGAV; } }
/// getter function for the balance function balance(address who) constant returns (uint256 balanceInmGAV) { balanceInmGAV = balances[who]; } }
Writing your first smart contract
Once we have our canvas ready, it's time to define the main building blocks - variables. Although experienced software developers will have no problem understanding this concept, we will briefly introduce it to beginners. Variables are placeholders for pieces of information that are subsequently referenced by the program that runs them.
Let's create a pair of variables: a string (a sequence of characters) and an integer (a number). In the case of Ethereum, variables are stored on the blockchain along with the rest of the contracts and can therefore be accessed and updated from anywhere. Another key feature of Solidity variables is that you can make them private by writing “private” next to the variables. Finally, for integers, Solidity has two types: signed (can be positive and negative) and unsigned (can only be positive). To indicate an unsigned variable, we must simply put 'u' in front of it.
A private string and an integer
Once we have a name variable, we need to write setter and getter methods. This looks like a JS function. Remember that Solidity is statically typed, so we must define variable types. Now any value we put into 'setName' will define the string 'name'. To get it, we'll use getName and specify what variable we expect to see. Now it's time to do the same for the age variable. The method is built similarly to getName.
Name/age setters and getters
Let's check out our little piece of code. Go to the Run tab of the compiler and click Deploy under your contract name. At the very bottom of the compiler you will see a section called “Deployed Contracts”, where our methods are available. To pass the name into the value "newName", we need to make sure that our string is written in JSON, otherwise "getName" will not return anything. For "setAge" just enter your age without quotes. As you can see, we can now set and get the name and age variables through our smart contract.
Compiler, with name and age
In the past, generating SPs on the blockchain has been a security disaster.
Fortunately this has changed. If you are a little immersed in the context, then I will tell you how I managed to solve this problem. Together we will figure out how you can now generate genuine random numbers very quickly.
And if you learn anything by the end of this article, I beg you, do me and the world a favor: create a smart Texas Hold'em game. I haven't been able to do this myself yet.
Why do we need midrangers?
For example, we might want to run a lottery, a game, or get random data for test datasets. Previously, we would have used the actual block hash for this task. It would become the basis for our pseudo-random number generator. Moreover, this is the most common approach, which is often offered in tutorials.
What are Wei and Gas
One of the great things about smart contracts is that to deploy them on the Ethereum network, you will need to initiate a transaction that costs a certain amount of money, which is paid out in Ether. It is vital to understand how fees are used in the system as they will be deducted every time you interact with the EVM.
What is Wei?
Let's assume that while reading our lesson, you have used Bitcoin at least once. You probably made a small transaction that was less than 1 BTC. In this case, you used satoshis, which are like cents on the dollar. Wei is similar to satoshi - it is the smallest part of 1 ether. If we think about it in programming terms, it is the lowest unsigned integer on the network. When interacting with the network, you most often encounter Gwei, which refers to Gigawei and is equal to 1 billion Wei.
What is Gas?
Gas is an integral part of the smart contract execution mechanism. It has two values for each transaction: the gas consumed and its price. It is worth noting that the user initiating the transaction determines these values himself. However, if the set Gas value is not enough to perform a certain operation, then Gas will be consumed but the transaction will not be completed. Moreover, if the gas price is set too low for the network at a given time, the transaction will not be processed by the nodes, which will ultimately make it fail. There are several services available to check the optimal values for your transactions, one of which is ethgasstation.info. To better understand Gas and why it's worth some money, let's start coding some of it ourselves.
Return to the Remix window and create a new file. In our example, we'll call it "Gas" and create a contract with the same name. Keep in mind that the more data we need to store on the blockchain, the more gas we will need. That being said, for the purposes of this tutorial we will create a cheap contract; The more you add to it, the higher the fee will be.
There is a function that returns an integer that is the sum of two inputs. To make it as easy as possible, we will specify that our contract will not store anything on the blockchain and to do this we will add “pure” next to the function.
Cheap contract
Now you can expand it into the compiler and enter any two numbers to get the integer "c". To check the price of our transaction, we must take a look at the terminal located under the code section. There is a transaction cost and an execution cost. The first refers to how much data a transaction has. The second refers to how much EVM energy was required for the transaction.
Cheap contract
This is a very simple transaction that costs almost nothing to the network. When writing meaningful smart contracts, you will add more details, which will increase their weight and therefore transaction fees.
Final ban on obsolete designs
Solidity has accumulated quite a lot of obsolete constructs that I would like to remove from the language, but I still can’t due to backward compatibility. This category of changes includes:
- Refusal from throw in favor of revert()/assert(…)/require(…). The transaction rollback mechanism is different from the exception mechanism, and I would like to emphasize this difference at the language level.
- Refusal from var, which, taking into account the rules of type inference, easily leads to errors, for example: for(var i = 0; i < 100500; i++) { ... // endless loop Additionally, they discuss what to replace constructions like var (z, y, z) = foo(); and how to more elegantly skip unnecessary values.
- It is forbidden to use constant for functions - it must be view or pure everywhere.
- Built-in function gasleft() instead of msg.gas. This makes it clearer that this is not some kind of constant, but the remaining amount of gas. Yes, gasleft() can be overridden.
- Moved block.blockhash to blockhash. It’s logical, because the blockhash of the current block is not available (block.blockhash(block.number) == 0).
- It is forbidden to mix hexadecimal constants and time/air multipliers. In fact, it is not entirely clear what 0xaf days are.
- We abandoned suicide/sha3 in inline assembly. It is not clear why outside of assembly these constructs are still available, although deprecated.
- Rejection of the unary plus, because it does not have any special role and can be involved in stupid errors (like x=+1; instead of x+=1;).
- It is planned to abandon the years multiplier, because it is now defined as 365 days, and this does not correspond very accurately with the usual calendar. This seems to me a controversial decision - it would seem that for now we can limit ourselves to a warning.
Create and deploy your own ERC20 token. Issue of tokens and creation of ICO.
Let's face it, most blockchain developers who are just starting out are looking to play big and create their own blockchains and tokens. Although this is an extremely complex topic that has attracted some of the best software developers from other fields, creating a basic ERC20 token is not a difficult task.
First we need to create another file in Remix and load the ERC20 interface, namely:
Not about language
There are several very important changes that do not directly affect the language, but will greatly affect the code of the new contracts.
- Finally, numbers with a fractional part will appear. It seems that everyone is used to doing without them, and now we will learn to use them correctly.
- At the EVM level, protection against short address attack will be added. It would seem like a small thing, but it’s nice that you don’t have to think about it anymore (and generally know about this problem). Maybe it will be even more strict, but there are difficulties there.
ERC20 standard
The totalSupply function allows us to see how many tokens we have in total. The balanceOf function is used to get the number of tokens at certain addresses. The transfer function allows users to perform transactions between themselves. The "TransferFrom", "allowance" and "Approve" functions allow users to authorize other users to initiate transactions on their behalf. Events are recording tools for the general ledger.
In addition to the interface itself, we will need a separate file. sol for our new token. Here we import the ERC20 interface and specify the symbol, name and decimals of our token.
uToday token
Before we compile it, we need to specify the constraints.
- Let's start with a general proposal - this is a constant integer variable that we will make private. Our total supply of tokens will be 1 million, we will also write a function to return this value.
- Secondly, we need to store our token somewhere. To do this, we will need to specify a mapping that will return the balance for any given address.
- Third, there should be a function for transferring tokens, which will essentially have the recipient's address and the number of tokens transferred. This function should also be able to check if the sender has enough tokens in their balance, which can be implemented using a simple if/then statement. In addition, we will set the conditions for '_value' so that users cannot send transactions with "0" tokens, as this will lead to the network being clogged with various garbage.
- Fourth, we must create a mapping for the remaining functions, which is an integer mapping.
We'll then specify some checks in the "approve" and "allowance" functions and set the conditions for 'transferFrom'. Finally, not all tokens will be available on the market. Some of the tokens are usually reserved for teams, funds, consultants and other purposes. It is therefore important that we make it clear how many tokens will be circulating in the system. When we have created tokens, the circulating supply equals our balance.
uToday token constraints
The code is ready, so let's check it out. Go to the Run tab of the compiler and expand our token contract. You will see that we have the token details as well as the total supply, balances and premiums. Congratulations, you have created your first token. In order for our token to actually work on the network, we need to deploy a smart contract (note that this is different from deploying it for testing in Remix). For this tutorial we will be using Remix and Metamask, but there are other ways to do this. Metamask is a simple yet effective Ethereum wallet program with a slick user interface that integrates as an extension into some of the most popular browsers. In our case we will use Opera. First, go to metamask.io and download the extension. Once this is done, you will see a fox icon in the top right corner of your browser.
Loading metamask and icon location
Click on the icon and follow the instructions provided to create a wallet. Don't forget to save your secret phrase! Once you have your wallet, click on the Metamask icon and change the network to "Ropsten" because we don't want to mess with the Ethereum mainnet.
Changing metamask to Ropsten
The last step is to create some Ether (unfortunately you won't be able to use it for any actual purchases, but they are needed for testing). Go to faucet.metamask.io and request 1 Ether. Now you're all set. Go back to the Remix window and change the environment to "Injected Web3" in the compiler. Also take a look at the account tab - your address should match the address you created in Metamask. Select the smart contract you want to deploy, which is your token contract but not the ERC20 interface, and click the appropriate button. A metamask window will appear with the transaction, its details and options for interacting with it. Send a transaction and our token will come to life.
Metamask popup
Now you can play with all the features we listed earlier. Let's take a look at our contract from a different perspective to make sure it's working correctly. Like any other blockchain, Ethereum has several block explorers that serve the main purpose of monitoring what is happening on the network. In our case, we'll use etherscan, although there are several other great alternatives. Note that if you just go to etherscan you will see the Main Network. Since we need to see the Ropsten network, you will need to put "ropsten" in front of the site address. Find your address and you will see two transactions - one for the free Ether you received and the other for deploying the contract.
User address in Etherscan
To find your contract address, click on TxHash and go to the “To” field. Here you can check the transactions, code and events of your smart contract. At this point we need to review and publish our contract. Go to the 'Code' section and click the 'Verify and Publish' link. Here you will need to again specify the name of your token, the compiler version (in our case, the last version of Solidity we used was 0.5.7, so we will stick to the corresponding compiler version). Now you need to copy the token smart contract code along with the ERC20 interface code from the Remix window into etherscan and click 'Verify and Publish' at the bottom of the screen.
Smart contract verification
It's time to return to your contract address. The code in the Code tab will now be subject to review. In addition, you will now have two more tabs: “Read contract” and “Write contract” 'Read contract' & 'Write contract'. In the reading section we can check the functionality of our token. Enter your address (not the contract address) in the 'balanceOf' field to see how many tokens you have; should show 1 million, which we hardcoded as the total supply and transferred it to our wallet. This means that our token now works correctly on the test network.
Getting balance
Methods returning address type
Below we will look at methods that return the address type.
msg.sender
msg.sender() returns address payable.
As the name suggests, the msg.sender function returns the address that initiated the call to this smart contract. However, it is important to note the following:
msg.sender is returned in the current call. It does not necessarily return the EOA sender who sent the transaction.
- If smart contract A is called directly in a transaction sent with EOA, then msg.sender will contain the EOA address.
- If smart contract A is called by another smart contract B, where B was called by a transaction sent with EOA, then msg.sender will contain the address of smart contract B.
tx.origin
Warning: Unsafe!
tx.origin() returns address.
tx.origin returns the EOA address of the originator of the original transaction. Thus, the complete call chain is returned.
block.coinbase
block.coinbase() returns payable address.
The address of the miner of the current block, i.e. address of the recipient of the current block fee and block reward.
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)
ecrecover recovers the address associated with the public key from the elliptic curve signature, or returns zero on error. The function parameters correspond to the ECDSA signature values:
- r = first 32 bytes of signature
- s = second 32 bytes of signature
- v = last 1 byte of signature
ecrecover returns address, not address payable.
Afterword
If you want to start a career in the crypto industry as a developer, you must understand that, despite its relative simplicity at its core, blockchain has incredible flexibility and functionality. Since 2022, blockchains have evolved significantly and their use cases have expanded beyond just financial transactions. With the advent of Ethereum, a whole new layer of networks has emerged that hosts various dApps and blockchain-based solutions. The tool for this evolution has been the smart contract, and if you want to expand your experience, make it more valuable and future-proof, you need to know how it works.
If you can code smart contracts using other languages, Solidity is still better suited for these purposes. Moreover, if you want to become an Ethereum developer or create ICO/ERC20 tokens for your project, this is definitely your choice. If you have some experience with C++ or JavaScript, coding in Solidity should be relatively easy for you. You will, however, have to understand some differences between the client-server and decentralized software launch models. Thanks to the Ethereum Foundation and several third parties, developers are provided with a set of convenient tools such as Remix and Etherscan for coding and deploying smart contracts.
We hope our tutorial has helped you understand most of the Solidity concepts to begin your exciting journey of learning blockchain technology. Remember you can always check with the latest Solidity documentation
Improving “working with memory”
In quotes because we are mainly talking about "unexpected" access to storage without using assembly.
- Prohibition of uninitialized references to storage, which actually pointed to the beginning of storage and therefore intersected with other state variables.
- Direct work with .length may be prohibited. Changing the length of an array manually is a fairly low-level operation. Its main advantage is gas savings with small changes in the length of the array. But this syntax allows you to accidentally (or intentionally) create an array of inadequate size, which can lead to an overlap attack. To clear an array, delete has long been used; to reduce the size, it is proposed to use pop(); in addition, operations like truncate() and extend() are discussed. Well, there is still assembly if you really need it.
Applying multiple modifiers to a function.
You can apply multiple modifiers to one function. This can be done as follows:
contract OwnerContract { address public owner = msg.sender; uint256 public creationTime = now; modifier onlyBy(address _account) { require(msg.sender == _account, “The sender is not authorized.”); _; } modifier onlyAfter(uint256 _time) { require(now >= _time, “The function was called too early”); _; } function disown() public onlyBy(owner) onlyAfter(creationTime + 6 weeks) { delete owner; } }
Modifiers will be executed in the order in which they are defined, that is, from left to right. So, in the example above, the function will check the following conditions before running:
- onlyBy(…) : Is the address calling the contract the owner?
- onlyAfter(…) : Is the caller the owner of the smart contract for more than 6 weeks?