Introduction
One of the most essential and helpful tools a blockchain developer has as an arsenal is making contract calls. When working on complex and multi-contract projects, it is very likely, that you won’t be deploying a single, smart contract file for the whole production. You might even deploy some contracts earlier than others.
Making contract calls is a flexible way for deployed contracts to interact with others on the blockchain.
This way, rather than having a messy long line of code, you have a network of contracts interacting with each other on the blockchain.
Throughout this tutorial, you will learn how to:
- Install and Setup Hardhat.
- Create a dummy smart contract.
- Use hardhat to deploy to the Celo Alfajores Network.
- Create a proficient test script on a hardhat.
- And make contract calls on your deployed contract using hardhat test scripts.
Prerequisites
To get the best out of this tutorial, you should have a basic and foundational understanding of the following:
- Celo Alfajores Testnet
- Faucets
- Hardhat, Don’t worry, you will be installing hardhat alongside this tutorial
- Node
node
and Node Package Managernpm
. This tutorial will make use of the node package manager.
You should have the node package manager npm
pre-installed. Follow the links for more information on installing node
and node package manager npm
.
Requirements
- We’ll need Metamask in this tutorial. Install it from HERE.
- Make sure to have NodeJS 12.0.1+ version installed.
A brief definition of Keywords
Before you get started with this tutorial, here is a quick recap of the keywords you’ll be working with during this tutorial.
Celo Alfajores
The Celo Alfajores is a test network run by the Celo Team. It is a blockchain simulation that enables deployments and testing of smart contracts on a testing blockchain. Although it is regarded as a testing blockchain, it primarily simulates deploying and testing contracts on the Celo Blockchain.
It functions exactly as effectively as on the Celo mainnets, except you call transactions using faucet funding (testnet money).
Faucets
These are simply testnet money funded into your wallet only to interact with a Testnet Blockchain.
To make transactions on the Alfajores Testnet you need CELO Testnet tokens.
Following this tutorial, you will need CELO faucets to deploy and transact on the Celo Alfajores blockchain.
Getting faucets is always as easy as taking these few baby steps:
-
Head over to the faucet site for the testnet you need. For example, a Celo Alfajore faucet will give you tokens to interact with the Celo Alfajore testnet (which you will also use in this tutoria).
-
Copy your wallet address from metamask or your preferred wallet and paste it into the tab.
-
Complete the authentication process, usually, I am not a robot captcha. Click the send button and wait for about 15 to 90 seconds, depending on the requesting network, and you’ll notice an increase in your wallet balance.
HardHat
This is an Ethereum Development Environment that runs on ether-js
, and other basic EVM-compatible libraries. It is used for compiling, running, and deploying solidity smart contracts.
Calling Contracts
What are the contract calls referred to in this tutorial?
Making a contract call simply means calling the functions from a deployed contract into another deployed contract on the blockchain.
The calls can be made to retrieve data from a query function, to a payable function for making payments, or even a modifier function for modifying a variable state.
Now that you’ve been reminded of the tools we’ll need, it’s time to get your hands dirty with writing code to understand the purpose of this tutorial.
Installing and Setting up Hardhat
To get started with the coding part o this tutorial, you need to install Hardhat.
In the next couple of steps, you will learn how to install Hardhat into your local work environment using yarn on your preferred package manager.
-
Create a workspace in you’re preferred code editor.
-
Go to the terminal of your work environment and run the code
npm init -y
. This is to initialize apackage.json
file. -
Run the command
npm install --save-dev hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers @nomicfoundation/hardhat-toolbox
. also, run the commandnpm i hardhat-deploy
on your terminal to install all the required dependencies you’ll need for this tutorial. -
Next run the command
npx hardhat
to fire up your hardhat development environment. You will be prompted to choose the language you’ll be working with. -
Click enter twice to enable the option
Create a Javascript Project
and to verify the project location. You will notice a new folder structure on your code editor file explorer.
Now that you have successfully installed and Setup up your hardhat development environment. next you will create the exemplary contracts you need to test the contract calls.
Creating your Smart Contracts
To simulate a contract call, you will need to create two smart contracts. These two contracts will be deployed on the Celo Blockchain.
One of the contracts will have the calling functions TestContract.sol
, while the other contract, Person.sol
will have the functions you will be calling from the previous contract, TestContract.sol
.
The Calling Contract Person
Navigate to the contract folder in your workspace and rename the existing contract from Lock.sol
to Person.sol
.
To initialize the contract and the needed variables, copy and paste the code below:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract Person {
// Initializing the variables to Null or None
string name;
uint256 age;
bool has_payed;
}
Inside the Person.sol
contract you will create the following simple functions:
- The first function will be an external function
getDetails
that modifies the public variablesname
, andage
. The function will accept a person’s details as inputs and assign them to the public variables.
Add thegetDetails
function below to thePerson.sol
contract created earlier.
function getDetails(string memory _name, uint256 _age) external {
name = _name;
age = _age;
}
- The second function
sayDetails
will also be an external view function that simply returns the most recent person’s details stored in thegetDetails
function.
Copy and add the code below into the Person.sol
Contract as the next function.
function sayDetails() external view returns (string memory, uint256) {
return (name, age);
}
- The third function,
payFee
will be an external payable function that transfers money into the contract to simulate a person making a payment, the function assigns the bool variableis_payed
totrue
and the variable paid amountamount
tomsg.value
.
Copy the function below into the Person.sol
contract.
function payFee() external payable {
value = msg.value;
has_payed = true;
}
- The last contract is an external view function that returns multiple variables
value
,contract_balance
,has_payed
based on the payment functionpayFee
being called earlier.
function getValue() external view returns(uint256, uint256, bool) {
return (value, address(this).balance, has_payed);
}
The four functions created are sample functions to copy a real scenario of calling different types of functions from a contract.
Note: Alternatively, When creating contract calls, you can use the keyword Interface
to initialize the calling contract. To know more about the interface Keyword and other Basic Solidity Data Types, click here.
For Uniformity purposes, copy and paste the entire code below into the Person.sol
contract file.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
contract Person {
string name;
uint256 age;
bool has_payed;
uint256 value;
function sayDetails() external view returns (string memory, uint256) {
return (name, age);
}
function getDetails(string memory _name, uint256 _age) external {
name = _name;
age = _age;
}
function payFee() external payable {
value = msg.value;
has_payed = true;
}
function getValue() external view returns(uint256, uint256, bool) {
return (value, address(this).balance, has_payed);
}
}
The Caller Contract TestContract
The second contract TestContract.sol
will be the testing contract that will make the contract calls to the Person.sol
contract.
The contract will also have four different functions to call the four different functions from the first contract, Person.sol
.
When you want to call contracts from other contracts, one of the inputs has to be the address of the contract you are calling to and following the format below:
function <function_name> <(function_inputs)> <visibility> <mutability> returns(output_datatype) {
do something
return something
}
Note: Note: Do not copy the function above, it is just a layout on how to structure a calling function.
To initialize the TestContract.sol
contract, copy the code below:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import './Person.sol';
contract TestContract {
}
***Note: You’ll need to import the Person.sol
contract to refer to the functions in the Person.sol
contract you’ll be calling.
- The first function,
callGetDetails
accepts the address of the deployedPerson.sol
contract as_test
and the other arguments_name
, and_age
to pass to thegetDetails
function in thePerson.sol
contract.
Copy and add the function below to the contract:
function callGetDetails(address _test, string memory _name, uint256 _age) external {
Person(_test).getDetails(_name, _age);
}
- The second function
callSayDetails
will be an external view function that takes the deployedPerson.sol
contract address as_test
. And returns thename
&age
variables in theSayDetails
function from thePerson.sol
contract.
Copy and add the function below to the contract:
function callSayDetails(address _test) external view returns (string memory, uint256) {
return Person(_test).sayDetails();
}
- The third function
callpayFee
will call thepayFee
function in thePerson.sol
contract. The function is a payable function for sending ETH into the smart contract.
function callpayFee(address _test) external payable {
paying(_test).payFee();
}
- The last function
callgetValue
will be called thegetValue
from the previous contractPerson.sol
. The function will simply return the same values as thegetValue
function.
function callgetValue(address _test) external view returns(uint256, uint256, bool) {
return paying(_test).getValue();
}
Copy and add the code below:
function callgetValue(address _test) external view returns(uint256, uint256, bool) {
return paying(_test).getValue();
}
After adding all the functions created above, your complete TestContract.sol
contract should look exactly like the code below.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import './Person.sol';
contract TestContract{
function callGetDetails(address _test, string memory _name, uint256 _age) external {
Person(_test).getDetails(_name, _age);
}
function callSayDetails(address _test) external view returns (string memory, uint256) {
return Person(_test).sayDetails();
}
function callpayFee(address _test) external payable {
Person(_test).payFee();
}
function callgetValue(address _test) external view returns(uint256, uint256, bool) {
return Person(_test).getValue();
}
}
Next, you’ll be deploying the contracts you’ve created to the Celo Blockchain.
Deploying to Celo Alfajores
Hopefully, you should be familiar with deploying a contract on the Celo blockchain. If not, here is a quick guide on deploying to the Celo Blockchain.
In the next few steps, you will deploy both of the previously created contracts to the Celo blockchain to begin making the contract calls.
-
To Compile the Contracts run the command
npm hardhat compile
on your terminal. -
Head over to the deploy folder and replace the
Lock.js
deploy script with another two deploy scripts. Rename the files withdeploy_TestContract.js
anddeploy_PersonContract.js
. -
Copy and paste the code below into the
deploy_PersonContract.js
file:
const hre = require("hardhat");
const main = async () => {
const PersonContract = await hre.ethers.getContractFactory("Person");
const Person = await PersonContract.deploy();
await Person.deployed();
console.log("The Person contract was deployed to: ", Person.address);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.error(error);
process.exit(1);
}
};
runMain();
- Copy and Paste the code below into the
deploy_TestContract.js
file:
const hre = require("hardhat");
const main = async () => {
const TestContract = await hre.ethers.getContractFactory("TestContract");
const TestingContractCalls = await TestContract.deploy();
await TestingContractCalls.deployed();
console.log(
"The TestContractCall contract was deployed to: ",
TestingContractCalls.address
);
}
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.error(error);
process.exit(1);
}
}
runMain();
-
Next, head over to the
hardhat.config.js
file in the root folder and replace the hardhat config. code there with the Celo configuration code here. -
Replace the solidity version specified at the bottom of the
hardhat.config.js
file with the same version of solidity specified in your contracts. -
Run the command
npm i dotenv
to download thedotenv
dependency, and create a new file in the root folder.env
. -
Create a variable name
MNEMONIC
inside the dotenv file and add your intended wallet MNEMONICs as the value.
Note: Your Wallet’s MNEMONICs is simply the recovery phrase used in creating your wallet. Still not clear on what your MNEMONICs are? Here’s a quick read.
Ensure that the .env
file is added to your .gitignore
file if you’ll be pushing to any version control..
- Finally, run the Following Command to deploy the two contracts:
- Run the command
npx hardhat run scripts/deploy_PersonContract.js --network alfajores
to deploy thePerson.sol
contract.
Note: Make sure to copy the contract address printed on the console; you’ll need it while making the contract calls..
- Run the command
npx hardhat run scripts/deploy_TestContract.js --network alfajores
to deploy theTestContract.sol
contract.
Note: Make sure to copy the contract address printed on the console; you’ll need it while making the contract calls.
And Voila, Contracts Deployed…
Making Contract Calls
Now it’s time to make those contract calls.
You’ll use the built-in hardhat tool Hardhat Console to interact with the contracts on the blockchain and make the contract calls.
- Run the command
npx hardhat console --network alfajores
to activate the hardhat console. You’ll notice a prompt arrow appears>
.
- Firstly, you’ll have to test the functions in the
Person.sol
contract by calling the functions.
-
To begin, Run the code
const Person = await ethers.getContractFactory("Person")
, to get the deployed contract factory. -
Next, run the command
const person = await Person.attach("<Person.sol_contract_address>")
, to gain access to the contract on the blockchain.
A successful transaction should look like the image below:
Now, to call the functions in the Person.sol
contract:
- Run the command
await person.sayDetails()
, returns empty variablesname
andage
. A successful transaction should look like the image below:
- Run the command
await person.getDetails("Albert", 22)
. A successful transaction should look like the image below:
- Rerun the first command
await person.sayDetails()
; this should return the name and the values input you sent in previously.Albert
and 22. A successful transaction should look like the image below:
- Run the command
await person.payFee()
. A successful transaction should look like the image below:
- Run the command
await person.getValue()
. A successful transaction should look like the image below:
- Now that you know what the functions in the
Person.sol
contract does, Now it’s time to try calling the same function from another deployed contractTestContract.sol
.
-
To begin, Run the code
const TestContract = await ethers.getContractFactory("TestContract")
, to simply get the deployed contract factory. -
Next, run the command
const test = await TestContract.attach("<TestContract.sol_contract_address>")
, to gain access to the contract on the blockchain:
A successful transaction should look like the image below:
Note: This is where you’ll need the contract address of the Person.sol
You will need to pass the address as the first argument to all the function calls.
assuming the deployed Person.sol
contract address is: 0xA019Ad7Ed1F3fc0276E0854F2fF022EFeFf5C8e1
- Run the command
await test.callGetDetails("0xA019Ad7Ed1F3fc0276E0854F2fF022EFeFf5C8e1", "Julia", 25)
. A successful transaction should look like the image below:
- Run the command
await test.callSayDetails("0xA019Ad7Ed1F3fc0276E0854F2fF022EFeFf5C8e1")
. A successful transaction should look like the image below:
- Run the command
await test.callpayFee("0xA019Ad7Ed1F3fc0276E0854F2fF022EFeFf5C8e1")
. A successful transaction should look like the image below:
- Run the command
await test.callgetValue("0xA019Ad7Ed1F3fc0276E0854F2fF022EFeFf5C8e1")
. A successful transaction should look like the image below:
Conclusion
Finálè, you have completed and learned quite a lot of new things here. You created two smart contracts, one will call functions and the other to make contract calls across the blockchain; you deployed both contracts to the Celo Blockchain successfully. You also interacted with the deployed contract using the Hardhat Console, and you made several contract calls on the celo blockchain.
Congratulations on taking another big step into the web3 rabbit hole.
Next Steps
You can also read about how to run the unit test for smart contracts using Truffle, and
how to run the unit test for smart contracts using Hardhat.
Here are some other tutorial articles you might be interested in.
About the Author
Mayowa Julius Ogungbola
A Software Engineer and technical writer always open to working on new ideas. I enjoy working on GitHub and you could also find out what I tweet about and connect with me on Twitter.
References
Here is a link to the complete tutorial sample code on my GitHub, Leave a on the repository if you find it helpful.