# Deploy Your First Contract

### Overview

This tutorial will guide you through deploying your first smart contract on OPN Chain. We'll create a simple storage contract, deploy it, and interact with it using various tools.

**What you'll learn:**

* Writing a basic smart contract
* Compiling with Solidity
* Deploying to OPN testnet
* Interacting with your contract
* Verifying your contract

**Time required:** 20 minutes

### Prerequisites

Before starting, ensure you have:

* \[x] MetaMask installed and connected to OPN testnet
* \[x] Test OPN tokens from the faucet
* \[x] Node.js 16+ installed
* \[x] Basic command line knowledge

### Step 1: Set Up Your Project

#### Create Project Directory

```bash
mkdir my-first-contract
cd my-first-contract
npm init -y
```

#### Install Dependencies

```bash
npm install --save-dev hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers
npm install dotenv
```

#### Initialize Hardhat

```bash
npx hardhat
```

Select:

* "Create a JavaScript project"
* Press Enter for all prompts
* Type 'y' to install sample project dependencies

#### Configure Hardhat for OPN

Create/update `hardhat.config.js`:

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

module.exports = {
  solidity: "0.8.30",
  networks: {
    opnTestnet: {
      url: "https://testnet-rpc.iopn.tech",
      chainId: 984,
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    }
  }
};
```

#### Set Up Environment Variables

Create `.env` file:

```bash
PRIVATE_KEY=your_wallet_private_key_here
```

⚠️ **Security Note:** Never commit your `.env` file! Add it to `.gitignore`:

```bash
echo ".env" >> .gitignore
```

### Step 2: Write Your Smart Contract

#### Create the Contract

Delete the sample contracts and create `contracts/SimpleStorage.sol`:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

/**
 * @title SimpleStorage
 * @dev Store & retrieve value in a variable
 * @custom:dev-run-script ./scripts/deploy.js
 */
contract SimpleStorage {
    uint256 private storedValue;
    
    // Event emitted when value changes
    event ValueChanged(uint256 oldValue, uint256 newValue, address indexed changer);
    
    // Constructor to set initial value
    constructor(uint256 _initialValue) {
        storedValue = _initialValue;
        emit ValueChanged(0, _initialValue, msg.sender);
    }
    
    /**
     * @dev Store a new value
     * @param _value value to store
     */
    function set(uint256 _value) public {
        uint256 oldValue = storedValue;
        storedValue = _value;
        emit ValueChanged(oldValue, _value, msg.sender);
    }
    
    /**
     * @dev Return the stored value
     * @return value of 'storedValue'
     */
    function get() public view returns (uint256) {
        return storedValue;
    }
    
    /**
     * @dev Increment stored value by one
     */
    function increment() public {
        uint256 oldValue = storedValue;
        storedValue = storedValue + 1;
        emit ValueChanged(oldValue, storedValue, msg.sender);
    }
    
    /**
     * @dev Check if value equals a number
     * @param _value value to compare
     * @return true if values match
     */
    function equals(uint256 _value) public view returns (bool) {
        return storedValue == _value;
    }
}
```

#### Understanding the Contract

This contract:

* Stores a single number (`storedValue`)
* Allows anyone to update the value
* Emits events when the value changes
* Provides functions to read and modify the value

### Step 3: Compile the Contract

#### Compile

```bash
npx hardhat compile
```

Expected output:

```
Compiled 1 Solidity file successfully
```

The compiled artifacts are saved in `artifacts/` directory.

#### Check Compilation

```bash
ls artifacts/contracts/SimpleStorage.sol/
```

You should see `SimpleStorage.json` containing the ABI and bytecode.

### Step 4: Write Tests

#### Create Test File

Create `test/SimpleStorage.test.js`:

