/ Ethereum

What is a Smart Contract?

A quick, easy guide to understanding what a smart contract is, what a smart contract can do and how to write your first smart contract.

A Brief History & Definition

In 1996, Nick Szabo first postulated his idea for smart contracts, as

“…a set of promises, specified in digital form, including protocols within which the parties perform on these promises.”

At the time, a fully decentralized “protocol within which the parties perform on these promises” did not yet exist. Certainly, several types of “contracts” or “agreements” are already built into existing systems. However, these systems did not encapsulate the methodology in which “parties perform on these promises” originally envisioned.

For example, one could say the system behind credit cards purchases contractually binds a user to pay for goods bought with the card. However, this agreement actually exists within several layers of siloed infrastructure. That is, if I do not pay my credit card bill, a separate collections agency will rely on the data held by the credit card company to resolve the issue along with any supplemental information that I can provide. In other words, as there is no shared repository of data or transactions with which to observe the contract, the collections agency must rely on the data provided by both parties in a disagreement to resolve the issue.

As blockchain technology evolved, thought leaders connected the idea that if a shared digital ledger of information, logic, and value was available, smart contracts could exist as intended. Although not the first, Bitcoin provided a decentralized system of purely digital value. Seeing Bitcoin as a uniquely transactional system , the creators of Ethereum imagined that if Turing-complete programs that stored value and information were developed, then smart contracts would flourish. Hence, the Ethereum smart contract system was born as written programs, operating within the constraints of the Ethereum protocol to ensure all parties interacting with the smart contract act in accordance with these codified “set[s] of promises.”

Now, within the Ethereum ecosystem, smart contracts provide a way for users to form agreements and execute logical code in an economy that is designed with incentives and deterrents to entice individuals to interact in a benevolent manner.

What Can Smart Contracts Do?

To begin, we first must understand that smart contracts are, in fact, a type of account, much like an account that we might use out of an exchange to purchase Ether or an ERC20 token, typically called an externally owned account as opposed to a smart contract account. This “account” designation for smart contract means that similar to an externally owned account, a smart contract can hold Ether or ERC20 tokens.

In addition, smart contracts hold code and storage. As expected, the code within a smart contract can be anything within the constraints with which the coding language, Solidity, allows, or any other language that will compile properly to work with the Ethereum Virtual Machine allows for that matter. Storage refers to the ability of smart contracts to store information. For example, an ERC20 token contract stores each user’s individual balance of tokens. However, to store something like an entire document within a smart contract is currently impractical and very expensive, which is why most smart contracts store the minimum amount of information required for a contract. Consequently, instead of attempting to store several pieces of information within a smart contract, many projects have opted to referencing a hash of a document or file, instead of the entire file itself.

Now, three years into the Ethereum ecosystem, the community has developed several standards with which smart contracts are built. The most well known application has been that of token contracts such as the fungible ERC20 tokens or non-fungible ERC721 tokens. These tokens store information about the specific token owners, token transfer abilities and other information. Other applications use smart contracts as a type of verification service. For example, with the Uport system of smart contracts, a user can register their identify and further use that verified identity in other Ethereum applications. Further along the lines of verification, industries are using smart contracts to verify the origin and production process of various products through initiatives such as Viant.

What Can Smart Contracts Not Do (Yet)?

As with any nascent technology, common overreaching assumptions exist. We have already mentioned one common misconception regarding smart contracts — Ethereum smart contracts do not easily or cost-effectively hold large amounts of data.

Another common misunderstanding regarding the capabilities of smart contract is their autonomy or lack thereof. In other words, smart contracts do not exist with any autonomy or ability to create their own transactions. An externally owned account must initiate a transaction — a small caveat to this is that once an externally owned account initiates a transaction a smart contract may call another smart contract. However, the transaction will always ultimately start via an externally owned account. Now, there are services in which an externally owned account may be programmed or timed to execute a certain transaction when conditions are met; however, this transaction again will not initiate from the smart contract itself.

Along the lines of autonomy, another common misconception regarding smart contracts is the ability to respond to data. As mentioned, a change in a smart contract must be initiated through an externally owned account. For example, say we wanted to watch the price of oil, and when it dropped 5%, we wanted to update pricing. There are no immediate, available methods with which a smart contract can watch or query information outside the Ethereum blockchain. Other services must be brought in to accomplish that goal of watching external data and responding to it on Ethereum.

Your First Solidity Smart Contract

As a simple starter, let’s build a smart contract with which we can store a number, send Ether to the contract and specify who we want to withdraw that Ether. You can follow along in Remix — the Solidity IDE that allows developers to interact with the main net, test nets, and a built in Javascript Virtual Machine that also mirrors running a local Ethereum blockchain. The code is posted here.

First, at the top of our smart contract we must indicate what compiler version we would like to use. Solidity is a compiled language. This means before we interact with the code, it must be compiled into bytecode to be read by the Ethereum Virtual Machine. To do this, we use:

pragma solidity ^0.4.24;

This tells the compiler to use the 0.4.24 version — the latest, most stable to date at the time of this writing. The ^ tells the compiler to use any compiler version 0.4.24 or higher and is to be used only in development stages. Whenever a contract is ready to be deployed to the main net, best practice is to remove the ^ so that an exact, expected compiler version is utilized.
Next, we initialize our contract by naming it, and we can add the first global variable that we would like to store, our number:

