Development Setup

Overview

This guide will help you set up a complete development environment for building on OPN Chain. Whether you're new to blockchain development or an experienced Ethereum developer, you'll find everything you need to get started.

Prerequisites

Required Software

Software
Version
Purpose

Node.js

16.0+

JavaScript runtime

npm/yarn

Latest

Package management

Git

2.0+

Version control

VS Code

Latest

Recommended IDE

Optional Tools

Tool
Purpose

Docker

Container deployment

Python

For Brownie/Ape frameworks

Rust

For Foundry framework

Step 1: Install Node.js

macOS:

# Using Homebrew
brew install node

# Or download from nodejs.org

Linux:

# Ubuntu/Debian
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs

# Fedora
sudo dnf install nodejs

Windows: Download installer from nodejs.org

Step 2: Install Development Framework

Choose your preferred framework:

Hardhat (Recommended)

# Create new project
mkdir my-opn-project
cd my-opn-project

# Initialize npm project
npm init -y

# Install Hardhat
npm install --save-dev hardhat

# Create Hardhat project
npx hardhat

# Install additional dependencies
npm install --save-dev \
  @nomiclabs/hardhat-waffle \
  @nomiclabs/hardhat-ethers \
  ethereum-waffle \
  chai \
  ethers

Foundry

# Install Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup

# Create new project
forge init my-opn-project
cd my-opn-project

Truffle

# Install Truffle globally
npm install -g truffle

# Create new project
mkdir my-opn-project
cd my-opn-project
truffle init

Step 3: Configure for OPN Chain

Hardhat Configuration

Create hardhat.config.js:

require("@nomiclabs/hardhat-waffle");
require("@nomiclabs/hardhat-ethers");
require("dotenv").config();

// Tasks
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
  const accounts = await hre.ethers.getSigners();
  for (const account of accounts) {
    console.log(account.address);
  }
});

// Configuration
module.exports = {
  solidity: {
    version: "0.8.30",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200
      },
      evmVersion: "pectra"
    }
  },
  networks: {
    // Local development
    hardhat: {
      chainId: 31337
    },
    
    // OPN Testnet
    opnTestnet: {
      url: process.env.OPN_TESTNET_RPC || "https://testnet-rpc.iopn.tech",
      chainId: 984,
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
      gasPrice: 7000000000, // 7 Gwei
      timeout: 60000
    },
    
    // OPN Mainnet (when available)
    opnMainnet: {
      url: process.env.OPN_MAINNET_RPC || "https://rpc.iopn.tech",
      chainId: 985, // Update when mainnet launches
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
      gasPrice: 7000000000,
      timeout: 60000
    }
  },
  
  // Etherscan-style verification (when available)
  etherscan: {
    apiKey: {
      opnTestnet: process.env.OPNSCAN_API_KEY || "placeholder"
    },
    customChains: [
      {
        network: "opnTestnet",
        chainId: 984,
        urls: {
          apiURL: "https://api-testnet.opnscan.io/api",
          browserURL: "https://testnet.opnscan.io"
        }
      }
    ]
  },
  
  // Gas reporter
  gasReporter: {
    enabled: process.env.REPORT_GAS !== undefined,
    currency: "USD",
    gasPrice: 7
  },
  
  // Paths
  paths: {
    sources: "./contracts",
    tests: "./test",
    cache: "./cache",
    artifacts: "./artifacts"
  }
};

Foundry Configuration

Create foundry.toml:

[profile.default]
src = 'src'
out = 'out'
libs = ['lib']
remappings = [
    '@openzeppelin/=lib/openzeppelin-contracts/',
]
solc_version = "0.8.30"
evm_version = "pectra"
optimizer = true
optimizer_runs = 200

[rpc_endpoints]
opn_testnet = "${OPN_TESTNET_RPC}"
opn_mainnet = "${OPN_MAINNET_RPC}"

[etherscan]
opn_testnet = { key = "${OPNSCAN_API_KEY}" }

# OPN specific settings
[profile.opn]
gas_price = 7000000000

Step 4: Environment Setup

Create .env file:

# Network RPC endpoints
OPN_TESTNET_RPC=https://testnet-rpc.iopn.tech
OPN_MAINNET_RPC=https://rpc.iopn.tech

# Private key (NEVER commit this!)
PRIVATE_KEY=your_private_key_here

# API Keys
OPNSCAN_API_KEY=your_api_key_here
INFURA_KEY=your_infura_key_here
ALCHEMY_KEY=your_alchemy_key_here

# Gas settings
GAS_PRICE=7000000000
GAS_LIMIT=3000000

# Other settings
REPORT_GAS=true

Add to .gitignore:

node_modules
.env
coverage
coverage.json
typechain
typechain-types