```javascript
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("SimpleStorage", function () {
  let simpleStorage;
  let owner;
  let addr1;

  beforeEach(async function () {
    // Get signers
    [owner, addr1] = await ethers.getSigners();
    
    // Deploy contract
    const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
    simpleStorage = await SimpleStorage.deploy(42);
    await simpleStorage.deployed();
  });

  describe("Deployment", function () {
    it("Should set the initial value", async function () {
      expect(await simpleStorage.get()).to.equal(42);
    });

    it("Should emit ValueChanged event on deployment", async function () {
      const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
      const contract = await SimpleStorage.deploy(100);
      
      await expect(contract.deployTransaction)
        .to.emit(contract, "ValueChanged")
        .withArgs(0, 100, owner.address);
    });
  });

  describe("Set function", function () {
    it("Should update stored value", async function () {
      await simpleStorage.set(123);
      expect(await simpleStorage.get()).to.equal(123);
    });

    it("Should emit ValueChanged event", async function () {
      await expect(simpleStorage.set(456))
        .to.emit(simpleStorage, "ValueChanged")
        .withArgs(42, 456, owner.address);
    });

    it("Should allow any address to set value", async function () {
      await simpleStorage.connect(addr1).set(789);
      expect(await simpleStorage.get()).to.equal(789);
    });
  });

  describe("Increment function", function () {
    it("Should increment value by 1", async function () {
      await simpleStorage.increment();
      expect(await simpleStorage.get()).to.equal(43);
    });

    it("Should handle multiple increments", async function () {
      await simpleStorage.increment();
      await simpleStorage.increment();
      await simpleStorage.increment();
      expect(await simpleStorage.get()).to.equal(45);
    });
  });

  describe("Equals function", function () {
    it("Should return true for matching value", async function () {
      expect(await simpleStorage.equals(42)).to.equal(true);
    });

    it("Should return false for non-matching value", async function () {
      expect(await simpleStorage.equals(99)).to.equal(false);
    });
  });

  describe("Gas usage", function () {
    it("Should use reasonable gas for operations", async function () {
      const setTx = await simpleStorage.set(999);
      const setReceipt = await setTx.wait();
      console.log("Set gas used:", setReceipt.gasUsed.toString());
      
      const incrementTx = await simpleStorage.increment();
      const incrementReceipt = await incrementTx.wait();
      console.log("Increment gas used:", incrementReceipt.gasUsed.toString());
    });
  });
});
```

#### Run Tests

```bash
npx hardhat test
```

Expected output:

```
  SimpleStorage
    Deployment
      ✓ Should set the initial value
      ✓ Should emit ValueChanged event on deployment
    Set function
      ✓ Should update stored value
      ✓ Should emit ValueChanged event
      ✓ Should allow any address to set value
    Increment function
      ✓ Should increment value by 1
      ✓ Should handle multiple increments
    Equals function
      ✓ Should return true for matching value
      ✓ Should return false for non-matching value
    Gas usage
Set gas used: 43954
Increment gas used: 41842
      ✓ Should use reasonable gas for operations

  10 passing (2s)
```

### Step 5: Deploy to OPN Testnet

#### Create Deployment Script

Create `scripts/deploy.js`:

```javascript
const hre = require("hardhat");

async function main() {
  console.log("Deploying to OPN Testnet...\n");

  // Get deployer account
  const [deployer] = await ethers.getSigners();
  console.log("Deploying contracts with account:", deployer.address);
  
  // Check balance
  const balance = await deployer.getBalance();
  console.log("Account balance:", ethers.utils.formatEther(balance), "OPN\n");

  // Deploy contract
  const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
  console.log("Deploying SimpleStorage...");
  
  const simpleStorage = await SimpleStorage.deploy(42);
  await simpleStorage.deployed();
  
  console.log("SimpleStorage deployed to:", simpleStorage.address);
  console.log("Transaction hash:", simpleStorage.deployTransaction.hash);
  
  // Wait for confirmations
  console.log("\nWaiting for confirmations...");
  await simpleStorage.deployTransaction.wait(5);
  console.log("Confirmed!\n");
  
  // Verify deployment
  const value = await simpleStorage.get();
  console.log("Initial value:", value.toString());
  
  // Save deployment info
  const fs = require("fs");
  const deploymentInfo = {
    network: "OPN Testnet",
    chainId: 984,
    contract: "SimpleStorage",
    address: simpleStorage.address,
    deployer: deployer.address,
    deploymentTx: simpleStorage.deployTransaction.hash,
    timestamp: new Date().toISOString(),
    initialValue: value.toString(),
    abi: SimpleStorage.interface.format('json')
  };
  
  fs.writeFileSync(
    "deployment-info.json",
    JSON.stringify(deploymentInfo, null, 2)
  );
  console.log("\nDeployment info saved to deployment-info.json");
}

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

#### Deploy

```bash
npx hardhat run scripts/deploy.js --network opnTestnet
```

Expected output:

```
Deploying to OPN Testnet...

