How To Deploy And Verify Contracts On Berachain

Ultimate Guide to Deploying an ERC-721 NFT and Verifying Contracts on Berachain Artio

De The Bera
berachain-devs
10 min readApr 23, 2024

--

Main Banner

Introduction to Berachain Artio Testnet

In the burgeoning world of 100+ blockchain technology, Berachain emerges as a goto cosmos based evm compatible chain for developers to build on. Known for its POL design, robust scalability, and enhanced security, Berachain stands out as a haven for developers as its lucrative for users to interact with the chain. Its early adoption of wide array of decentralized applications (dApps) and evm compatibility makes it an ideal choice for developers to build and test their products on.

Like any other blockchains Artio testnet is a key component for upcoming Berachain Mainnet, providing a testing ground for developers to prepare, test their tools well in advance. It’s here that bear magic of smart contracts, especially the ERC-721 standard for Non-Fungible Tokens (NFTs), comes to life. 🐻 ❤️

🤔 Why Hardhat?

Hardhat is a pivotal tool in Ethereum development. It streamlines various processes such as compiling, deploying, testing, and debugging smart contracts. Think of it as your personal blockchain workshop, equipped with all the tools you need for dApp development.

🛠️ What are we Building?

In this tutorial, we will guide you through creating and deploying an ERC-721 smart contract on the Berachain Testnet (Artio) using hardhat-ethers. This tutorial focuses on writing, deploying and also verifying the non-fungible (ERC-721) token smart contract using Hardhat, ethers.js onto Berachain Testnet!

Snippet of Successful Contract Deployment Using Hardhat
Snippet of Successful Verification of Deployed Contract Using Hardhat

We will majorly be covering the initial setup, creation, and deployment of the contract!

⚠️ Prerequisites

Before embarking on this technical tutorial about, it’s imperative to equip yourself with the following:

  • A solid understanding of blockchain fundamentals and smart contract principles.
  • Proficiency in Solidity, the primary language for Ethereum-based contract development.
  • The latest versions of Node.js and npm installed on your machine, verifiable through the command line with node -v.

Step 1: Initiating a Node.js Project

Begin by creating a new Node.js project. This foundational step involves setting up a dedicated project directory and initializing it with Node’s package management tools.

mkdir bera-hardhatTest && cd bera-hardhatTest
npm init -y
# Wrote to /Users/dethebera/BeraWork/hardhat-verification-bera/package.json:

# {
# "name": "hardhat-verification-bera",
# "version": "1.0.0",
# "description": "",
# "main": "index.js",
# "scripts": {
# "test": "echo \"Error: no test specified\" && exit 1"
# },
# "keywords": [],
# "author": "",
# "license": "ISC"
# }

This command sequence crafts a new directory and then triggers the Node.js project initialization with default parameters.

Step 2: Setting Up Hardhat

Install Hardhat in your project directory and initiate a new project environment:

npm install --save-dev hardhat

# added 263 packages, and audited 264 packages in 31s

# 52 packages are looking for funding
# run `npm fund` for details

# found 0 vulnerabilities

Select TypeScript as your project language and follow through the setup prompts, ensuring the inclusion of a .gitignore file and necessary dependencies.

Note: DEPRECATION WARNING

Initializing a project with npx hardhat is deprecated and will be removed in the future. Please use npx hardhat init instead.

npx hardhat init
# 888 888 888 888 888
# 888 888 888 888 888
# 888 888 888 888 888
# 8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
# 888 888 "88b 888P" d88" 888 888 "88b "88b 888
# 888 888 .d888888 888 888 888 888 888 .d888888 888
# 888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
# 888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888

# 👷 Welcome to Hardhat v2.21.0 👷‍

# ✔ What do you want to do? · Create a TypeScript project
# ✔ Hardhat project root: · /Users/dethebera/BeraWork/hardhat-verification-bera
# ✔ Do you want to add a .gitignore? (Y/n) · y
# ✔ Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) · y

# npm install --save-dev "@nomicfoundation/hardhat-toolbox@^4.0.0"

# added 273 packages, and audited 537 packages in 40s

# 90 packages are looking for funding
# run `npm fund` for details

# found 0 vulnerabilities

# ✨ Project created ✨

# See the README.md file for some example tasks you can run

# Give Hardhat a star on Github if you're enjoying it! ⭐️✨

# https://github.com/NomicFoundation/hardhat

After setting up Hardhat, verify its proper functioning with:

npx hardhat test

#Downloading compiler 0.8.24
# Compiled 1 Solidity file successfully (evm target: paris).


