Understanding The Web

Ethereum Bootcamp: Smart Contract Basics

Posted

Week 4 of Alchemy University’s Ethereum Bootcamp analyzes the smart contract.  While there are many smart contract languages, Solidity is the smart contract language explored as it is one of the main languages of the Ethereum Virtual Machine (EVM).  Solidity is defined through the idea of constructors, variables, and data types.  Further exploration of Solidity involves the use of functions in smart contracts.  Understanding the basics of smart contracts and functions lays the foundation for understanding smart contract communication.  The big picture these topics put together is the idea of address interactions through smart contracts.  Learning these concepts is essential preparation for a deep understanding of smart contracts. 

Solidity Syntax

Smart Contracts Theory

The idea of a smart contract was theorized by a paper written by computer scientist Nick Szabo: “A smart contract is a computerized transaction protocol that executes the terms of a contract.”  In Ethereum, a smart contract is a program that runs on the Ethereum Computer.  Smart contracts inherit the cryptographic properties of Ethereum.  Another key feature is that smart contracts are permissionless: anyone can deploy a smart contract to the Ethereum computer. 

Smart contracts are composed of logic designed for some form of user interaction. The logic of the smart contract dictates how the user will interact with the smart contract.  If a user interacts outside of the scope of the programmed logic, the smart contract will fail to execute.

While there are many smart contract programming languages and smart contract platforms (including an alternative Ethereum programming language known as Vyper), Solidity is the programming language covered in the Alchemy University Ethereum Bootcamp.

Solidity is an object-oriented, high-level programming language that resembles popular languages such as C++, Python, and JavaScript. When the founders of Ethereum were deciding on which language to choose for Ethereum, they decided to create their own language.  Solidity was specifically designed to be compiled and run on the Ethereum computer. 

Smart Contract Basics

Solidity was designed to create smart contracts for the EVM.

The constructor function is called only once when the contract is deployed.  It is typically used to specify state on contract deployment. 

In the example above, owner and isHappy are state variables.  As soon as the contract is deployed, the values provided to the constructor are assigned to the variables.

The concept of visibility is important to understand. Applying public to the variables means that they are accessible via getters.  State variables can be public, internal, or private.  Functions can be external, public, internal, or private. 

Numbers in Solidity are declared with int or uint.  A uint is an unsigned integer with a set size, and uint by default is the same as uint256.  A uint256 can represent a very large number and is used to track balances.  As with other programming languages, Solidity can perfor normal mathematic operations with numbers.

Along with numbers, Solidity has various data types.  Solidity is statically typed so the type for each variable needs to be declared as seen with uint preceding variables that store numbers.  Solidity has typical data types found in other languages such as booleans, strings, integers, arrays, mappings, and enums.  A data type unique to Solidity is the address.  Addresses can have the distinction of “payable” meaning the address is able to receive ether. 

Functions

Understanding the basic components of smart contracts such as data types and visibility leads to functions.  Functions contain the logic of a smart contract.

Syntax

Functions in solidity are commonly declared with the function keyword.  The function is named and may or may not take arguments. The function type follows the function name.  The function type includes visibility and modifiers

If a function returns a value, the returns keyword is used.

The example above shows a function that can change the owner of the contract.  This contract can be called:

Once this contract function is called, the EVM will use the address that entered and change the owner.  A transaction will be mined and the effects will be indexed and added to the EVM.

Declarations

Function will often contain the keywords view and pure.  A function with the view keyword will not change any state.  A function with the pure keyword will not change state or be read. 

In the example above, the function sum is an example of a view function.  The sum function does not change any state.  It reads from state producing that value of x and y, but it does not change state. 

The function above is an example of a pure function.  The keyword pure means that this function does not read or write to storage.  While this is not a very useful function, it exemplifies the concept of existing independent from the contract state.

Returns

A more useful example of a pure function is one that returns something.  The function above is typical of functions used in libraries or for functionality that is not specific to a smart contract’s state.  These types of functions are used for independent contract operations. 

In the example above, the return syntax includes the type of value that will be returned.  That syntax requires the return keyword inside the function body.  Functions can also be returned implicitly if the variable name is included in the function declaration:

A function can have multiple implicit returns:

Functions can also return multiple values using the return keyword:

Writing to Storage

Functions that are not pure or view functions write to storage. Functions that write to storage change Ethereum state. Changing Ethereum state will incur a gas fee.

In the example above, the variable z is being changed and written to storage.  Executing this function on the Ethereum network will cost gas. 

Visibility

Like state variables, functions have the property of visibility.  Functions can be public, private, internal, or external.  Any contract or externally owned account can call a public function.  Private functions can only be called by the contract it belongs to.  Internal functions can only be called by the contract it belongs to or contracts that are part of its inheritance chain.  External functions can only be called by other contracts or externally owned accounts.