Deploying contracts with account: 0xYourAddress
Account balance: 9.876543210 OPN

Deploying SimpleStorage...
SimpleStorage deployed to: 0xContractAddress
Transaction hash: 0xTransactionHash

Waiting for confirmations...
Confirmed!

Initial value: 42

Deployment info saved to deployment-info.json
```

🎉 **Congratulations!** You've deployed your first smart contract on OPN Chain!

### Step 6: Interact with Your Contract

#### Create Interaction Script

Create `scripts/interact.js`:

```javascript
const hre = require("hardhat");
const deploymentInfo = require("../deployment-info.json");

async function main() {
  console.log("Interacting with SimpleStorage on OPN Testnet...\n");

  // Get signer
  const [signer] = await ethers.getSigners();
  
  // Connect to deployed contract
  const simpleStorage = await ethers.getContractAt(
    "SimpleStorage",
    deploymentInfo.address,
    signer
  );
  
  // Read current value
  console.log("Current value:", (await simpleStorage.get()).toString());
  
  // Set new value
  console.log("\nSetting value to 123...");
  const setTx = await simpleStorage.set(123);
  console.log("Transaction hash:", setTx.hash);
  
  // Wait for confirmation
  const receipt = await setTx.wait();
  console.log("Confirmed in block:", receipt.blockNumber);
  console.log("Gas used:", receipt.gasUsed.toString());
  
  // Read updated value
  console.log("\nNew value:", (await simpleStorage.get()).toString());
  
  // Increment value
  console.log("\nIncrementing value...");
  const incrementTx = await simpleStorage.increment();
  await incrementTx.wait();
  
  console.log("After increment:", (await simpleStorage.get()).toString());
  
  // Check equals
  const equals124 = await simpleStorage.equals(124);
  console.log("\nEquals 124?", equals124);
  
  // Listen for events
  console.log("\n Listening for ValueChanged events...");
  simpleStorage.on("ValueChanged", (oldValue, newValue, changer) => {
    console.log(`Value changed from ${oldValue} to ${newValue} by ${changer}`);
  });
  
  // Trigger an event
  await simpleStorage.set(200);
  
  // Wait a bit for event
  await new Promise(resolve => setTimeout(resolve, 5000));
  
  // Remove listener
  simpleStorage.removeAllListeners();
}

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

#### Run Interaction

```bash
npx hardhat run scripts/interact.js --network opnTestnet
```

### Step 7: Interact via Web Interface

#### Create Simple Web Interface

Create `interface.html`:

```html
<!DOCTYPE html>
<html>
<head>
    <title>SimpleStorage DApp</title>
    <script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 600px;
            margin: 50px auto;
            padding: 20px;
        }
        .container {
            border: 1px solid #ddd;
            padding: 20px;
            border-radius: 8px;
        }
        button {
            background: #4CAF50;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin: 5px;
        }
        button:hover {
            background: #45a049;
        }
        input {
            padding: 8px;
            margin: 5px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        #status {
            margin-top: 20px;
            padding: 10px;
            background: #f0f0f0;
            border-radius: 4px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>SimpleStorage on OPN Chain</h1>
        
        <div>
            <h3>Contract Address</h3>
            <p id="contractAddress">Not connected</p>
        </div>
        
        <div>
            <h3>Current Value</h3>
            <p id="currentValue">?</p>
            <button onclick="getValue()">Refresh Value</button>
        </div>
        
        <div>
            <h3>Set New Value</h3>
            <input type="number" id="newValue" placeholder="Enter new value">
            <button onclick="setValue()">Set Value</button>
        </div>
        
        <div>
            <h3>Increment</h3>
            <button onclick="incrementValue()">Increment by 1</button>
        </div>
        
        <div id="status"></div>
    </div>

    <script>
        // Replace with your contract address
        const CONTRACT_ADDRESS = 'YOUR_CONTRACT_ADDRESS_HERE';
        
        // Contract ABI (paste from deployment-info.json)
        const CONTRACT_ABI = [
            {
                "inputs": [{"internalType": "uint256", "name": "_initialValue", "type": "uint256"}],
                "stateMutability": "nonpayable",
                "type": "constructor"
            },
            {
                "anonymous": false,
                "inputs": [
                    {"indexed": false, "internalType": "uint256", "name": "oldValue", "type": "uint256"},
                    {"indexed": false, "internalType": "uint256", "name": "newValue", "type": "uint256"},
                    {"indexed": true, "internalType": "address", "name": "changer", "type": "address"}
                ],
                "name": "ValueChanged",
                "type": "event"
            },
            {
                "inputs": [{"internalType": "uint256", "name": "_value", "type": "uint256"}],
                "name": "equals",
                "outputs": [{"internalType": "bool", "name": "", "type": "bool"}],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [],
                "name": "get",
                "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [],
                "name": "increment",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "inputs": [{"internalType": "uint256", "name": "_value", "type": "uint256"}],
                "name": "set",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            }
        ];
        
        let web3;
        let contract;
        let account;
        
        async function init() {
            if (typeof window.ethereum !== 'undefined') {
                web3 = new Web3(window.ethereum);
                
                try {
                    // Request accounts
                    const accounts = await window.ethereum.request({ 
                        method: 'eth_requestAccounts' 
                    });
                    account = accounts[0];
                    
                    // Check network
                    const chainId = await web3.eth.getChainId();
                    if (chainId !== 984) {
                        alert('Please switch to OPN Testnet in MetaMask!');
                        return;
                    }
                    
                    // Initialize contract
                    contract = new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS);
                    document.getElementById('contractAddress').textContent = CONTRACT_ADDRESS;
                    
                    // Get initial value
                    await getValue();
                    
                    // Listen for events
                    contract.events.ValueChanged()
                        .on('data', (event) => {
                            console.log('Value changed:', event.returnValues);
                            getValue();
                            updateStatus(`Value changed from ${event.returnValues.oldValue} to ${event.returnValues.newValue}`);
                        });
                    
                } catch (error) {
                    console.error('Error:', error);
                    alert('Error connecting to MetaMask!');
                }
            } else {
                alert('Please install MetaMask!');
            }
        }
        
        async function getValue() {
            try {
                const value = await contract.methods.get().call();
                document.getElementById('currentValue').textContent = value;
            } catch (error) {
                console.error('Error getting value:', error);
                updateStatus('Error getting value: ' + error.message);
            }
        }
        
        async function setValue() {
            const newValue = document.getElementById('newValue').value;
            if (!newValue) {
                alert('Please enter a value!');
                return;
            }
            
            try {
                updateStatus('Sending transaction...');
                
                const tx = await contract.methods.set(newValue).send({
                    from: account,
                    gas: 100000,
                    gasPrice: web3.utils.toWei('7', 'gwei')
                });
                
                updateStatus(`Transaction successful! Hash: ${tx.transactionHash}`);
                document.getElementById('newValue').value = '';
                
            } catch (error) {
                console.error('Error setting value:', error);
                updateStatus('Error: ' + error.message);
            }
        }
        
        async function incrementValue() {
            try {
                updateStatus('Incrementing value...');
                
                const tx = await contract.methods.increment().send({
                    from: account,
                    gas: 100000,
                    gasPrice: web3.utils.toWei('7', 'gwei')
                });
                
                updateStatus(`Increment successful! Hash: ${tx.transactionHash}`);
                
            } catch (error) {
                console.error('Error incrementing:', error);
                updateStatus('Error: ' + error.message);
            }
        }
        
        function updateStatus(message) {
            document.getElementById('status').innerHTML = 
                `<strong>Status:</strong> ${message}<br>
                 <small>${new Date().toLocaleTimeString()}</small>`;
        }
        
        // Initialize on load
        window.addEventListener('load', init);
    </script>
</body>
</html>
```

