# 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://iopn.gitbook.io/iopn/developer-docs/tutorials/deploy-your-first-contract.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