# Lock
# Deployment
# ✔ Should set the right unlockTime (691ms)
# ✔ Should set the right owner
# ✔ Should receive and store the funds to lock
# ✔ Should fail if the unlockTime is not in the future
# Withdrawals
# Validations
# ✔ Should revert with the right error if called too soon
# ✔ Should revert with the right error if called from another account
# ✔ Shouldn't fail if the unlockTime has arrived and the owner calls it
# Events
# ✔ Should emit an event on withdrawals
# Transfers
# ✔ Should transfer the funds to the owner


# 9 passing (717ms)

Step 3: Diving into NFT Smart Contract Development

Now, delve into the heart of your project — the NFT smart contract. Navigate to the contracts folder and begin scripting your ERC-721 contract. This phase is crucial as it involves the actual code that defines your NFT's characteristics and behavior.

Head over to contracts folder and create a new file NFT.sol

Here’s a basic template using OpenZeppelin’s secure and reputable libraries:

File: ./contracts/NFT.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract NFT is ERC721 {
uint256 public currentTokenId;

constructor() ERC721("NFT Name", "NFT") {}

function mint(address recipient) public payable returns (uint256) {
uint256 newItemId = ++currentTokenId;
_safeMint(recipient, newItemId);
return newItemId;
}
}

OpenZeppelin’s contracts are a treasure trove of pre-written, secure code snippets. They significantly reduce the development time and enhance the security of your smart contracts.

npm install @openzeppelin/contracts

# added 1 package, and audited 539 packages in 2s

# 91 packages are looking for funding
# run `npm fund` for details

# found 0 vulnerabilities

In your NFT.sol file, ensure that the Solidity version is in sync with the one specified in hardhat.config.ts. Explain the purpose of each component, highlighting the role of OpenZeppelin's contracts in your NFT's architecture.

Step 5: Integrating MetaMask & Berachain Artio with Hardhat

This step involves linking your MetaMask wallet and the Berachain Testnet to the Hardhat environment. This connection is pivotal for deploying your smart contract on the Berachain Artio.

  1. Installing dotenv: A tool for managing environment variables.
npm install dotenv --save

# added 1 package, and audited 538 packages in 612ms

# 91 packages are looking for funding
# run `npm fund` for details

# found 0 vulnerabilities

2. Configuring the .env File: Safely store your MetaMask private key in a newly created .env file in your project's root directory.

Create a .env file in your project root directory and add your MetaMask private key. For help exporting your key from MetaMask, see this guide.

Your .env file should look like this:

PRIV_KEY = 0x"your exported private key"

Step 6: Configuring hardhat.config.js

This step is about fine-tuning your Hardhat setup to align with Berachain’s specifications. Update your hardhat.config.ts file with necessary dependencies and network configurations, paying special attention to the custom chains settings for Berachain Artio.

File: ./hardhat.config.ts

import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import "@nomicfoundation/hardhat-verify";

require("dotenv").config();

const config: HardhatUserConfig = {
solidity: {
version: "0.8.23",
},
networks: {
// for testnet
"berachain-artio": {
url: "https://rpc.ankr.com/berachain_testnet",
accounts: [process.env.WALLET_KEY as string],
chainId: 80085,
// gas: "auto",
gasPrice: 10000000000,
},
// for local dev environment
"berachain-local": {
url: "http://localhost:8545",
gasPrice: 1000000000,
},
},
defaultNetwork: "hardhat",
etherscan: {
apiKey: {
berachainArtio: "berachainArtio", // apiKey is not required, just set a placeholder
},
customChains: [
{
network: "berachainArtio",
chainId: 80085,
urls: {
apiURL:
"https://api.routescan.io/v2/network/testnet/evm/80085/etherscan",
browserURL: "https://artio.beratrail.io",
},
},
],
},
};

export default config;

Step 7: Creating the Deployment Script

In your scripts/ directory, draft the deployment script (update deploy.ts). This script is the blueprint for deploying your smart contract on the blockchain. Explain each line of code and its role in leveraging Hardhat's deployment features.

File: ./scripts/deploy.ts

import { setBlockGasLimit } from '@nomicfoundation/hardhat-toolbox/network-helpers';
import { ethers } from 'hardhat';