# Hardhat
cache
artifacts

# Foundry
out
cache_forge

# IDE
.vscode
.idea

# OS
.DS_Store
Thumbs.db

Step 5: Install Development Tools

Essential npm Packages

# Development dependencies
npm install --save-dev \
  @openzeppelin/contracts \
  @openzeppelin/test-helpers \
  @chainlink/contracts \
  hardhat-gas-reporter \
  hardhat-contract-sizer \
  solidity-coverage \
  prettier \
  prettier-plugin-solidity \
  eslint \
  eslint-config-standard \
  eslint-plugin-promise \
  solhint

# Regular dependencies
npm install \
  dotenv \
  @nomiclabs/hardhat-etherscan

VS Code Extensions

Install these extensions for the best development experience:

  • Solidity (Juan Blanco)

  • Hardhat Solidity

  • Prettier - Code formatter

  • ESLint

  • GitLens

  • Better Comments

Create .vscode/settings.json:

{
  "editor.formatOnSave": true,
  "solidity.linter": "solhint",
  "solidity.defaultCompiler": "remote",
  "solidity.compileUsingRemoteVersion": "v0.8.30+commit.e5a0919f",
  "[solidity]": {
    "editor.defaultFormatter": "JuanBlanco.solidity"
  },
  "solidity.remappings": [
    "@openzeppelin/=node_modules/@openzeppelin/"
  ],
  "files.associations": {
    "*.sol": "solidity"
  }
}

Step 6: Create Project Structure

# Create directories
mkdir -p {contracts,scripts,test,deploy}

# Create initial contract
cat > contracts/Greeter.sol << 'EOF'
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract Greeter {
    string private greeting;
    
    event GreetingChanged(string oldGreeting, string newGreeting);
    
    constructor(string memory _greeting) {
        greeting = _greeting;
    }
    
    function greet() public view returns (string memory) {
        return greeting;
    }
    
    function setGreeting(string memory _greeting) public {
        string memory oldGreeting = greeting;
        greeting = _greeting;
        emit GreetingChanged(oldGreeting, _greeting);
    }
}
EOF

# Create test file
cat > test/Greeter.test.js << 'EOF'
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Greeter", function () {
  let greeter;
  let owner;
  let addr1;

  beforeEach(async function () {
    [owner, addr1] = await ethers.getSigners();
    
    const Greeter = await ethers.getContractFactory("Greeter");
    greeter = await Greeter.deploy("Hello, OPN Chain!");
    await greeter.deployed();
  });

  describe("Deployment", function () {
    it("Should set the right greeting", async function () {
      expect(await greeter.greet()).to.equal("Hello, OPN Chain!");
    });
  });

  describe("Transactions", function () {
    it("Should update greeting", async function () {
      await greeter.setGreeting("Hola, OPN Chain!");
      expect(await greeter.greet()).to.equal("Hola, OPN Chain!");
    });

    it("Should emit event on greeting change", async function () {
      await expect(greeter.setGreeting("New Greeting"))
        .to.emit(greeter, "GreetingChanged")
        .withArgs("Hello, OPN Chain!", "New Greeting");
    });
  });
});
EOF

# Create deployment script
cat > scripts/deploy.js << 'EOF'
async function main() {
  const [deployer] = await ethers.getSigners();

  console.log("Deploying contracts with the account:", deployer.address);
  console.log("Account balance:", (await deployer.getBalance()).toString());

  const Greeter = await ethers.getContractFactory("Greeter");
  const greeter = await Greeter.deploy("Hello, OPN Chain!");

  await greeter.deployed();

  console.log("Greeter deployed to:", greeter.address);
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });
EOF

Step 7: Configure Git

# Initialize git repository
git init

# Create .gitignore
cat > .gitignore << 'EOF'
# Dependencies
node_modules
package-lock.json
yarn.lock

# Environment
.env
.env.local
.env.*.local

# Hardhat
cache
artifacts
coverage
coverage.json
typechain
typechain-types

# Foundry
out
cache_forge

# IDE
.vscode
.idea
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Testing
test-results.xml
mochawesome-report

# Build
dist
build
EOF

# Create README
cat > README.md << 'EOF'
# OPN Chain Project

This project demonstrates a basic OPN Chain use case.

## Quick Start

```bash
npm install
npm test
npm run deploy:testnet

Commands

# Compile contracts
npx hardhat compile

# Run tests
npx hardhat test

# Run tests with gas reporting
REPORT_GAS=true npx hardhat test

# Deploy to OPN testnet
npx hardhat run scripts/deploy.js --network opnTestnet

# Verify contract
npx hardhat verify --network opnTestnet DEPLOYED_CONTRACT_ADDRESS "Constructor argument"

Initial commit

git add . git commit -m "Initial OPN Chain project setup"


## Development Workflow

### 1. Write Smart Contracts

```solidity
// contracts/MyToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

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

