Intro to Ethereum smart contracts

For programmers interested in Web3, the good news is that your programming skills will easily translate to this new paradigm. That’s because blockchain and its decentralized technology model aren’t replacing the older paradigm; they are augmenting it. Clean code is clean code, anywhere.

We’re going to do a fast but gentle introduction to Web3 and blockchain by building an Ethereum smart contract. A smart contract is a chunk of code that executes on the distributed network of a blockchain. We’ll use Solidity, the most popular high-level language for the Ethereum virtual machine (EVM).

If you have any background in object-oriented programming, you’ll be right at home with Solidity. It is an object-oriented language, although it exists in a unique context. We’ll touch on some of the curiosities of coding in the blockchain. Just remember: you are writing programs that are deployed into a cryptographically secured, distributed transaction datastore. 

The Ethereum blockchain

At the highest level, blockchain applications consist of two main component types: a smart contract and a decentralized application (dApp). We can say that a smart contract is an on-chain chunk of code and a dApp is any off-chain program that interacts with smart contracts. In a sense, dApps are the Web2 clients of Web3 smart-contract backends.

To build on the Ethereum blockchain, we need to deploy code into the network. To do this, we’ll issue a transaction that contains the code. Code-bearing transactions are a special kind of message on the network in that they are executable. Other than that, they behave just like the transactions that move amounts of Ether between accounts. (Ether is the native coin on Ethereum.)

To deploy our contract into the blockchain, we need to participate in the network from a full node. Instead of actually spinning one up, we can use a service like Alchemy that lets us access virtualized infrastructure. Think about it like IaaS for Web3. Note that you will need to sign up for a free Alchemy account.

Set up the Goerli testnet

Now that you have an account we are going to grab some free Ether cryptocurrency (ETH) to play with. Actually, we’re going to grab some free test Ether. Ethereum hosts test networks (testnets) for exactly our current need: developing smart contracts and testing them out. The current main testnet is called Goerli. If you go to goerlifaucet.com, you’ll see a screen like the one shown in Figure 1.

Intro to smart contract development IDG

Figure 1. Goerli faucet: Accessing the Ethereum testnet

The faucet offers a field for an address and a button to click. We’ll put our wallet address in there, then later we’ll use our free ETH to send requests to the contract we’ve developed. Contracts require what’s called gas, which is a transaction fee to run. Contracts also can interact with a value sent, besides the gas fee, called the value of the transaction. Gas and value are always separate.

To use the faucet, you’ll need an Ethereum-compatible wallet. The most common one is MetaMask, which runs as a browser extension. Open another tab in your browser and install the free MetaMask extension

Set up your crypto wallet

If you’ve never used a crypto wallet before, it’s a bit different from other applications. The most critical thing to keep in mind is that you will set up a seed phrase for your wallet. This is a cryptographic key that will let you recover the wallet in case of a disaster, like forgetting your password or losing your device. The seed phrase must be kept safe, as anyone who has it can access the funds in your wallet. 

Once you have a MetaMask wallet set up, enable the test networks by toggling the switch as shown in Figure 2.

Intro to smart contract development IDG

Figure 2. Enable test networks in MetaMask

Now that you’ve enabled testnets, you can open the extension at the top-right of your web browser (mine is Chrome), and select the Goerli network from the dropdown list.

There should also be a button just below the network selector, which says something like “Account1 0x744…” That is the address for your wallet. Click the button and copy the address into your clipboard.

Now, return to the Goerli faucet page and put your wallet address into the appropriate field, then hit the Send me ETH button. After waiting a few moments for the validators to accept the transaction, you can open up MetaMask and see the .1 ETH in your wallet.

Next, go back to the Alchemy tab and click the Apps dropdown list at the top of the screen, then hit Create App. In the form presented to you, give the application a name (mine is “InfoWorld Intro”). Leave the chain as Ethereum and select Goerli as the network. Hit Create Application

The application will now appear in the Alchemy dashboard. Note the field on the application list called API Key. This is the address of the application on the network. If you click that, you can get the address, which you’ll need in a few moments.

Set up the Hardhat tooling

Hardhat is a suite of tools for developing Ethereum applications. To start a new project with Hardhat, navigate to an empty folder on your command line and type npx hardhat. This launches an interactive console. For our demo here, select Create a basic sample project. You can accept all the defaults, which will deploy a new project structure.

This is an npm project with familiar elements like package.json and a /node_modules directory. There are three other directories:

  • /contracts holds the actual smart contract code.
  • /scripts contains scripts to help deploy smart contracts.
  • /test is for testing smart contracts.
  •  

Finally, there is the hardhat.config.js file, which is a JavaScript file to configure plugins and tasks.

Hardhat has many capabilities, but we’re going to skip right along here and get to deploying the contract, which is shown in Listing 1.

Listing 1. A simple contract in the Solidity language for blockchain