async function main() {
const nft = await ethers.deployContract('NFT');

await nft.waitForDeployment();

console.log('NFT Contract Deployed at ' + nft.target);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

Step 8: Actual Deployment

Execute the deployment of your contract to the Berachain Artio:

npx hardhat run scripts/deploy.ts --network berachain-artio
# NFT Contract Deployed at 0xBC862C68CE9EE3aFa762DbF7A51712E3c6E79215

Guide the reader through interpreting the deployment output and confirm the successful launch of the contract.

Understanding the overall changes performed to Hardhat

This section can be skipped if you already understood the changes made to the above deployment script and the hardhat configuration to work with Berachain Artio.

  1. The default Hardhat deployContract does have some issues. But crafting the deploy transaction manually with gasLimit and gasPrice specified works with Artio.
  2. Removing the gas: “auto” setting in hardhat config file is must. Having it set to Auto increases the time to deploy and causes delay for the transaction to get submitted.
  3. Building the transaction manually and setting a forced gas limit override (this may be more expensive gas-wise, but not significantly).

Verifying Contracts on Berachain

Verification is a crucial post-deployment step. It involves publicly associating the deployed bytecode with its corresponding source code. This process enhances transparency and trust in the blockchain community.

Install the @nomicfoundation/hardhat-verify plugin and guide the users through the verification process, emphasizing the criticality of using the correct contract address.

Installation

Install hardhat-verify plugin that will help verify contracts directly via hardhat.

npm install --save-dev @nomicfoundation/hardhat-verify

# up to date, audited 539 packages in 3s

# 91 packages are looking for funding
# run `npm fund` for details

# found 0 vulnerabilities

We’ve already included the following code for Routescan api which helps in verifying contracts.

For more info head over to https://artio.beratrail.io/documentation/recipes/hardhat-verification.

Verify

Now to verify the contract all you need is the contract address of the contract deployed.

npx hardhat verify --network berachain-artio <0x...>

Make sure to relace the whole “<0xaddress> “. Correct example would be :-

✅ npx hardhat verify — network berachain-artio 0x3229075dd6F75bD879F7af07d384A0856c30a806

❌ npx hardhat verify — network berachain-artio <0x3229075dd6F75bD879F7af07d384A0856c30a806> (This is incorrect)

Once done should give you an output similar to below.


# [INFO] Sourcify Verification Skipped: Sourcify verification is currently disabled. To enable it, add the following entry to your Hardhat configuration:

# sourcify: {
# enabled: true
# }

# Or set 'enabled' to false to hide this message.

# For more information, visit https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#verifying-on-sourcify
# The contract 0xBC862C68CE9EE3aFa762DbF7A51712E3c6E79215 has already been verified on Etherscan.
# https://artio.beratrail.io/address/0xBC862C68CE9EE3aFa762DbF7A51712E3c6E79215#code

If we head over to Berachain Explorer(Beratrail) using the link provided in terminal- we will be able to see the contract’s source code has been verified successfully.!

Complete Code

File: ./contracts/NFT.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract NFT is ERC721 {
uint256 public currentTokenId;
constructor() ERC721("NFT Name", "NFT") {}
function mint(address recipient) public payable returns (uint256) {
uint256 newItemId = ++currentTokenId;
_safeMint(recipient, newItemId);
return newItemId;
}
}

File: ./scripts/deploy.ts

import { setBlockGasLimit } from '@nomicfoundation/hardhat-toolbox/network-helpers';
import { ethers } from 'hardhat';
async function main() {
const nft = await ethers.deployContract('NFT');
await nft.waitForDeployment();
console.log('NFT Contract Deployed at ' + nft.target);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

File: ./hardhat.config.ts

import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import "@nomicfoundation/hardhat-verify";
require("dotenv").config();
const config: HardhatUserConfig = {
solidity: {
version: "0.8.23",
},
networks: {
// for testnet
"berachain-artio": {
url: "https://rpc.ankr.com/berachain_testnet",
accounts: [process.env.WALLET_KEY as string],
chainId: 80085,
// gas: "auto",
gasPrice: 10000000000,
},
// for local dev environment
"berachain-local": {
url: "http://localhost:8545",
gasPrice: 1000000000,
},
},
defaultNetwork: "hardhat",
etherscan: {
apiKey: {
berachainArtio: "berachainArtio", // apiKey is not required, just set a placeholder
},
customChains: [
{
network: "berachainArtio",
chainId: 80085,
urls: {
apiURL:
"https://api.routescan.io/v2/network/testnet/evm/80085/etherscan",
browserURL: "https://artio.beratrail.io",
},
},
],
},
};
export default config;

You should also be able to get the complete code on our Official Github repository using the link below.

Recap

This guide aims to be a comprehensive resource, guiding readers through every step of deploying an ERC-721 NFT and mainly, verifying contracts on Berachain Artio. We’ve covered the basics of deploying using Hardhat to the a few intricacies and customization needed for smart contract development and verification.

Want To Build and How To Get Support ?

Learning Resources: Explore additional guides currently available via our docs at https://docs.berachain.com/developers.

How To Get Support ?

Discord Community: Join the Third Web Discord community for further assistance, discussions, and to stay updated with the latest developments in Web3.

⚠️ Still facing issues or have doubts? Head over to discord and raise a ticket as shown in the gif below!

Our DevRel team will be happy to assist! 🤝

Byyeeee bears! 🐻

--

--