If you’ve shipped a contract to Ethereum, Polygon, or Arbitrum, you already know 90% of what you need to deploy on Novus. This post walks you through the rest.

By the end, you’ll have a live ERC-20 token contract running on Novus testnet, deployed from your local machine in under an hour.

Who this is for

You write Solidity. You’ve used Hardhat or Foundry before. You’re curious about deploying to a sovereign Layer 1 that runs on Cosmos SDK but speaks fluent EVM. The goal here isn’t to teach Solidity, it’s to show you exactly where Novus behaves the same as the chains you already know, and the few places it doesn’t.

What you’ll build

A standard ERC-20 token. Nothing exotic. The point is to see how Novus behaves at every step so the next contract you ship is one you actually care about.

Prerequisites

Before starting:

  • Node.js 18 or later
  • A wallet (MetaMask works) with testnet FWRD from the Novus faucet
  • Hardhat installed in your project
  • A text editor

If you’ve shipped to any EVM chain, you already have all of this.

Step 1: Configure Hardhat for Novus

Create a fresh project if you don’t have one:

bash

mkdir my-novus-token && cd my-novus-token
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox dotenv
npx hardhat init

Pick “Create a JavaScript project” when prompted.

Now open hardhat.config.js and add Novus testnet as a network:

javascript

require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();

module.exports = {
  solidity: "0.8.20",
  networks: {
    novusTestnet: {
      url: "https://rpc-testnet.novusnetworks.io",
      chainId: 9001,
      accounts: [process.env.PRIVATE_KEY]
    }
  }
};

A few things worth calling out:

  • The chainId matches Novus testnet exactly. Hardhat uses this to confirm you’re talking to the right chain before it broadcasts anything.
  • The RPC URL is the public testnet endpoint. For mainnet I’d swap this for the mainnet RPC, but I always recommend doing the first deploy on testnet.
  • Never commit your private key. The .env pattern keeps it out of git, and you’ll want a .gitignore entry for .env as well.

Create a .env file in your project root:

PRIVATE_KEY=your_wallet_private_key_here

Step 2: Write the contract

In the contracts/ folder, create NovusToken.sol:

solidity

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

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

contract NovusToken is ERC20 {
    constructor(uint256 initialSupply) ERC20("Novus Sample Token", "NST") {
        _mint(msg.sender, initialSupply);
    }
}

Install OpenZeppelin if you haven’t:

bash

npm install @openzeppelin/contracts

This is the simplest legitimate ERC-20 you can write. It mints the initial supply to whoever deploys it. In a real project you’d want access control, mint/burn rules, and probably a permit function, but for a first deploy this is enough.

Step 3: Write the deploy script

In the scripts/ folder, create deploy.js:

javascript

const hre = require("hardhat");

async function main() {
  const initialSupply = hre.ethers.parseUnits("1000000", 18);

  const NovusToken = await hre.ethers.getContractFactory("NovusToken");
  const token = await NovusToken.deploy(initialSupply);

  await token.waitForDeployment();

  console.log("NovusToken deployed to:", await token.getAddress());
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

This mints one million tokens (with 18 decimals to match the EVM convention) to the deployer wallet.

Step 4: Deploy

Compile first to catch any errors:

bash

npx hardhat compile

Then deploy to Novus testnet:

bash

npx hardhat run scripts/deploy.js --network novusTestnet

If your wallet has testnet FWRD and your config is right, you’ll see something like:

NovusToken deployed to: 0x742d35Cc6634C0532925a3b844Bc9e7595f1F1b3

Save that address. You’ll need it.

Step 5: Verify the deployment

The fastest way to confirm your contract is live is to check it on the testnet explorer:

https://testnet-explorer.novusnetworks.io/address/YOUR_CONTRACT_ADDRESS

You should see the contract code, the deployment transaction, and the initial supply minted to your wallet. If you imported your wallet into MetaMask and added the Novus testnet network, you’ll also see the tokens show up in your balance once you import the token using the contract address.

Step 6: Interact with your contract

Let’s confirm the contract actually works. Create scripts/interact.js:

javascript

const hre = require("hardhat");

async function main() {
  const contractAddress = "YOUR_CONTRACT_ADDRESS_HERE";

  const NovusToken = await hre.ethers.getContractFactory("NovusToken");
  const token = NovusToken.attach(contractAddress);

  const [signer] = await hre.ethers.getSigners();
  const balance = await token.balanceOf(signer.address);
  const symbol = await token.symbol();
  const totalSupply = await token.totalSupply();

  console.log(`Symbol: ${symbol}`);
  console.log(`Your balance: ${hre.ethers.formatUnits(balance, 18)} ${symbol}`);
  console.log(`Total supply: ${hre.ethers.formatUnits(totalSupply, 18)} ${symbol}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Run it:

bash

npx hardhat run scripts/interact.js --network novusTestnet

You should see your token balance match the initial supply. That’s the round trip: contract deployed, state readable, ready to build on.

What’s different about Novus

Now that the contract is live, I want to flag the things that genuinely differ from a typical Ethereum L2 deployment, because they’ll matter for your next project:

Gas is paid in FWRD, not ETH. Same EVM, different fee token. Your wallet handles this transparently once the network is configured, but if you’re scripting fee estimates or building a gasless transaction layer, this is the line you’ll want to adjust.

Block times are faster than Ethereum mainnet. Expect confirmations in roughly 2 to 3 seconds rather than 12. If you have any code that polls for receipts with long timeouts, you can tighten those.

The chain runs on Cosmos SDK underneath. You don’t see this from the EVM layer (your contract has no idea it’s not on Ethereum), but it means Novus also exposes a Cosmos REST and gRPC API alongside the JSON-RPC. If you ever need to query validator sets, governance proposals, or staking positions directly, those Cosmos endpoints are available without contract calls.

Application-specific L2s are a first-class option. If your dApp grows to the point where you want your own execution environment (custom fee tokens, isolated state, tailored gas economics), Novus supports launching a purpose-built L2 underneath the main chain. That’s a much bigger topic for another post, but I want you to know the option exists.

Where to go next

You’ve shipped your first contract. The fastest ways to keep learning:

  1. Add access control to the token using OpenZeppelin’s Ownable. Try restricting mint to the owner.
  2. Write a Hardhat test for the contract. Local Hardhat network behaves identically to Novus for unit tests.
  3. Build a tiny frontend that connects with MetaMask and reads the balance. Any standard ethers.js or wagmi setup works without changes.

If you hit anything that doesn’t behave like a vanilla EVM chain, that’s a great topic for the next post. Send it to the community channel and I’ll write it up.

Happy building.

Leave a Reply