contract MyToken is ERC20, Ownable {
    constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {
        _mint(msg.sender, initialSupply);
    }
    
    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}

2. Write Tests

// test/MyToken.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("MyToken", function () {
  let token;
  let owner;
  let addr1;
  let addr2;
  
  beforeEach(async function () {
    [owner, addr1, addr2] = await ethers.getSigners();
    
    const Token = await ethers.getContractFactory("MyToken");
    token = await Token.deploy(ethers.utils.parseEther("1000000"));
    await token.deployed();
  });
  
  describe("Deployment", function () {
    it("Should assign total supply to owner", async function () {
      const ownerBalance = await token.balanceOf(owner.address);
      expect(await token.totalSupply()).to.equal(ownerBalance);
    });
  });
  
  describe("Transactions", function () {
    it("Should transfer tokens between accounts", async function () {
      await token.transfer(addr1.address, 50);
      expect(await token.balanceOf(addr1.address)).to.equal(50);
      
      await token.connect(addr1).transfer(addr2.address, 50);
      expect(await token.balanceOf(addr2.address)).to.equal(50);
      expect(await token.balanceOf(addr1.address)).to.equal(0);
    });
  });
});

3. Run Tests

# Run all tests
npx hardhat test

# Run specific test file
npx hardhat test test/MyToken.test.js

# Run with gas reporting
REPORT_GAS=true npx hardhat test

# Run with coverage
npx hardhat coverage

4. Deploy Contracts

// scripts/deploy-token.js
async function main() {
  const [deployer] = await ethers.getSigners();
  
  console.log("Deploying MyToken with account:", deployer.address);
  
  const Token = await ethers.getContractFactory("MyToken");
  const token = await Token.deploy(ethers.utils.parseEther("1000000"));
  
  await token.deployed();
  
  console.log("MyToken deployed to:", token.address);
  console.log("Transaction hash:", token.deployTransaction.hash);
  
  // Wait for confirmations
  await token.deployTransaction.wait(5);
  
  // Verify the deployment
  const totalSupply = await token.totalSupply();
  console.log("Total supply:", ethers.utils.formatEther(totalSupply));
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

Deploy with:

npx hardhat run scripts/deploy-token.js --network opnTestnet

5. Verify Contracts

# Verify on OPNScan (when available)
npx hardhat verify --network opnTestnet \
  DEPLOYED_CONTRACT_ADDRESS \
  "1000000000000000000000000"

Advanced Setup

Multi-Signature Wallet Setup

For production deployments, use a multi-sig:

// scripts/setup-multisig.js
const { ethers } = require("hardhat");

async function main() {
  const signers = [
    "0x...", // Owner 1
    "0x...", // Owner 2
    "0x...", // Owner 3
  ];
  
  const MultiSigWallet = await ethers.getContractFactory("MultiSigWallet");
  const multiSig = await MultiSigWallet.deploy(signers, 2); // 2 of 3
  
  await multiSig.deployed();
  console.log("MultiSig deployed to:", multiSig.address);
}

CI/CD Pipeline

Create .github/workflows/test.yml:

name: Test

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Compile contracts
      run: npx hardhat compile
    
    - name: Run tests
      run: npm test
    
    - name: Run coverage
      run: npx hardhat coverage
    
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3

Security Setup

Install security tools:

# Install Slither
pip3 install slither-analyzer

# Install MythX CLI
npm install -g @mythx/cli

# Install Echidna (fuzzer)
# Follow instructions at: https://github.com/crytic/echidna

Create security check script:

#!/bin/bash
# scripts/security-check.sh

echo "Running Slither..."
slither . --json slither-report.json

echo "Running Solhint..."
npx solhint 'contracts/**/*.sol'

echo "Checking contract sizes..."
npx hardhat size-contracts

echo "Security check complete!"

Troubleshooting

Common Issues

1. "Cannot find module" errors

# Clear cache and reinstall
rm -rf node_modules package-lock.json
npm install

2. Compilation errors

# Clear Hardhat cache
npx hardhat clean
npx hardhat compile

3. Network connection issues

// Add timeout and retry logic
module.exports = {
  networks: {
    opnTestnet: {
      url: "https://testnet-rpc.iopn.tech",
      timeout: 60000, // 60 seconds
      httpHeaders: {
        "User-Agent": "Hardhat"
      }
    }
  }
};

4. Gas estimation errors

// Manually set gas limits
const tx = await contract.method({
  gasLimit: 300000,
  gasPrice: ethers.utils.parseUnits('7', 'gwei')
});

Getting Help

  1. Join our Discord


Last updated