Smart Contract Communication

Compilation

Understanding smart contract communication begins with the exploration of the low-level details of smart contract compilation.  In order to interact with a smart contract, it must first be compiled and deployed.  Compiling smart contracts produces two artifacts known as Application Binary Interface (ABI) and Bytecode. ABI is used to allow front-end libraries to communicate with the smart contract will bytecode is deployed to the blockchain. 

ABI – Application Binary Interface

In computer science, an ABI is an interface between two program modules.  The communication between an operating system and a user program is bridged by an ABI.  An ABI acts as a bridge that encodes and decodes machine code.  It defines how data structures and functions are accessed in machine code.

In Ethereum, a contract’s ABI is the standard way to communicate with the contract. Whether interacting with a contract from outside the blockchain or attempting contract-to-contract interaction, the ABI is needed to conduct communication.  A contract’s ABI defines how functions in the contract  can be invoked, and it describes how each function will accepts arguments and return its result. 

What Does ABI Look like?

The example above is a smart contract that functions as a counter. It has a single state variable counter and three functions.  The following image is an example of what this contract’s ABI would look like:

The ABI for the contract is a JSON object.  It is necessary in order for front end tools to communicate with a smart contract.

Interacting With a Smart Contract

When a web application tries to interact with a smart contract on Ethereum, it needs the contract’s address and the contract’s ABI.  Front-end libraries such as ethers.js use the ABI to understand and interact with the contract.  The following example shows how a front-end library can use a contract’s ABI to interact with a smart contract.  The script calls the inc function from the contract above.

Bytecode

The other artifact produced is the bytecode.  The contract bytecode is the translation of the smart contract that machines can understand.  It represents the actual program in machine code which exists on the Ethereum computer.  Front-end libraries use the ABI to communicate with the smart contract.  On the Ethereum network, the contract exists as machine readable bytecode.

Address Interactions

There are two types of addresses in Solidity: Externally owned accounts (EOAs) and smart contracts.  All transactions originate from an EOA which is controlled by a private key that signs transactions.  A smart contract is code that is deployed to the blockchain. A smart contract is programmed to respond to different inputs sent by EOAs or other contracts.

Calling EOAs

In the example above, any time the smart contract address receives ether, it sends half to each of the authors that are stored in state variables.  The main function, receive, is a payable function. Payable functions can receive ether.  When this contract receives ether, the receive function will be invoked. 

The call syntax is important to remember.  In the code snippet above, a call method is being used.  The call method is found on every address when input data or ether is being sent.  Using the call method makes a call message. 

The curly braces contain information regarding value. The curly braces provide an opportunity to override value and gas parameters on the message call. 

The example above also has an empty string argument. The empty string argument is due to this function sending ether to EOAs. If this function was instead targeting a smart contact, the string argument would contain calldata

Finally, the example shows a boolean return value.  A call method returns multiple values and the first message returned indicates whether the message call was successful.  The require block of code indicates that in order for the logic to execute, the message call must be successful.

Reverting Transactions

Transactions can be reverted.  At its lowest level, Solidity has a REVERT opcode.  This allows developers to use reserved Solidity keywords revert, require, or assert to revert transactions.  Reverting transactions halts the execution of the transaction and removes all state changes.  The transaction can still be included in a block, which means the sender will have to pay gas fees even though the transaction was reverted.

In this example of a failed Uniswap transaction, the user was still charged gas fees.  A decentralized exchange like Uniswap has conditions that must be met for each transaction. In this case, it isn’t clear exactly why the transaction failed.  However, from the time the EOA signed the transaction to the time the transaction was included in the block, conditions changed causing the transaction to revert.  No state changes occurred and token balances remained unchanged, but the transaction still made it to the block so the gas fee was paid by the user.

Calling Contract Addresses

In the contract above, contract A sets the value on contract B. The same call syntax is used as in the previous example. Instead of an empty string, the contract metadata is passed rather than values.  This would be the method used if the logic of contract B existed elsewhere.  In this case, since the contracts exist on the same file, the syntax is much simpler:

It won’t always be necessary to pass call data.  Solidity also has an Interface type which can be used to extract parts of contracts:

These methods are all different ways to accomplish the same goal. 

Conclusion

Developers entering the world of blockchain development can learn various programming languages for different smart contract blockchains.  Solidity is the dominant smart contract language of the Ethereum blockchain.  Solidity was created specifically to create smart contracts on the Ethereum Virtual Machine, and it shares similarities to C++, Python, and JavaScript.  Like other languages, Solidity syntax involves constructors, variables, and data types.  Solidity uses functions to create logic in smart contracts.  Functions are the key to communicating with smart contracts.  The ability to communicate with smart contracts allows the addresses that compose the Ethereum network to interact with each other.  Mastering these fundamental concepts paves the way for a strong foundation in smart contract development.