a first look at avalanche
Avalanche is an open-source, proof-of-stake blockchain with smart contract functionality that uses the Snow family of consensus protocols.
Introduction
Avalanche is an open-source, proof-of-stake blockchain with smart contract functionality that uses the Snow family of consensus protocols. Avalanche features 3 built-in blockchains that are validated and secured by the Primary Network:
- Exchange Chain (X-Chain) - Acts as a decentralized platform for creating and trading digital smart assets like AVAX. These assets are a representation of a real-world resource with a set of rules that govern its behavior. The X-Chain is an instance of the Avalanche Virtual Machine (AVM).
- Platform Chain (P-Chain) - Metadata blockchain on Avalanche that coordinates validators, keeps track of active subnets, and enables the creation of new subnets. The P-Chain implements the Snowman consensus protocol.
- Contract Chain (C-Chain) - Allows for the creation smart contracts using the C-Chainโs API.
All the code for this example can be found on my GitHub.
Configure MetaMask Wallet for Avalanche
You can create an Avalanche Wallet online at wallet.avax.network or you can configure an existing wallet that allows connecting to RPC endpoints. We will use MetaMask in this tutorial which you can download here.
Add Avalanche Network
Open MetaMask and check the available networks. If you just installed the extension, you will only see Ethereum Mainnet.
Click "Add Network" to configure MetaMask for the Avalanche network and include the following information for the Fuji Testnet:
- Network Name: Avalanche FUJI C-Chain
- New RPC URL:
https://api.avax-test.network/ext/bc/C/rpc
- ChainID: 43113
- Symbol: AVAX
- Explorer:
https://testnet.snowtrace.io/
Also include the information for Mainnet:
- Network Name: Avalanche Network
- New RPC URL:
https://api.avax.network/ext/bc/C/rpc
- ChainID: 43114
- Symbol: AVAX
- Explorer:
https://snowtrace.io/
You will now see the Avalanche logo and "Avalanche FUJI C-Chain" written at the top.
Fuji Testnet Faucet
To interact with Fuji, we need to have AVAX in your wallet. Like the Ropsten faucet on Ethereum, Avalanche has the Fuji Testnet Faucet. Include your wallet address and click "Request 10 AVAX."
Return to your wallet and you should now have 10 AVAX.
Create Project
We will use the Vite React template and install dependencies for Hardhat, Ethers, and dotenv
to manage environment variables.
yarn create vite ajcwebdev-avalanche --template react
cd ajcwebdev-avalanche
yarn add -D dotenv hardhat ethers @nomiclabs/hardhat-ethers
Create the directories and files for your smart contract, Hardhat deployment script, and Hardhat configuration.
mkdir contracts scripts
touch contracts/HelloWorld.sol scripts/deploy.js hardhat.config.js
Create a .env
file to hold environment variables for our endpoint URL, private key, and contract address.
echo 'QUICKNODE_URL=\nPRIVATE_KEY=\nVITE_CONTRACT_ADDRESS=' > .env
Add .env
to .gitignore
so you don't commit any private information.
echo '.env' >> .gitignore
Hello World Solidity Contract
Our HelloWorld
contract will have a string variable called helloMessage
. The hello
function will return the value set to helloMessage
. The setHello
function will change the value of helloMessage
to whatever argument is passed into the function.
// contracts/HelloWorld.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.6;
import "hardhat/console.sol";
contract HelloWorld {
string private helloMessage;
constructor(string memory _helloMessage) {
console.log(_helloMessage);
helloMessage = _helloMessage;
}
function hello() public view returns (string memory) {
return helloMessage;
}
function setHello(string memory _helloMessage) public {
console.log("Changing helloMessage from '%s' to '%s'", helloMessage, _helloMessage);
helloMessage = _helloMessage;
}
}
Now that we have created our smart contract, let's look at the script we will use to deploy this contract to Avalanche.
Deployment Script
Our deployment script in deploy.js
calls the getContractFactory
method on the ethers
library and passes in HelloWorld
as the name of the contract. HelloWorldFactory
is deployed with the message Hello from ajcwebdev
and set to helloMessage
which is called on the next line with the deployed
method. The address and signer for the contract are logged to the console.
// scripts/deploy.js
async function main() {
const HelloWorldFactory = await ethers.getContractFactory("HelloWorld")
const helloMessage = await HelloWorldFactory.deploy("Hello from ajcwebdev")
await helloMessage.deployed()
console.log("Contract deployed to:", helloMessage.address)
console.log("Contract deployed by " + JSON.stringify(helloMessage.signer) + " signer")
process.exit(0)
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})
Now that we have our contract and a script to deploy it, the last step is to write our Hardhat configuration.
Hardhat Configuration
The Hardhat configuration includes the Solidity version, path for the contract artifacts, and network information.
// hardhat.config.js
require("dotenv").config()
require("@nomiclabs/hardhat-ethers")
module.exports = {
solidity: "0.8.6",
paths: {
artifacts: './src/artifacts',
},
networks: {
fuji: {
url: process.env.QUICKNODE_URL,
accounts: [`0x` + process.env.PRIVATE_KEY],
chainId: 43113,
},
},
}
We need to include two environment variables in .env
before we can deploy this contract. Set your Avalanche wallet's private key to the PRIVATE_KEY
variable. Now we will visit QuickNode to deploy our RPC endpoint.
Deploy Avalanche Node on QuickNode
First you will need to create an account on QuickNode by filling in the form on the homepage.
Create an Endpoint
After creating an account, you will see the following screen.
Click the "Create an endpoint" button to see the available blockchains.
After selecting Avalanche you will be asked whether you want a node on Mainnet or the Fuji Testnet. Select Fuji.
You'll be asked if you want any of the add-ons including Archive Mode or Trace Mode. You'll then be asked for your credit card information but you will not be charged for the first seven days.
Copy the HTTP provider URL and paste it into your .env
file. Include /ext/bc/C/rpc
at the very end of the URL to specify that you want to connect to the C-Chain, an instance of the Ethereum Virtual Machine that allows for creating smart contracts with the C-Chainโs API.
Deploy Contract to Fuji
Before deploying the contract we need to first compile the contract.
yarn hardhat compile
Deploy the contract and include a --network
flag to specify the Fuji test network.
yarn hardhat run scripts/deploy.js --network fuji
You will receive an output that looks like the following but with your own addresses:
Contract deployed to: 0x873E3BB2A752DBDFA06017CC5a709600Ac3c0153
Contract deployed by "<SignerWithAddress 0x6b492Ef06CA3b462f20db50EB288fAbB1E3e8Bfc>" signer
Go to Snowtrace Testnet and search for your contract address.
Include the contract address in .env
so it can be accessed from our frontend client in the next section.
Create React App
Our contract address can now be used to create a frontend client with React that interacts with the contract's methods.
// src/App.jsx
import { useState } from 'react'
import { ethers } from 'ethers'
import HelloWorld from './artifacts/contracts/HelloWorld.sol/HelloWorld.json'
const contractAddress = import.meta.env.VITE_CONTRACT_ADDRESS
function App() {
const [hello, setHelloValue] = useState()
async function requestAccount() {
await window.ethereum.request({ method: 'eth_requestAccounts' })
}
async function fetchHello() {
if (typeof window.ethereum !== 'undefined') {
await requestAccount()
const provider = new ethers.providers.Web3Provider(window.ethereum)
const contract = new ethers.Contract(contractAddress, HelloWorld.abi, provider)
try {
const data = await contract.hello()
setHelloValue(data)
console.log('Greeting: ', data)
console.log('Contract Address: ', contract.address)
} catch (err) {
console.log("Error: ", err)
}
}
}
return (
<div>
<header>
<h1>Avalanche</h1>
</header>
<main>
<h3>Hello World</h3>
<button onClick={fetchHello}>
Click me, you know you want to
</button>
<div>{hello}</div>
</main>
</div>
)
}
export default App
Include the following in the head of index.html
.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css">
This provides default styles with Water.css.
Start Development Server
Run the following command to start the development server with Vite.
yarn dev
Open localhost:3000 to see the application.
Click the button. You know you want to.
The message will be displayed on the screen and logged to the console. Return to App.jsx
and add the following after fetchHello
but before the return statement begins:
// src/App.jsx
async function setHello() {
if (!hello) return
if (typeof window.ethereum !== 'undefined') {
await requestAccount()
const provider = new ethers.providers.Web3Provider(window.ethereum)
const signer = provider.getSigner()
const contract = new ethers.Contract(contractAddress, HelloWorld.abi, signer)
const transaction = await contract.setHello(hello)
await transaction.wait()
fetchHello()
}
}
Include the following in the return statement below fetchHello
button:
// src/App.jsx
<input
onChange={e => setHelloValue(e.target.value)}
placeholder="Set hello message"
/>
<button onClick={setHello}>
Set hello message
</button>
Now when you enter a new hello message and click the "Set hello message" button, you will be asked to confirm the transaction from your MetaMask wallet.
After confirming the transaction, it will be pending for a few seconds. Once the transaction settles you will see the new message logged to the console.
Configure Netlify Deployment
Our hello world application is complete and we can deploy it to the internet with a service like Netlify or Vercel. Create a netlify.toml
file for our Netlify configuration.
touch netlify.toml
Add the following instructions:
[build]
publish = "dist"
command = "yarn build"
The build command is set to yarn build
and the publish directory is set to dist
.
Create GitHub Repository
Initialize a Git repository and push the project to a GitHub repo.
git init
git add .
git commit -m "let it snow let it snow let it snow"
gh repo create ajcwebdev-avalanche --public --push \
--source=. \
--description="Deploy a smart contract to Avalanche's Fuji Testnet with Hardhat, Ethers, and QuickNode" \
--remote=upstream
I used the GitHub CLI but you can also visit repo.new and follow the instructions provided.
Deploy to Netlify
Go to your Netlify dashboard, click "Add new site," and select the newly created repo.
Your build settings will be imported from the netlify.toml
file. The only other information you need to include is your contract address under "Advanced build settings." Lastly, click "Deploy site."
Go to "Domain settings" to give your site a custom domain. You can see this example at ajcwebdev-avalanche.netlify.app.
Resources
Did you find this article valuable?
Support Anthony Campolo by becoming a sponsor. Any amount is appreciated!