#### Update Contract Address

1. Open `deployment-info.json`
2. Copy the contract address
3. Replace `YOUR_CONTRACT_ADDRESS_HERE` in the HTML file
4. Open `interface.html` in your browser

### Step 8: Verify Your Contract (Optional)

#### Why Verify?

Contract verification:

* Makes source code public
* Enables direct interaction on block explorer
* Builds trust with users
* Allows easy contract reading

#### Manual Verification Steps

Until automated verification is available:

1. **Prepare verification info:**

```javascript
// Create verify-info.js
const info = {
  address: "YOUR_CONTRACT_ADDRESS",
  constructorArguments: [42],
  contract: "contracts/SimpleStorage.sol:SimpleStorage",
  compiler: "v0.8.30+commit.73712a01",
  optimizer: false
};

console.log("Verification info:", info);
```

2. **Save flattened contract:**

```bash
npx hardhat flatten contracts/SimpleStorage.sol > SimpleStorage_flat.sol
```

3. **When block explorer launches:**
   * Navigate to your contract
   * Click "Verify Contract"
   * Paste flattened source
   * Enter constructor arguments
   * Submit verification

### Advanced Topics

#### Gas Optimization

Optimize your contract for OPN's gas costs:

```solidity
// Gas-optimized version
contract OptimizedStorage {
    uint256 private value;
    
    // Pack multiple updates in one transaction
    function batchUpdate(uint256[] calldata values) external {
        uint256 finalValue = value;
        for (uint i = 0; i < values.length; i++) {
            finalValue = values[i];
        }
        value = finalValue;
    }
}
```

#### Using Events Efficiently

```solidity
contract EventOptimized {
    // Index important parameters for filtering
    event Transfer(
        address indexed from,
        address indexed to,
        uint256 value
    );
    
    // Use events for off-chain data
    event Metadata(string dataHash);
}
```

#### Upgradeable Contracts

For production, consider upgradeable patterns:

```solidity
// Using OpenZeppelin upgradeable contracts
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract SimpleStorageV2 is Initializable {
    uint256 private value;
    
    function initialize(uint256 _value) public initializer {
        value = _value;
    }
}
```

### Troubleshooting

#### Common Issues

**"Insufficient funds" error:**

* Check your wallet has OPN tokens
* Visit the faucet for more tokens

**"Network error" when deploying:**

* Verify RPC URL: `https://testnet-rpc.iopn.tech`
* Check your internet connection
* Try increasing gas limit

**"Nonce too high" error:**

```bash
# Reset Hardhat's nonce tracking
npx hardhat clean
rm -rf cache/
```

**Contract not verified:**

* Ensure exact compiler version match
* Check constructor arguments encoding
* Try flattening the contract

#### Getting Help

If you encounter issues:

1. Check error messages carefully
2. Search our [Discord](https://discord.gg/iopn)
3. Review FAQs

### Summary

In this tutorial, you:

* ✅ Set up a Hardhat project
* ✅ Wrote a smart contract
* ✅ Compiled and tested it
* ✅ Deployed to OPN testnet
* ✅ Interacted via scripts and web interface
* ✅ Learned about verification

You're now ready to build more complex applications on OPN Chain!

***

**Share your success!** Tweet your deployed contract address with #OPNChain