//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0; import "hardhat/console.sol"; contract Greeter { string private greeting; constructor(string memory _greeting) { console.log("Deploying a Greeter with greeting:", _greeting); greeting = _greeting; } function greet() public view returns (string memory) { return greeting; } function setGreeting(string memory _greeting) public { console.log("Changing greeting from '%s' to '%s'", greeting, _greeting); greeting = _greeting; }
}

Listing 1 is a simple program that we use to define a greeting string, which is saved to state and returned via the greet() method. There is a constructor method that is executed only once, when the contract is first deployed. 

Handling environment variables with dotenv

To deal with environment variables, let’s add dotenv to the project with the command npm install dotenv.

Now, create an .env file and add two entries to it, as shown in Listing 2.

Listing 2. Add two entries to a .env file


ALCHEMY_URL = “<URL-FROM-ALCHEMY-PROJECT>”
METAMASK_KEY = “<YOUR-PRIVATE-KEY>”

We’ll use these two fields to configure the deploy script. The ALCHEMY_URL field comes from the Alchemy dashboard we previously noted. The METAMASK_KEY is going to be the private key for the wallet (which is why I suggested using a tester wallet). You can get the private key by going to MetaMask -> Account Details -> Export private keys and entering your wallet password.

The ALCHEMY_URL field will be the location where the contract will deploy; METAMASK_KEY will be the from address and provide the gas to deploy the contract.

Update the Hardhat config

Next, we’ll update hardhat.config.js, as seen in Listing 3. The purpose of the code in Listing 3 is to apply the environment variables we’ve just defined to the Hardhat config. This code also tells Hardhat that the default network to use when running scripts is the testnet Goerli. (You can ignore the task definition.)

Listing 3. hardhat .config.js update


require("@nomiclabs/hardhat-waffle");
require("dotenv").config(); const { ALCHEMY_URL, METAMASK_KEY } = process.env; task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { const accounts = await hre.ethers.getSigners(); for (const account of accounts) { console.log(account.address); }
}); /** * @type import('hardhat/config').HardhatUserConfig */
module.exports = { solidity: "0.8.4", defaultNetwork: "goerli", networks: { hardhat: {}, goerli: { url: ALCHEMY_URL, accounts: [`0x${METAMASK_KEY}`] } }
};

Deploy the contract

Now, you can deploy the contract by typing npx hardhat run scripts/sample-script.js. You should get a confirmation that the greeter contract was deployed. This confirmation will give you the address where the contract was deployed. If you check your MetaMask wallet, you should see the Goerli balance has been debited around .001 ETH. Your Alchemy dashboard also will reflect activity on the project.

Before we move on to running a request against the new script, let’s look at the deploy script that took care of pushing the contract to the blockchain. The deploy script is shown in Listing 4.

Listing 4. The deploy script: /scripts/sample-script.js


const hre = require("hardhat"); async function main() { const Greeter = await hre.ethers.getContractFactory("Greeter"); const greeter = await Greeter.deploy("Hello, InfoWorld!"); await greeter.deployed(); console.log("Greeter deployed to:", greeter.address);
} main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });

Listing 4 uses the ethers.js project that the Hardhat template installed to simplify deploying contracts. The script grabs the greeter contract by name (“Greeter”) from Hardhat, which has compiled it for us automatically. It then is deployed with the argument for the constructor (“Hello, InfoWorld!”).

The script relies on the Hardhat config to know what network to deploy to. Hardhat automatically knows where to load the contracts from.

Interact from the Hardhat console

Let’s interact with the live contract from Hardhat’s REPL shell. Type: npx hardhat console. This will attach us to the console with the default network and private keys we defined earlier. Now enter the commands in Listing 5.

Listing 5. Using the Hardhat console to interact with the Goerli contract


> npx hardhat console
METAMASK_KEY: ***
Welcome to Node.js v16.14.2.
Press Ctrl+C to abort current expression, Ctrl+D to exit the REPL > const Greeter = await ethers.getContractFactory("Greeter");
undefined
> const greeter = await Greeter.attach("0x8cAFa7a0F3cDd8Aeb69F3e73eDE1D65Df89b17Ba")
undefined
> await greeter.greet(); 'Hello, InfoWorld!'
greeter.setGreeting("Hello, FooBar!"); 'Hello, FooBar!'

Listing 5 shows you how to interact with the contract using the contract defined in sample-contract.js. This lets you instantiate the interface and make remote procedure calls against it: greeter.greet() and greeter.setGreeting().

For more information on using contracts, including from within code, see the OpenZeppelin guide to deploying and interacting with smart contracts. The process is just like you’ve seen on the interactive console.

Once you have the ability to write code that accesses your blockchain contracts from familiar platforms like Java and JavaScript you are off to the races. You can create and deploy smart contracts and bridge the divide between Web3 and more conventional Web2 applications.