contract FirstSmartContract {
 uint256 public theNo;
}

Above we initialize a global variable theNo in which we will store our number. It will be an unsigned integer of 256 bits, to put it simply, it’s a positive number. Notice that we denote this variable with the visibility specifier public . Using this specification, we automatically receive a “getter” function in which we can get the value of theNo from the contract.

There are other visibility specifiers we could have used, which include private and internal. The private specification is a bit of a misnomer. As private the variable will still be available for anyone examining the blockchain to see; however, the automatic getter function that we received with the public specifier will not be generated. Unlike the private specification, the internal specification, allows a variable to be accessed only from contracts that inherit from it but similarly there is not getter function provided.

Now, as we said in the beginning, we would like to be able to set this number, so we can set up a function as the below to accomplish that goal:

contract FirstSmartContract {

    uint256 public theNo;

    function setNo(uint256 _theNo) public {
       theNo = _theNo;
   }
}

The above function setNo() takes in a uint256 _theNo and assigns it to our global variable theNo. Up until this point, if we had used the getter function to set what number was stored by theNo, we would have seen 0 — this is a universal default of Solidity, unassigned variables default to 0 value.

As our variables had visibility specifiers so do functions. A public visibility specifier allows any externally owned account or other smart contract to make a call to this function. Additionally, calls to a public function can be made from within the contract itself from other functions as well. Similar the variable visibility specifiers, the private specifier only allows the function to be called within the contract, not any contracts inherited, as in another function within the contract would be the other way to call this function. Internal functions may only be called within the existing contract or from inherited contracts. There is also an external visibility specifier in which functions can only be access externally, not internally within the contract.

Now, recall that we wanted to be able to send Ether to this contract and assign it to another address to withdraw. Let’s see some additions that will allow us to do that.

contract FirstSmartContract {
     uint256 public theNo;
     mapping (address => unit256) public balance;
     function setNo(uint256 _theNo, address _receiver)
        public
        payable
     {
           theNo = _theNo;
           balance[_receiver] = msg.value;
     }
}

First, we have added a mapping in which we take an address and map that address to a uint256, which will be a balance in our case. Again, we have used a public visibility specifier so that we will gain an automatic “getter” function.
Looking to our updated setNo() function, we have added an additional parameter for an address _receiver or the address we will choose allow to withdraw the Ether sent to the contract within the contract using:

balance[_receiver] = msg.value;

This maps the address of the _receiver to the amount of Ether sent to the contract. Msg.value is a global variable available in any contract function, which refers to the amount of Ether sent in the transaction. Note that the amount of Ether will actually be denominated in Wei — one Ether is 10¹⁸ Wei.

Now, we want to allow the _receiver to withdraw the Ether in the contract. In order to do that, we implement the following function:

function getEth() public {
   require(balance[msg.sender] > 0);
   uint256 amount = balance[msg.sender];
   balance[msg.sender] = 0;
   msg.sender.transfer(amount);
}

This is our first view of a require() statement. Require() statements in Solidity allow us to specify certain conditions that must evaluate to true in order to allow the function to fully execute. Should the conditions within a require() statement evaluate to false, then the function and all actions prior to the require() statement would be reversed or reverted.

Here, we have required that the account sending the transaction have some Ether assigned to it in order to proceed with the withdrawing — as there is no sense in allowing an address with zero Ether assigned to withdraw zero Ether.

Again, msg.sender is a another global variable available in Solidity functions that refers to the account that sent the transaction — note that this can refer to a smart contract account as well, should the transaction be sent from a smart contract. In our case, if the msg.sender does have some Ether assigned to their account, we assign that Wei amount to the local variable amount. To ensure that they do not attempt to withdraw more Ether than their allotted amount, we now set their mapping to 0. This type of pattern ensures that we are not vulnerable to re-entrance attacks — a much larger topic that is discussed in detail here.

Lastly, we use the global function transfer() to send the amount to the msg.sender, which has uses the form:

addressToSend.transfer(amountToSendInWei)

An important feature of the transfer() function is that if it is unsuccessful, it will revert, much like our require() statement. Thus, if for whatever reason, transfer() is unable to execute, the msg.sender will not lose their assigned Ether as balance[msg.sender] = 0 will revert and return back to the original state.

All together, we have a simple contract that allows us to:

  1. Set a number in the contract.
  2. Send Ether to the contract and assign it to another address to withdraw.
  3. Withdraw any allocated Ether from the contract to a certain address.

The full code is below and can be found here.

contract FirstSmartContract {

   uint256 public theNo;
   mapping (address => uint256) public balance;

   function setNo(uint256 _theNo, address _receiver)
       public
       payable
   {
       theNo = _theNo;
       balance[_receiver] = msg.value;
   }

   function getEth() public
   {
       require(balance[msg.sender] > 0);
       uint256 amount = balance[msg.sender];
       balance[msg.sender] = 0;
       msg.sender.transfer(amount);
   }

}

We hope this brief introduction has allowed you to understand a bit more about where and how the idea for smart contracts came together. Smart contracts truly enable an entirely new economic system through the use of Ether, tokens, and decentralization verification of data. There are many more nuances to understand in order to build effective, scale-able, and secure smart contracts, and we intend to dive into those aspects in further publications.

What is a Smart Contract?
Share this

Subscribe to Token Foundry