The Computation of Tomorrow … Today!

Ethereum Tutorial

by: burt rosenberg
at: university of miami
date: april 2026

HelloWorld

We will create an ethereum contract HelloWorld with methods getGreeting and setGreeting. Find the code on github.com/the-real-blockchin.

Our work will be done in two programming languages: JavaScript and Solidity.

We will use the Hardhat development environment. Hardhat is a Node package which automates the creation, deployment and testing of Solidity contracts. It uses convention over configuration, with a directory structure and configuration files determined by the Hardhat environment.

We will run our code on a block-chain. Consider these three cases of blockchains:

Hardhat localnet
Where Hardhat is our IDE. This blockchain is spun up on your localhost on demand, and represents the easiest way run etherium code.
Sepolia testnet
This requires sepolia eth which you get for free from a faucet. Where "for free" has the usual meaning: provide the sender of the eth with a marketing opportunity. However it exactly etherium, just with Sepolia tokens.
Ethernet mainnet
This will cost you money and all your mistakes are forever.

Installation

Install node version 22.

% echo Download and install nvm
% curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
% export NVM_DIR="$HOME/.nvm"
% [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 
% [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" 

% echo Download and install Node.js
% nvm install 22
% node --version
v22.4.1
Create a directory for your project. Change into that directory then initialize npm and node.js, and install and initialize Hardhat.
% mkdir hello-eth
% cd hello-eth
% npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

Press ^C at any time to quit.
package name: (hello-eth) 
version: (1.0.0)  
description:  
entry point: (index.js)  
test command:  
git repository:  
keywords:  
author:  
license: (ISC)  
About to write to /home/ubuntu/hello-eth/package.json:

{
  "name": "hello-eth",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Is this OK? (yes)  

% npm install --save-dev hardhat

added 60 packages, and audited 61 packages in 14s

16 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

% npx hardhat --init

Which version of Hardhat would you like to use? · hardhat-2
Where would you like to initialize the project?

Please provide either a relative or an absolute path: · .
What type of project would you like to initialize? · · empty-hardhat-config-js
You need to update the following dependencies using the following command:
npm install --save-dev "hardhat@^2.14.0"

Do you want to run it now? (Y/n) · true 

% npm install --save-dev "@nomicfoundation/hardhat-toolbox@hh2"
% echo now add |require("@nomicfoundation/hardhat-toolbox");| to the top off hardhat.config.js

A first Contract

Create the contracts directory in the project directory, and in that we will write this Solidity code into the file HelloWorld.sol,

// contracts/HelloWorld.sol
// SPDX-License-Identifier: UNLICENSED
//  ^ means 0.9 etc also acceptable (~ means 0.8.25 also acceptable)
pragma solidity ^0.8.24;

contract HelloWorld {

        string private _greeting = "Hello World!" ;
        address private _owner ;

        constructor () {
                _owner = msg.sender ;
        }

        modifier onlyOwner() {
                require (
                        msg.sender == _owner, "Ownable: caller is not the owner"
                ) ;
                _ ;
        }

        function getGreeting() external view returns(string memory) {
                return _greeting ;
        }

        function setGreeting(string calldata greeting) external onlyOwner {
                _greeting = greeting ;
        }

        function owner() public view returns(address) {
                return _owner ;
        }
}

The compile this code with,

% npx hardhat compile

Deploy the contract locally

We will need the following script to deploy our contract. Write this script to deploy.js in the script directory

// scripts/helloworld-deploy.js

async function main () {
  // We get the contract to deploy
  const HelloWorld = await ethers.getContractFactory('HelloWorld');
  console.log('Deploying HelloWorld ...');
  const hello_world = await HelloWorld.deploy();
  await hello_world.waitForDeployment();
  console.log('HelloWorld deployed to:', await hello_world.getAddress());
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

Note: the use of async and await. Node supports asynchronous programming where tasks are launched in threads. Returning from any such launch is an object the represents the perhaps ongoing execution. This is particularly relevant in ethereum because execution on the blockchain are by volunteer nodes, and the results have to be checked and finalized, and the time for this to occur depends.

Start the blockchain in the background.

% npx hardhat node
Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/

Accounts
========

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

Account #1: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000 ETH)
Private Key: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d

....

Account #19: 0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199 (10000 ETH)
Private Key: 0xdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

^Z
% echo control-Z and background the node process
% bg

When Hardhat starts up a local node it creates wallets for several accounts and fills them with test ether. Note in the console the address of the wallerts and their private keys.

You can rejoin this thread with the command fg and that control-C to kil it.

% fg
npx hardhat node
^C
%

You can also kill it using the kill _pid_ command if you know the PID; which you can get by looking at the output of ps aux.

Now deploy the contract to the local chain.

$ npx hardhat run --network localhost ./scripts/helloworld-deploy.js 
eth_accounts
hardhat_metadata (20)
Deploying HelloWorld ...
eth_blockNumber
eth_getBlockByNumber
eth_feeHistory
eth_maxPriorityFeePerGas
eth_sendTransaction
  Contract deployment: HelloWorld
  Contract address:    0x5fbdb2315678afecb367f032d93f642f64180aa3
  Transaction:         0x734b661b8219b522852dff71e7df78fe546504aad6481dcadb556223f6b91392
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  Value:               0 ETH
  Gas used:            528107 of 16777216
  Block #1:            0xf084a45e868274c4af4e93b9eab1c36b27d3e5af08e9f59c87bd5bcc5e36ff68

eth_getTransactionByHash
eth_getTransactionReceipt
HelloWorld deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3

Deploying HelloWorld ...
HelloWorld deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3

Interacting with the contract

Note, there is still open another window on your local machine in which the Hardhat node is currently running, and the console output in the window is interesting and sometimes necessary.

Then get to a console that can connect with the node, to exercise the contract,

% npx hardhat console --network localhost

Welcome to Node.js v22.4.1.
Type ".help" for more information.
> await ethers.provider.getBlockNumber()
1
> const HW = await ethers.getContractFactory('HelloWorld')
undefined
> const hw = await HW.attach("0x5fbdb2315678afecb367f032d93f642f64180aa3")
undefined
> await hw.getGreeting()
'Hello World!'
> await hw.setGreeting('Goodnight Moon')
ContractTransactionResponse {
  provider: HardhatEthersProvider {
    _hardhatProvider: LazyInitializationProviderAdapter {
      _providerFactory: [AsyncFunction (anonymous)],
      _emitter: [EventEmitter],
      _initializingPromise: [Promise],
      provider: [BackwardsCompatibilityProviderAdapter]
    },
    _networkName: 'localhost',
    _blockListeners: [],
    _transactionHashListeners: Map(0) {},
    _eventListeners: []
  },
  blockNumber: 2,
  blockHash: '0x6f288b0fdfcb56173bef9294240cfb31912793a1ccb5e6ec46ed6ea3f01c33b1',
  index: undefined,
  hash: '0xbe27e55d179ba7632cb262c26639ef0ba22dce9e475687c84ca682f7848f8b24',
  type: 2,
  to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
  from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  nonce: 1,
  gasLimit: 30000000n,
  gasPrice: 973870221n,
  maxPriorityFeePerGas: 232421875n,
  maxFeePerGas: 973870221n,
  maxFeePerBlobGas: null,
  data: '0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000e68656c6c6f20657468657269756d000000000000000000000000000000000000',
  value: 0n,
  chainId: 31337n,
  signature: Signature { r: "0x83e432c71291c5f53a6bb27d38d3cff4649555c7344c0e1b2068ebc5cc0bf308", s: "0x75960700254ce1e815c938e02b5949e2987f6ded906926049b3401496700c9f7", yParity: 0, networkV: null },
  accessList: [],
  blobVersionedHashes: null
}
> await ethers.provider.getBlockNumber()
2
> await hw.getGreeting()
'Goodnight Moon'
> .exit
%

Reference
Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

author: burton rosenberg
created: 1 aug 2024
update: 7 apr 2026