Let's create an Ethereum private network node, code a smart contract and deploy it to the network, then build a decentralized application that is powered with our network.
- JavaScript (Main programming language).
- Solidity (Ethereum programming language to write smart conracts).
- Shell (To run the commands).
- Geth - 1.10.15-stable or above.
- NodeJS - 12 LTS or above.
If you don't want to go through the full tutorial, just download or fork the repo and then run:
npm install
or
yarn
then skip the setting up the project, and jump into starting a node.
Create a new directory for the project.
mkdir aures-blockchain && cd aures-blockchain
Inside that directory create another directory for your blockchain.
mkdir blockchain
The bootnode is the process responsible for linking nodes together, this process is also responsible of identifying peers in as specific network.
First change directory to your blockchain folder
cd blockchain
First we have to generate a key for our bootnode
bootnode -genkey boot.key
Then to start the bootnode use the following command:
bootnode -nodekey boot.key -nat "none:127.0.0.1" -addr ":30300"
The output should include an enode that looks similiar to this, this text will be used to connect nodes in the same network to each other:
enode://1fef27cc534a194318d040b2dc3c9775a879c9fca1aa6ee75f00406c1cd09f2f1009450bfeea4102a7d0f97a90e0c13d7f93e0bdfc4da533f8eb789d08889728@127.0.0.1:0?discport=30300
In order to bootstrap a blockchain node we need a crypto account.
Use this command to generate a new account, the datadir flag is used to specify the directory where the account is going to be stored.
geth account new --datadir node
You will be asked to enter a password, make sure you don't lose that password since it's going to be used to unlock your newly generated account (example password: secret123)
The command will spit out an account address that looks similiar to this:
0xf35Ea5Cd8E2302A07263A94BD9cbB239eeeC2c40
Make sure you store the password in a text file inside the node directory and call it password.txt so that we can unlock the account using this file later when we try to start a miner node.
cd node && touch password.txt && echo secret123 > password.txt
In order to start our node we first need to setup our genesis block
We can use puppeth to generate a genesis block easily, note that Puppeth comes with Geth.
puppeth
Puppeth will prompt you with steps to setup your genesis block, make sure you follow them carefully.
We are going to build a PoA network, make sure you choose the Clique Consensus Engine. Also, for the accounts that are allowed to seal, enter the address of the account that we generated before. As for the accounts to be prefunded, use the address of your test account that you can get from a wallet like Metamask.
Use the newly generated genesis block to initialize your account that you created earlier, in my case my blockchain network name is "aures" so the genesis block JSON file will be called "aures.json"
geth init --datadir node aures.json
To start the node we can use the newly generated account, the bootnode address that we setup earlier and the genesis block that we setup:
The network id comes from the genesis block, open up the genesis block JSON file and look for the chainId property.
It should look something like this, replace the arguments to the flags with your own args.
Also notice that the node we are about to start is a signer node (aka the miner node in PoA networks).
This node will be responsible for authorizing and sealing new blocks.
geth --datadir node --networkid 38028 --bootnodes "enode://1fef27cc534a194318d040b2dc3c9775a879c9fca1aa6ee75f00406c1cd09f2f1009450bfeea4102a7d0f97a90e0c13d7f93e0bdfc4da533f8eb789d08889728@127.0.0.1:30300" --port 30301 --http --http.corsdomain "*" --http.port 8301 --allow-insecure-unlock --unlock 0xf35Ea5Cd8E2302A07263A94BD9cbB239eeeC2c40 --password "./node/password.txt" --mine
If everything goes well, then congratulations you have successfully setup a new blockchain, you can add other nodes (members or peers) to this network by following the same steps mentioned before, but make sure you initiate them with the same genesis block, networkId and enode address.
Note: If you want to start a non-miner node, then all you have to do is remove the --mine
flag at the end of the node start command.
To generate a new react application we are going to use the create-react-app CLI.
Switch to the your parent folder aures-blockchain
and run the following command
npx create-react-app .
or if you are using Yarn:
yarn create react-app .
Hardhat is an Ethereum development environment that will help us compile and tes our contracts before we deploy them to a specific network.
npm install --save-dev hardhat
or, if you are using yarn:
yarn add -D hardhat
then run the following command, you will be prompted What do you want to do?, you can choose Create a sample project or another option if you are sure what you are doing.
For me I ll just go with a sample hardhat setup:
npx hardhat
After hardhat is initialized you can see that you have a few extra folders on your directory such as scripts and contracts
- contract: is a folder where all of our smart contracts files will be stored.
- scripts: is a folder where we can write scripts that we can later run.
- test: is a folder where we can write test scripts to test our contracts before deployment.
We can also see that we have a new file in our directory called hardhat.config.js
, this is the configuration file of our hardhat environment, for now we only need to change one thing which is the exported object of our file.
module.exports = {
solidity: '0.8.4',
paths: {
artifacts: './src/artifacts',
},
networks: {
hardhat: {
chainId: 1337,
},
},
};
As we can see we changed the output path of our compiled contracts to ./src/artifacts
, we will find the ABI (application binary interface).
To create a smart contract we have to write the code for it and compile it using a Solidity language compiler, then take the result of the compilation and deploy it to our blockchain.
First we have to create a new file inside our contracts folder, for the sake of this tutorial, we'll be creating a bank smart contract, the contract will:
- Have accounts for different addresses.
- Each account has a balance.
- Account owners will be able to see the balance of their accounts.
- Account owners will be able to credit, debit and transfer money from their accounts if certain conditions are met.
So let's create our file and call it Bank.sol
cd ./contracts && touch Bank.sol
You can open up that file with your favourite code editor, then copy paste this code inside
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract Bank {
string public name = "Aures Bank";
string public symbol = "DZD";
uint256 public initialSupply = 10000000;
mapping(address => uint256) balances;
constructor() {
balances[msg.sender] = initialSupply;
}
function debit(uint256 amount) external returns (uint256) {
require(balances[msg.sender] >= amount, "Insufficient funds to debit.");
balances[msg.sender] -= amount;
return amount;
}
function credit(uint256 amount) external returns (uint256) {
// require(balances[msg.sender] > amount, "Insufficient funds.");
balances[msg.sender] += amount;
return amount;
}
function transfer(address to, uint256 amount) external {
require(
balances[msg.sender] >= amount,
"Insufficient funds to transfer."
);
balances[msg.sender] -= amount;
balances[to] += amount;
}
function balanceOf(address account) external view returns (uint256) {
return balances[account];
}
}
Before saving that file, we check if there are any errors in our contract code. If no errors were found, we can proceed to compiling it, to compile our contract we can run:
npx hardhat compile
If the compilation goes well we should see new files are generated inside our src/artifacts
folder.
- A
build-info
folder, dont worry about it right now. - A
contracts
folder: there we can find our compiled contracts.
If we open up our contracts folder from the artifacts, we see a new folder called Bank.sol
folder, inside that folder we can find a file called Bank.json
file, that is what we need in order to deploy our smart contract.
In order to do that we have to write a deployment scripts, open up the scripts
folder, there we can find a JavaScript file, let's rename it to deploy.js
In there we remove the old code that deploys a greeting smart contract and replace it with our own deployment code that deploys our bank smart contracts, it should look something like this.
const hre = require('hardhat');
async function main() {
const Bank = await hre.ethers.getContractFactory('Bank');
const bank = await Bank.deploy();
await bank.deployed();
console.log('Bank contract deployed to:', bank.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
The process is as follows:
- Create a async main function to benefit from asynchronous features.
- Get the contract factory (ABI and binary code) and deploy it to a a network.
- Wait for the address of the deployment contract if it is successfully deployed or catch errors if there are any.
- Exit with code 1.
If we recall, we scpeified a network field on our hardhat.config.js
file.
module.exports = {
...
networks: {
hardhat: {
chainId: 1337,
},
},
...
};
Hardhat network in this case is called localhost.
First we start Hardhat's test network, it will give us some pre-funded accounts to test with
npx hardhat node
We'll get something that looks like:
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
...
Then we run the deployment script against the test network.
npx hardhat run scripts/deploy.js --network localhost
Make sure you pass the network flag and the localhost
argument since we are not deploying directly to our newly created blockchain, but to hardhat test network first.
If everything goes well, we should get a success message and a deployment address:
Compiling 1 file with 0.8.4
Solidity compilation finished successfully
Bank contract deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Whoa! we have successfully deployed a contract to our test network, let's make sure we save that contract deployment address
0x5FbDB2315678afecb367f032d93F642f64180aa3
All we have to do is:
- Edit hardhat.config.json and add a new network configuration object.
- Get the private key of one of our prefunded accounts in order to use to pay for the contract deployment.
Inside hardhat.config.json we add
aures: {
chainId: 38028,
url: 'http://127.0.0.1:8301',
accounts: [process.env.ACCOUNT_PK],
},
Then we setup an environment variable containing our account's private key.
In the root directory we create .env
file.
touch .env
Inside the .env
file we paste our private key.
ACCOUNT_PK=<private_key_here>
Save that file, make sure your newly created blockchain node is up and running then run the following command:
npx hardhat run scripts/deploy.js --network aures
Notice that I used aures
as an argument to the network flag, in order to deploy it to this specific network.
After a block time, our terminal will return the deployed contract's address, we can copy that address now and connect it to ethers.js or web3 on our front-end app.
Example output:
Bank contract deployed to: 0x57Ad5f6D9E770B54235A1e0DB86D327656f85b8A
Go to ./src/components/Bank.js
and change the bankContractAddress variable to the address we received from the terminal.
const bankContractAddress = '0x67CACc9265321403B24E9d18F1a6813d4fbce684';
All we have to do now is run this command in the root directory of our project:
npm run start
or
yarn start
If you find errors, then make sure you install Chakra UI
needed dependencies then try again.
If everything goes right, a browser window will show up with the app.
Connect your MetaMask account and make sure you are using the right network.
Go to MetaMask extension and click on Ethereum Mainnet button, then click on Add Network
There you add your blockchain:
Network Name -> Aures Testnet
New RPC URL -> http://127.0.0.1:8301
Chain ID -> 38028
And that's it, you can now use the app, thank you for your time, I hope this tutorial was helpful to you in some way.