Recipe ID: hsts-r32
We offer blockchain introduction, Hyperledger for system admin, Ethereum, Solidity, Corda R3, Hyperledger for developers, blockchain cybersecurity and more classes in self-paced video format starting at $60. Click here to learn more and register. For complete self-paced blockchain training, visit our Complete Blockchain Development Training page.
The latest version of this tutorial along with all updated source codes is available via the below link:
https://myhsts.org/blog/ethereum-dapp-with-evm-remix-golang-truffle-and-solidity-part1.html
Ethereum is a general-purpose blockchain that is more suited to describing business logic, through advanced scripts, also known as smart contracts. Ethereum was designed with a broader vision, as a decentralized or world computer that attempts to marry the power of the blockchain, as a trust machine, with a Turing-complete contract engine. Although Ethereum borrows many ideas that were initially introduced by bitcoin, there are many divergences between the two.
The Ethereum virtual machine and smart contracts are key elements of Ethereum, and constitute its main attraction. In Ethereum, smart contracts represent a piece of code written in a high-level language (Solidity, LLL, Viper) and stored as bytecode in the blockchain, in order to run reliably in a stack-based virtual machine (Ethereum Virtual Machine), in each node, once invoked. The interactions with smart contract functions happen through transactions on the blockchain network, with their payloads being executed in the Ethereum virtual machine, and the shared blockchain state being updated accordingly.
For those who are not familiar with blockchain technology reading History and Evolution of Blockchain Technology from Bitcoin article is strongly recommended. Also, if you wish to learn and practice Hyperledger blockchain development, visit Comprehensive Hyperledger Training Tutorials page to get the outline of our Hyperledger tutorial articles.
We have written two sets of tutorials to explore Ethereum and Solidity programming in depth. First set covers the following nine recipes:
In short, you learn about how to set up and configure Ethereum and develop blockchain applications using Solidity programming language. We explore its key components, including smart contracts and Web3.JS API via an Auction Decentralized Application (DApp) step-by-step.
In second set, we will discuss more advance topics in Ethereum blockchain development and solidity while building a Tontine DApp game step-by-step. Specifically, we cover Truffle and Drizzle. For instance, we show you how a tool such as Truffle can be an assistant in building, testing, debugging, and deploying DApps. In summary, we are going to cover four main topics:
The 2nd set consists of 8 recipes as follows:
IMPORTANT: Understanding and completing the first set of recipes are required prior to working on second set of recipes.
Now that you have a basic idea of what Ethereum and DApps are, we can start to build our auction DApp. An auction is a typical example, but it is complex enough to provide a perfect first DApp. It demonstrates the trustless nature of the blockchain, in which we can manage funds automatically and securely, without requiring legal recourse or relying on a trusted third party. Moreover, auctions are generally needed for building smart contracts for token sales in ICOs. In short, our auction DApp will be a web application that enables users to join auctions using ether. Below is high level snapshot of steps we will take in this and follow up recipes:
Let's start with the backend layer, represented by the smart contract that manages the auction. We'll consider the following auction design.
A vehicle's owner deploys the contract to the blockchain and becomes the auction owner. The auction is open immediately after the contract deployment, and, once the bidding period is over, the highest bidder wins the auction, and the other participants withdraw their bids. In this example, the bid will be cumulative, which means that if, for example, you bid 100 ETH, and then someone else bids 110 ETH, you can only send an additional 10.000000000000000001 ETH the next time to outbid them; your new bid is the sum of your two bids.
Furthermore, the auction owner can cancel the auction in exceptional cases, and must also be allowed, at the end of the auction, to withdraw the winning bid. The auction interaction flow is illustrated in the following diagram:
To write our auction contract, we will use Solidity, which is the most popular language used to write smart contracts for the Ethereum blockchain. It's a JavaScript-like language, compiled into bytecode running in the Ethereum virtual machine. If you are familiar with Object-Oriented Programming, learning to write Solidity contracts should be fairly straightforward. Through this auction example, I'll try to lay out the basic and important features of Solidity.
Our contract design will be simple. In the first step, we will create an abstract contract, in which we will declare our elementary functions and events. Then, we will use inheritance to create a compliant implementation—a contract with the exact same functions implemented. Abstract contracts help us to decouple the definition of a contract from its implementation, providing better extensibility and code readability.
Start by creating a file called Auction.sol (the .sol extension refers to Solidity files), and paste in the code of our abstract contract, Auction:
pragma solidity ^0.4.24;
contract Auction {
address internal auction_owner; uint256 public auction_start; uint256 public auction_end; uint256 public highestBid; address public highestBidder; enum auction_state {
CANCELLED, STARTED
}
struct car {
string Brand; string Rnumber;
}
car public Mycar; address[] bidders;
mapping(address => uint) public bids; auction_state public STATE;
modifier an_ongoing_auction() { require(now <= auction_end);
_;
}
modifier only_owner() { require(msg.sender == auction_owner);
_;
}
function bid() public payable returns (bool) {} function withdraw() public returns (bool) {} function cancel_auction() external returns (bool) {}
event BidEvent(address indexed highestBidder, uint256 highestBid); event WithdrawalEvent(address withdrawer, uint256 amount);
event CanceledEvent(string message, uint256 time);
}
I know this first contract is perhaps enigmatic for you, and maybe it's the first time you have seen a contract of such size (even if it's small). But, don't worry; we will use this first abstract contract as a playground for learning Solidity. In fact, my approach will be to dissect this code line by line and section by section, in order to introduce you to the different major Solidity features.
In Solidity, a contract resembles a class in object-oriented languages, and it defines almost all conventional elements: variables, functions, structures, interfaces, libraries, inheritance, and so on.
The first line—version pragma ^0.4.24;—is a declaration of Solidity's compiler version that your particular code should use. The caret operator (^) indicates that the preceding code will not compile with an earlier compiler, but will compile with both 0.4.24 and any newer version that doesn't introduce any breaking changes.
The second line—contract Auction {...}—declares a contract with the name Auction.
In Solidity, we introduce the concept of states, to express variables (used to store information) declared inside of the contract scope and outside of the function scope, similar to the global variable concept in other programming languages. They represent values that are permanently stored in contract storage (on the blockchain); hence, the contract knows their updated values. In general, a state variable in Solidity is declared as follows:
<Variable type> <visibility specifier> <variable name>
In this contract, we'll need to define a set of variables, as follows:
In our code, we used only two basic variable types: uint256 and address
The first represents an unsigned integer of 256 bits. Solidity deals with signed and unsigned integers of various sizes, and to declare an integer, we use one of the keywords, int or uint, for signed and unsigned integers of 8 to 256 bits (uint8 to uint256, in steps of 8).
The type address is a dedicated type that holds a 20-byte value (the size of an address), to represent an Ethereum account's address.
As you will notice, each variable is preceded by a visibility specifier (public or internal) that defines the accessibility scope. A public state (storage variable) is visible internally and externally (in this case, Solidity creates an implicit public getter for you). An internal variable, however, is only accessible within the current contract or contracts deriving from it, not by external calls, making it similar to the protected keyword in object-oriented programming languages. By default, variables are internal; therefore, the internal keyword can be omitted. In order to access an internal state, we need to define an explicit public getter function. The following is a getter that we define to read the
auction_owner as an internal state (we can define this state as public, and avoid such manipulation):
address internal auction_owner;
function get_owner() view returns(address) { return auction_owner;
}
Another access modifier, which is unused in our code, is private. This makes the
only variable visible within the contract where it is defined, and not in derived contracts.
As in other programming languages, enumerations help us to define our own data
type that consists of a list of named constants, to ease reading the code. Here, we define an enumeration representing the auction status, with two possible states: CANCELLED or STARTED.
First, we define our enumeration auction_state by using the enum keyword, as follows: enum auction_state { CANCELLED,STARTED }.
Then, we declare a variable of the auction_state type: auction_state public STATE;.
The enum values are converted into integers and numbered in the order that they are defined, starting at zero. If you want to get the value of your declared enum constants, use an explicit conversion uint(STATE). Instead of using the corresponding integer value, you can directly use the enum constants, as follows: auction_state.STARTED (equals 0) or auction_state.CANCELLED (equals 1).
Solidity provides three types of data structures: struct, mapping, and arrays.
In our contract, we define a dynamic array to contain all of the bidder addresses. Solidity supports fixed and dynamic sized arrays; therefore, as we don't know the exact number of the bidders a priori, we declare a dynamic array (without fixed length), as follows:
address[] bidders;
An array of a fixed size n and an element type T is declared as T[n], and an array of dynamic size is declared as T[].
A mapping is a special dynamic data structure, similar to a hash table, that maps keys to values. A map cannot contain duplicate keys, because a key is hashed and used as a unique index; thus, each key can map to, at most, one value. You can get the value directly by providing a key, without the need to use indices, like in an array:
In Solidity, a mapping is declared as a variable by using the mapping
keyword, mapping(KeyType => ValueType), where KeyType can be almost any type (except for a mapping) and ValueType can actually be any type, including further mappings. In our contract, we're creating a mapping that accepts the bidder's address as the key, and with the value type being the corresponding bid:
mapping(address => uint) public bids;
As with other variables, the mapping can be declared as public, to avoid the need to define a getter for reading its values. Mappings are very useful, as we don't have to manage the indices of the stored elements. For example, to store the bid made by a participant, we
use bids["participant address"] = bid value;.
A structure is a collection of variables of different data types, under a single name. As with most programming languages, we define a structure type by using the struct keyword. In the following example, we declare a structure with two members:
struct car {
string Brand; string Rnumber;
}
Here, the struct represents a car by defining its brand and registration number. We then declare a car object by using car public Mycar;.
After defining the auction states, it's time to define the functions that handle the auction's functionalities. In Solidity, a function has the same meaning as in any other language, and is declared using the keyword function, as follows:
function function_name(<parameter types>)
{internal|external|private|public} [pure|constant|view|payable] [returns (<return types>)]
Like variables, functions can be specified as being external, internal, private, or public, while the default visibility is public. As our first contract, auction, is an abstract contract, we only declare the functions without implementation. They will be implemented later, in a child contract.
Along with the visibility specifier, we can define additional specifiers, such as the following:
In this contract, we define the following methods, in order to guarantee the basic auction operations:
One of the most interesting features in Solidity is the function modifiers. They are special control methods that are designated by a special keyword, modifier. Their role is to modify the behavior of defined functions, according to specific conditions. For example, they can automatically check a condition prior to executing the function. Consider the two following modifiers:
modifier an_ongoing_auction() { require(now <= auction_end);
_;
}
modifier only_owner() { require(msg.sender == auction_owner);
_;
}
The first checks whether the auction is still open, whereas the second restricts the authorization to executing a function to the contract's owner (auction_owner). The usage of the only_owner() modifier makes the contract ownable, which is a common smart contract pattern, thereby giving the contract owner specific privileges, similar to an admin in other systems. Generally, in modifiers, we check conditions by using the require, revert, or assert functions. The underscore in the modifier's body will be substituted by the code of the function being modified.
Solidity initially used the special keyword throw to stop and exit the contract execution when called. Since then, the functions assert(), require(), and revert() have been introduced (in Solidity 0.4.10), in order to enhance the error handling. Although the throw function is now being deprecated, you are still able to use it.
With all of these functions in place, we can use them equivalently, as follows:
You might have noticed that we have reversed the required conditional statement between, on the one hand, require() and assert(), and on the other hand, throw and revert(). In assert() and require(), we provide the statement that we want to be true,
whereas throw and revert() behave like exit functions that you call when your condition isn't met.
The differences between these functions can be described as follows:
As revert() and require() both refund the unconsumed gas, they should be used to ensure valid conditions, whereas assert() should be used to check a harmful statement and to protect your contract, meaning that you can use assert to avoid overflow or underflow. Think of assert() as a handbrake that you use when something very wrong has happened, and the other functions as normal brakes.
An important feature that has been introduced in a newer Solidity version (0.4.22) is that you can return an argument to specify the error reason in your assert or require function:
require(msg.sender == owner, "the execution is reserved to the contract's owner");
if (msg.sender != owner) {
revert("the execution is reserved to the contract's owner");
}
Events allow for the convenient use of the EVM logging facilities. They are an important concept in smart contracts, as any off-chain environment connected to Ethereum's JSON- RPC API can listen to these events and act accordingly.
In general, there are three main use cases for events and logs in DApps:
When they are called, they cause their arguments to be stored in the transaction's log. An event is declared as using the event keyword, as follows: event CanceledEvent(string message, uint256 time);.
In the declaration, we determine the parameter types that we expect to include in the log. In our case, the previously created event CanceledEvent will be emitted once the auction is cancelled and will broadcast a message, Auction has been cancelled, with the time of cancellation. We also defined two other events:
As you'll see later when developing the web interface, it is possible to filter for specific values of indexed arguments (using the indexed keyword) in the user interface using the filtering functionality of Web 3.0.
So far, we have covered the basic programming constructs (such as variables, data types, and data structure) and introduced the important concept of a contract by defining an abstract contract representing auctions in general. Now, let's move forward and define our main derived contract.
Solidity supports advanced features, such as multiple inheritance, including polymorphism. After writing our abstract class, let's extend our code by writing our second contract (child contract), called MyAuction, in the same contract file. It will inherit from the first contract and implement the defined auction functions.
The following is how you declare a derived contract inheriting from our first contract: contract MyAuction is Auction {..}.
The keyword is indicates to the compiler that MyAuction is derived from
the Auction contract. Hence, the MyAuction contract can access all members, including private functions and storage variables in the Auction contract.
Hint: Instead of defining both contracts in the same Solidity file, you can define the parent in a separate file and import it by using the directive import "filename";.
As with Object-Oriented Programming (OOP), you can define a constructor for your contract. It's a special function using the same name as the contract it belongs to. Unlike in OOP, a contract's constructor is invoked once and only once during the first deployment to the blockchain, and usually defines the initial contract behavior.
In the MyAuction contract, our constructor is extremely simple:
function MyAuction (uint _biddingTime, address _owner,string _brand,string
_Rnumber) public { auction_owner = _owner; auction_start = now;
auction_end = auction_start + _biddingTime* 1 hours; STATE = auction_state.STARTED;
Mycar.Brand = _brand; Mycar.Rnumber = _Rnumber;
}
This constructor, upon creation, sets the relevant states by defining the auction owner, the auction opening and ending date, and the car's details. Consequently, our auction starts immediately, once the contract is deployed. In newer Solidity versions (from the compiler version 0.4.22), you can declare a constructor by using the constructor keyword:
constructor(uint _biddingTime, address _owner,string _brand,string
_Rnumber) public {/*code*/}
As you will notice, in the constructor, we used the keyword now to set the auction start time, whereas the auction's end is calculated by adding to the auction start time a number of hours defined by the _biddingTime argument.
The now keyword is an integer variable that returns the block's timestamp (it's an alias for the special global variable block.timestamp), in which the contract was embedded.
Solidity also provides us with some helpful time units (seconds, minutes, hours, days, weeks, and years) that can be applied to a variable as a suffix, to automatically convert a unit to the equivalent time, in seconds. In your constructor, the elapsed number of hours (_biddingTime * 1 hours) is automatically converted to seconds and added to the Linux epoch time provided by the now variable.
We could, alternatively, manage the auction duration by using block numbers instead of the time epoch. In that case, the auction will start once a specific block is mined and stop at a specific future block. For that, you'll use the special variable block.number provided by Solidity, which gives you the current block's number.
Earlier, we introduced two special system variables: block.timestamp and block.number. Solidity provides us with a set of global variables and special functions that exist in the global namespace and that are mainly used to provide information about blocks and transactions.
The following is a list from the official documentation:
The values of all attributes of the msg object, including msg.sender and msg.value, vary for every external function call according to the sender and the amount carried by the transaction. In our constructor, we can provide the auction owner as an argument,
using msg.sender to directly set the sender of the deployment transaction as the owner:
auction_owner = msg.sender;.
In Solidity, there is another special method, which is the fallback function. It's an unnamed function that cannot have arguments, nor return anything, and it is declared as follows: function () public payable { }.
The fallback function is executed if the contract is called and no function in the contract matches the specified function's signature. This is not a mandatory function to declare, but you can include it, in order to define your contract's behavior when it receives transactions without data. To accepts funds, you have to declare it as payable.
At this level, we need to overload the functions in the child contract, MyAuction, that we defined earlier in the abstract contract, Auction. In order to achieve that goal, we need to keep the same name and declaration (the same visibility and return type) as in the Auction contract and then define the function's body. You'll overload the function in the sequence in which they were defined in the abstract contract, as follows.
Let's start by defining the bidding function, which will allow participants to place bids in ether (wei):
function bid() public payable an_ongoing_auction returns (bool){ require(bids[msg.sender] + msg.value > highestBid, "can't bid, Make a
higher Bid");
highestBidder = msg.sender; highestBid = msg.value; bidders.push(msg.sender);
bids[msg.sender] = bids[msg.sender] + msg.value; emit BidEvent(highestBidder, highestBid);
return true;
}
Nothing is different from the original declaration; we only add a modifier at the end of the function declaration, to enable its execution only during the auction period; otherwise, an exception will be raised. As I have stated repeatedly, the payable keyword enables the function to receive ether carried by the bidding transactions.
In the body of the bid() function, we initially check whether the total of the bids sent by a participant is higher than the highest bid, using require(bids[msg.sender] + msg.value > highestBid);.
Depending on how much ether the bidder sends, either they will be the new highest bidder or their bid will be refused. If the bid was successful, the contract will fire the BidEvent event, to announce the new highest bid and bidder. In the newer Solidity versions, you can use the emit keyword to fire an event.
Before emitting the event, we add the bidder's address to the array of participants, using the push function. Afterwards, we update the participant's bid in our mapping bids: bids[msg.sender] = bids[msg.sender] + msg.value;.
At the end, this function returns true to indicate a successful execution (bidding). We will use this return pattern in all other functions.
Wouldn't it be logical to enable the auction owner to cancel the auction?
Such a function should be executed exclusively by the auction owner, while the auction is still open; thus, we use the modifiers only_owner and an_ongoing_auction:
function cancel_auction() only_owner an_ongoing_auction returns (bool) { STATE = auction_state.CANCELLED;
CanceledEvent("Auction Cancelled", now); return true;
}
This function will change the state of the auction status to Cancelled, using the enum value auction_state.CANCELLED we defined earlier, and the event CanceledEvent will be fired announcing the auction cancellation.
After the auction ends, you'll need to enable bidders to get their ether back. For security reasons, it's better to adopt a withdrawal pattern —this helps us to avoid security issues that could cause funds to be lost:
function withdraw() public returns (bool){
require(now > auction_end , "can't withdraw, Auction is still open"); uint amount = bids[msg.sender];
bids[msg.sender] = 0; msg.sender.transfer(amount); WithdrawalEvent(msg.sender, amount); return true;
}
There is nothing new here except, as you might have noticed, the use of a new function, transfer(). Along with another function, send(), both methods enable the contract to send funds to a designated address (msg.sender). In this case, the contract sends the corresponding bid amount back to the withdrawal requester's address.
Notice that we're following the recommended pattern for functions that send
You might be wondering why we don't just define a function that automatically refunds all participants once the auction is over, a function similar to the following:
function payback_participants() internal returns (bool){ uint256 payback = 0;
for (uint256 i = 0; i < bidders.length; i++)
{
if (bids[bidders[i]] > 0) { payback = bids[bidders[i]]; bids[bidders[i]] = 0; bidders[i].transfer(payback);
}
}
return true;
}
Well, it's a risky anti-pattern. You should know that send or transfer methods can actually fail (for different reasons) and therefore, the payment loop won't end by successfully paying back all of the participants. For example, a malicious user that bid only 1 wei from a contract with a non-payable fallback can block the refund process by making the transfer method fail (and therefore throw) forever, meaning that the loop will never get executed completely.
What about introducing a nuclear launch button in our contract that, once pushed by the auction owner, will make the contract disappear from the blockchain? The contract destruction will be ensured by the destruct_auction() method, which can be invoked if all participants have withdrawn their bids, as follows:
function destruct_auction() external only_owner returns (bool) { require(now > auction_end, "You can't destruct the contract,The auction
is still open");
for (uint i = 0; i < bidders.length; i++)
{
assert(bids[bidders[i]] == 0);
}
selfdestruct(auction_owner); return true;
}
The first thing that we can see here is that Solidity defines for-loops like standard languages, and you can access the length of a dynamic size array by using
the .length member.
More importantly, Solidity defines a special function, selfdestruct(address), to destroy the current contract (it will no longer be accessible) and send all its funds to the address given as the argument. This is a space- and cost-saving feature as, when you are finished with a contract, it will cost you far less gas using selfdestruct than just sending the contract funds using address.transfer(this.balance).
But wait! Isn't the blockchain immutable?
The answer needs a little dive into the Ethereum block structure, which is out of the scope of this recipe. However, it's worth noting that the selfdestruct method clears all the contract's data, and frees up space from the current and future state tree (the mapping between the contract's account and its states), but not from the previous blocks. The contract bytecode will remain in an old block but its account will no longer be accessible in the state tree; therefore, the data is still immutable. In other words, after calling selfdestruct, the state tree will be updated without the destroyed contract states.
All set! We have finished our auction smart contract. Now let's enjoy compiling and running our first achievement in the next recipe.
To conclude this recipe, we like to recommend our Learn Hands-on Blockchain Ethereum Development & Get Certified in 30 Hrs, and Become Blockchain Certified Security Architect in 30 hours courses to those interested in pursuing a blockchain development career. This recipe is written by Brian Wu who is our senior Blockchain instructor in Washington DC. His Blockchain By Example book is highly recommended for learning more about blockchain development.
Hands-on Node.JS, MongoDB and Express.js Training
Advance JavaScript, jQuery Using JSON and Ajax
Learn Hands-on Blockchain Ethereum Development & Get Certified in 30 Hrs
Learn Blockchain Hyperledger Development & Get Certified in 30 Hrs
Become Blockchain Certified Security Architect in 30 hours
Blockchain Certified Solution Architect in 30 hours
Introduction to Python Programming
Object Oriented Programming with UML
We offer private coding classes for beginners online and offline (at our Virginia site) with custom curriculum for most of our classes for $59 per hour online or $95 per hour in virginia. Give us a call or submit our Private Coding Classes for Beginners form to discuss your needs.