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
Node.js
16.0+
JavaScript runtime
npm/yarn
Latest
Package management
Git
2.0+
Version control
VS Code
Latest
Recommended IDE
Optional Tools
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
Join our Discord
Last updated