Creating a DAO and Deploying on Polygon Test Network

Contract Features

  • Member Management

The DAO initiates with the contract creator as the first member. Members have exclusive rights to add new members, ensuring a trusted network of participants.

  • Creating Proposals

Members can propose initiatives by specifying details and allocating funds. Proposals require an accompanying Ether deposit exceeding the proposal amount, aligning interests with DAO’s financial health.

  • Voting System

Each member holds equal voting power, with one vote per proposal. This democratic approach ensures collective decision-making and prevents individual dominance.

  • Executing Proposals

A proposal is executable upon achieving a majority vote and verifying sufficient DAO funds. This ensures that only viable and community-backed proposals are realized.

Creating the Project Directory

Create a directory called ‘DAO’.

Launch the VS Code editor and access the terminal. Then, navigate to the DAO directory.

Run the below command on terminal.

npx hardhat init
  • Smart Contract

Once the project files have been created, navigate to the ‘contracts’ folder and change the name of the ‘Lock.sol’ file to ‘Dao.sol’.

Paste the below code into the ‘Dao.sol’ file.

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

contract SimpleDAO {
    struct Proposal {
        string description;
        address payable recipient;
        uint amount;
        bool executed;
        uint voteCount;
    }

    Proposal[] public proposals;
    mapping(address => bool) public members;
    mapping(uint => mapping(address => bool)) public voted;

    modifier OnlyMember() {
        require(members[msg.sender], "Not a member");
        _;
    }

    constructor() {
        members[msg.sender] = true;
    }

    function addMember(address _member) public OnlyMember {
        members[_member] = true;
    }

    function createProposal(
        string memory _description,
        address payable _recipient,
        uint _amount
    ) public payable OnlyMember {
        require(msg.value > _amount, "Incorrect Ether value");

        proposals.push(
            Proposal({
                description: _description,
                recipient: _recipient,
                amount: _amount,
                executed: false,
                voteCount: 0
            })
        );
    }

    function vote(uint _proposalIndex) public OnlyMember {
        require(!voted[_proposalIndex][msg.sender], "Already voted");
        require(_proposalIndex < proposals.length, "Invalid proposal");

        Proposal storage proposal = proposals[_proposalIndex];
        proposal.voteCount += 1;
        voted[_proposalIndex][msg.sender] = true;
    }

    function executeProposal(uint _proposalIndex) public OnlyMember {
        Proposal storage proposal = proposals[_proposalIndex];
        require(!proposal.executed, "Proposal already executed");
        require(proposal.voteCount > (countMembers() / 2), "Not enought votes");
        require(
            address(this).balance >= proposal.amount,
            "Insufficient funds in DAO"
        );

        proposal.recipient.transfer(proposal.amount);
        proposal.executed = true;
    }

    function countMembers() internal view returns (uint) {
        uint memberCount = 0;
        for (uint i = 0; i < proposals.length; i++) {
            if (members[address(uint160(i))]) {
                memberCount++;
            }
        }

        return memberCount;
    }

    receive() external payable {}
}
  • Compile the Smart Contract

Use the below command on terminal to compile smart contract.

npx hardhat compile
  • Testing with Hardhat

Navigate to the ‘test’ folder and change the name of the ‘Lock.js’ file to ‘test.js’.

Paste the below code into the ‘test.js’ file.

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


describe("SimpleDAO", function () {
    let dao, owner, member1, nonMember, member2;

    beforeEach(async function () {
        const SimpleDAO = await ethers.getContractFactory("SimpleDAO");
        dao = await SimpleDAO.deploy();
        await dao.waitForDeployment();
        [owner, member1, nonMember, member2] = await ethers.getSigners();
    });

    it("should automatically make the deployer a member", async function () {
        expect(await dao.members(owner.address)).to.be.true;
    });

    it("should not allow a non-member to add a member", async function () {
        await expect(dao.connect(nonMember).addMember(member1.address)).to.be.revertedWith("Not a member");
    });

    it("should allow a member to add another member", async function () {
        await dao.addMember(member1.address);
        expect(await dao.members(member1.address)).to.be.true;
    });

});

To initiate the testing with Hardhat, run the following command. You should see ‘3 passing’ tests as a result.

npx hardhat test
  • Deploying the Smart Contract

Navigate to the ‘ignition/modules’ folder and change the name of the ‘Lock.js’ file to ‘deploy.js’.

Paste the below code into the ‘deploy.js’ file.

const { ethers } = require("hardhat");

async function main() {
    // Compile the contract
    await hre.run('compile');

     // Get the Contract Factory
  const SimpleDAO = await ethers.getContractFactory("SimpleDAO");
    // Deploy the contract
  const simpleDao = await SimpleDAO.deploy();
  await simpleDao.waitForDeployment();

  console.log("SimpleDAO deployed to:", simpleDao.target);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
    console.error(error);
    process.exitCode = 1;
  });

To verify that the deployment script is functioning correctly, execute the following command in the terminal, and you should receive the address of the deployed contract.

npx hardhat run ./scripts/deploy.js

To deploy the contract on Polygon Test Network, we need to create ‘.env’ file and import in the ‘hardhat.config.js’ file. To install the env, run the following command in the terminal.

npm install dotenv

Once installed, navigate to the project folder and create a file named ‘.env’. This file is used to store sensitive information that should not be shared. Insert the following code into the ‘.env’ file.

TEST_RPC_URL = "LINK THAT YOU GET FROM ALCHEMY.COM"
MNEMONIC = "YOUR METAMASK MNEMONIC"

To access the Polygon test network, navigate to https://dashboard.alchemy.com/ and either sign up or log in with your Google account. Then, click the ‘+ Create new app’ button.

Choose the ‘Polygon POS’ chain and the ‘Polygon Amoy’ network. Enter ‘DAO’ as the name and then click on the ‘Create app’ button.

This process will generate an API key that can be used to deploy our contract on a test network. Click the ‘API Key’ button and copy the HTTPS link into the ‘.env’ file under TEST_RPC_URL.

Navigate to the project folder, open the ‘hardhat.config.js’ file, and replace its contents with the following code.

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

const { MNEMONIC, TEST_RPC_URL } = process.env;

module.exports = {
  solidity: "0.8.20",
  networks: {
    amoy: {
      url: TEST_RPC_URL,
      accounts: {
        mnemonic: MNEMONIC,
      },
      gas: 2100000,
      gasPrice: 8000000000,
    },
  },
};

To deploy the contract on the Polygon test network, execute the following command in the terminal.

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

To add the Amoy Test Network to your Metamask wallet, use the following details.

NOTE: You need to ensure that Metamask wallet which we used the MNEMONIC has enough MATIC to deploy the contract. You can request 0.1 MATIC daily from alchemy faucet.

https://www.alchemy.com/faucets/polygon-amoy