The Basics of Smart Contracts 1: Hello World in Solidity
- andy1265
- Jun 20, 2022
- 6 min read
Updated: Aug 2, 2024
In this series we will be going over writing smart contracts in solidity and some front end work with JavaScript. The aim of this series is to demonstrate that smart contracts are not nearly as complicated as people assume and that learning to write them can be reasonably straight forward.
The issue that complicates writing smart contracts is security. Very few other languages are as transaction focussed as smart contracts. Neither are other languages immutable or accessible by anyone on the internet. As such later in this series we will go over smart contract security however for this and the next few installments we will simply be focusing on writing very basic smart contracts.
The contract we will write in this installment will be incredibly simple and we will mostly focus on setting up the environment, deploying a contract to a local Ethereum block chain and then calling it with JavaScript using the web3.js library.
Setting up the Environment
To begin with you will want to install NodeJS and NPM (these come bundled together but I have included individual instruction sets just in case they are needed) and a text editor. For the text editor I have used Atom however this is just personal preference and almost every modern text editor will have a solidity plugin that can be used for autocompletion and linting.
Next we need to install the Solidity compiler and Hardhat. There are options other than Hardhat such as Truffle which is very popular however for this tutorial we will be using Hardhat. To install the solidity compiler use the following command:
npm install -g solc
Hardhat is installed in the projects root directory and a local installation is needed in each project where it will be used. The latest version of hardhat at the time of writing is 2.8.0 however the latest version can always be found here.
Now navigate to the location where you would like your project to be stored and issue the following commands:
mkdir solidity_tutorial
cd solidity_tutorial
npm init --yes
npm install --save-dev hardhat
npm install -D @nomiclabs/hardhat-ethers
npm install web3
Following this you should now be able to run the hardhat executable from your project directory. You can test this by running the following command:
npx hardhat init
Then select "Create an empty hardhat.config.js". This should create a file named hardhat.config.js in your projects root. Add the following line to your hardhat.config.js at the top of the file:
require("@nomiclabs/hardhat-ethers");
Following this we need to make a folder to store our contracts and a source file for our contract code.
mkdir contracts && cd contracts && touch contract.sol
At this point you should now be setup to start writing the source code for your first solidity project.
Solidity Contract Code
Our first solidity contract will simply return the string "Hello World!". It's only 6 lines of text and is about as simple as a smart contract can get.
// SPDX-License-Identifier: MIT
pragma solidity >= 0.4.22 < 0.9.0;
contract Contract {
function getString() public view returns (string memory) {
return "This is working!";
}
}
We will now go over this contracts code line by line starting with the first one:
//SPDX-License-Identifier: MIT
The first line is just a reference to the License being used. In this case it is the MIT license which is very commonly used with open source projects and can be found here.
pragma solidity >=0.4.22 <0.9.0;
This line states the version of the solidity complier we want to use. The line code states that it is compatible with compilers of version greater than and equal to 0.4.22 but less than version 0.9.0.
contract Contract {
This line declares a new contract which our code will be encapsulated in. Contracts are very similar to classes in other OOP languages. There are some differences such as smart contracts are only instantiated once and the state persists across all transactions where a normal class is instantiated each time a new instance is created.
function getString() public view returns (string memory) {
This is the main line of this program. It's a function declaration. It states the name of the function, input parameters (in this case none) the functions access modifier, the function type (which can be pure, view, constant and payable) which is view and means the function is read only, the return type which in this case is a string and finally we have memory which means the value is only to be stored for the short term.
Short term storage means the value is only stored for the life of this instance of the smart contract.
return "This is working!";
This final line just returns the string "This is working!" to the caller.
Deploying the Smart Contract to a Local Block Chain
Now our contract is written we are ready to deploy it to our local block chain. To do this we will use the hardhat tool again.
First we will create a folder named scripts in our projects root directory to hold our deployment script and then create a file named deploy,js in the scripts directory.
mkdir scripts
touch scripts/deploy.js
Then we need to compile our contract. It is important to note that your contract must be in a file name "contracts" or hardhat will not be able to identify what to compile.
npx hardhat compile
Following this copy and paste the below code into deploy.js. Don't worry too much about what this does for now as we will go over deploying contracts in more detail in a later installment.
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying contracts with the account:", deployer.address);
console.log("Account balance:", (await deployer.getBalance()).toString());
const newContract = await ethers.getContractFactory("Contract");
const NewContract = await newContract.deploy();
console.log("Contract address:", NewContract.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
In one terminal run the following to start a hardhat ethereum node and a local testnet:
npx hardhat node
and in another run the following command to deploy your new smart contract to that testnet:
npx hardhat run scripts/deploy.js --network localhost
Now our script is deployed you should see three lines of output from the terminal and the last one should be a contact address which will look something like this:
Contract address: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Keep this handy as we will be using it in the next step.
**Should you get an error regarding the compiler version check what version is listed in your hardhat.config.js file and ensure your pragma statement is the same. If the solcjs command gives you a compiler version error change the value in your pragma statement and your hardhat.config.js to be the same as the one suggested by solcjs.
Calling the Smart Contract With JavaScript
Now we are onto the final part. Calling our smart contract with JavaScript code. This allows us to simulate what calling our contract would like from a real web application.
To begin with we will need the Application Binary Interface (ABI) of our contract. An ABI tells you how to make calls to a specific contract and what output to expect.
solcjs --abi contracts/contract.sol
This will create a file containing a JSON string which will look something like the following:
[{"inputs":[],"name":"getString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]
Now that we have these two we are ready to start writing our JavaScript. You can make your JavaScript file wherever you want. I personally put mine in the root of my project however it is not required.
Now we want to take the Javascript below and paste it into the file that was just created. I will only go over the lines here that are relevant to the smart contract here. There are many places on the internet you can learn more about how JavaScript works in general.
const {Web3} = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:8545"));
const contract_abi = [{"inputs":[],"name":"getString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]
async function getOutput(){
const contract_address = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
var Contract = new web3.eth.Contract(contract_abi, contract_address);
console.log(await Contract.methods.getString().call());
}
getOutput();
So lets go over each relevant line.
const Web3 = require('web3');
So this file starts out by requiring the web3 module we installed at the start of this tutorial.
const web3 = new Web3(new web3.providers.HttpProvider("http://localhost:8545"));
Then we are connecting to our local testnet which is running on port 8545.
const contract_abi = [{"inputs":[],"name":"getString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]
After that we declare our ABI.
const contract_address = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
Then we declare our contract address.
var Contract = new web3.eth.Contract(contract_abi, contract_address);
console.log(await Contract.methods.getString().call());
We then instantiate a new instance of this contract and finally we get the output and display it in the terminal.
We can now run the following command and expect to get the string "This is working!" as the output.
node Javascript_file_name.js
Challenges
Hopefully this was all useful to you. I always find having a few challenges at the end of a tutorial to work on and try and really ingrain the information into my mind is very useful. As such here are 3 things you can do to expand on the information in this article:
Output a different string.
Return something that is not a string.
Recreate the whole project using Truffle instead of Hardhat. (the steps are very similar)
Create a basic html page to output the results with instead of using the terminal.
Comments