How to Develop a Decentralized Autonomous Organisation

Posted By : Pradeep

Jun 23, 2022

DAOs resemble the rules of a company. DAOs are governed by smart contract development. Traditional companies are governed by corporate laws, regulations, and judiciary systems (courts). DAOs are regulated by code -- Code is the law. DAO tokens are used for governance, voting, etc. They work like shares in a company each token is a vote. Token holders can make proposals and update the DAO.

 

DAO Stands for:

 

  • Decentralized autonomous organization
  • Decentralized democratic organization
  • Decentralized autonomous corporation

 

Benefits of DAO

 

  • Trustless (don't need to trust a CEO or management team)
  • They can't be shut down (unless the majority of token holders agree)
  • Open source and fully transparent
  • Fully democratized
  • Token holders can propose innovative ideas and they can be put forward by voting
  • Any changes need to be approved by members voting
  • Services offered are handled automatically in a decentralized manner (for example distribution of philanthropic funds)

 

Also, Check | Code Analysis Tools for Solidity Smart Contracts

 

Disadvantages: 

 

  • DAOs may also be vulnerable to bugs
  • No business secrets (see the number of Olympus DAO forks)

 

Most Common Types of DAOs :

 

  • Defi protocol DAO: Uniswap, Aave, MakerDAO, Olympus DAO, DigixDAO, etc.
  • Investment DAO: MetaCartel, The LAO, The DAO, etc. 
  • Grants/Launchpad DAOs: Gitcoin, Moloch, Seedfy, etc.
  • Collector DAO: PleaserDAO, Flamingo, Mutant Cats DAO, Constitution DAO, etc. 
  • Social DAO: Ape DAO, Decentraland DAO, etc. 

 

DAOs legal recognition : 

 

  • Most DAOs don't have a legal structure (i.e. they don't have legal personality)
  • In most countries, DAO regulation is nonexistent or unclear
  • In July 2021, Wyoming became the first US state to recognize DAO as a legal entity
  • DAO tokens may be considered regulated security or not, depending on the Howey Test and Securities Act of 1933 (for the US)
  • DAO founders/teams need to be careful

 

DAOs security:

 

  • Blockchains are very secure
  • Smart contracts however may have security flaws or bugs
  • 3rd-party smart contract reviews, testing, and auditing are important
  • Beware of scammers and rug pulls

 

DAOs future :

 

  • Smart contracts could automate a business like a fleet of "Uber" cars.
  • The smart contract can trigger invoices, handle payments, asset maintenance, etc.
  • The DAO can pay for the cars, parts, mechanics, cleaning, energy/fuel, etc.
  • DAO token holders are the shareholders of a business.

 

NOTE : 

 

DAOs are still very new and investing in DAOs is very risky because of the unknown unknowns, known unknowns, and known knowns.

 

Also, Explore | Best Practices for Smart Contract Development

 

DAO Development Steps

 

Prerequisite :

 

1 - Nodejs and npm installed

2 - Truffle (to write, compile, and deploy smart contracts and interact with smart contracts deployed on the development blockchain using scripts)

3 - Javascript (to write scripts)

4 - git and GitHub (plus to your tool pack)

 

Truffle commands to be used for the development environment : 

 

truffle compile
truffle develop
truffle migrate
truffle exec scripts/<name of the script file>

 

Breakdown of several smart contracts : 

 

1 - Token

2 - Governance

3 - Treasury

4 - Timelock

 

Token

 

This is going to govern the actual cryptocurrency token that the token holders are going to possess; because you need a token to vote. This is an ERC20 token. Some of the tokens are token holders, so they can actually vote. Now the voting is going to take place with a different smart contract( Governance contract).

 

Treasury

 

The Treasury is where all the ether funds will go. It holds Ethereum cryptocurrency (ether). The voters are going to vote on how they want these funds to be spent. In our scripts we will propose some sort of recipient, we will say --"hey do you want this person to receive this amount of money?" eg. Fund a new project. 

 

Governance Contract:

 

As token holders, you can talk to this Governance contract. You wanna create a proposal (eg. we want x amount of ether, go to the Recipient, for some reason). You will add this proposal to the government contract once that proposal is accepted. The token holders can then vote on this proposal. And if enough votes passed (you can specify - how many votes you want to pass). Then the Governance contract will actually take a special encoded function, and it will call that function on this Treasury contract to release funds. That's what the Governance contract does, when you create a proposal, you basically tell the function you want to call on a different contract( in this case, this can be 'release funds from the Treasury). And those ether funds are released to the recipient after the proposal is passed and executed.

 

Timelock

 

There is a small time delay between, when the proposal passes and the stuff actually can happen( i.e. execution). They are common in DAOs, so it's now you make a decision. Everybody has time to prepare for it and adjust and get ready for it. We have a separate contract with Timelock. Essentially, there is a time delay between the time the vote passes and the action occurs (i.e. executed).

 

You may also like | Transform Your Business with Smart Contracts - Oodles Blockchain Edition!

 

Folder structure

 

1 - Contracts - Code that lives on a blockchain( in our case: development blockchain)

2 - Migrations - Deploy contracts on a blockchain 

3 - Truffle config file - connects to the contracts on the blockchain 

4 - Script  - Helps us to create a new proposal and interact with the deployed contracts on a blockchain

 

Token contract

 

This is an ERC20 token. we import special ERC20Votes which is a voting token. It has a special function named _afterTokenTransfer(). It works like a regular ERC20 token, it must have some external little functionalities.

 

Code

 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
contract Token is ERC20Votes {
constructor(
string memory _name,
string memory _symbol,
uint256 _initialSupply
) ERC20(_name, _symbol) ERC20Permit(_name) {
_mint(msg.sender, _initialSupply);
}
// The functions below are overrides required by Solidity.
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal override(ERC20Votes) {
super._afterTokenTransfer(from, to, amount);
}
function _mint(address to, uint256 amount) internal override(ERC20Votes) {
super._mint(to, amount);
}
function _burn(address account, uint256 amount)
internal
override(ERC20Votes)
{
super._burn(account, amount);
}
}

 

Treasury contract

 

This is where the ether cryptocurrency will go, whenever we want to transfer. That's where it will live, until we transfer it.

 

Code:

 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
contract Treasury is Ownable {
uint256 public totalFunds;
address public payee;
bool public isReleased;
constructor(address _payee) payable {
totalFunds = msg.value;
payee = _payee;
isReleased = false;
}
function releaseFunds() public onlyOwner {
isReleased = true;
payable(payee).transfer(totalFunds);
}
}

 

Timelock contract

 

This contract controls the delay. The delay in releasing the funds, after the proposal has passed.

 

Also, Check |  AI for DAO | Robots are Essential for a Better Future

 

Code

 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/governance/TimelockController.sol";
contract TimeLock is TimelockController {
constructor(
uint256 _minDelay,
address[] memory _proposers,
address[] memory _executors
) TimelockController(_minDelay, _proposers, _executors) {}
}

 

Governance contract

 

This is the main contract, that controls the project. It pulls all the openzeppenlin contracts, Governor, GovernorCountingSimple, GovernorVotes, GovernorQuorumFraction, and GovernorTimelockControl.

 

Code

 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/governance/Governor.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
// NOTE: Contract created with the help of --> https://wizard.openzeppelin.com/#governor
contract Governance is
Governor,
GovernorCountingSimple,
GovernorVotes,
GovernorVotesQuorumFraction,
GovernorTimelockControl
{
uint256 public votingDelay_;
uint256 public votingPeriod_;
constructor(
ERC20Votes _token,
TimelockController _timelock,
uint256 _quorum,
uint256 _votingDelay,
uint256 _votingPeriod
)
Governor("Oodles DAO")
GovernorVotes(_token)
GovernorVotesQuorumFraction(_quorum)
GovernorTimelockControl(_timelock)
{
votingDelay_ = _votingDelay;
votingPeriod_ = _votingPeriod;
}
function votingDelay() public view override returns (uint256) {
return votingDelay_;
}
function votingPeriod() public view override returns (uint256) {
return votingPeriod_;
}
// The following functions are overrides required by Solidity.
function quorum(uint256 blockNumber)
public
view
override(IGovernor, GovernorVotesQuorumFraction)
returns (uint256)
{
return super.quorum(blockNumber);
}
function getVotes(address account, uint256 blockNumber)
public
view
override(IGovernor, GovernorVotes)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
function state(uint256 proposalId)
public
view
override(Governor, GovernorTimelockControl)
returns (ProposalState)
{
return super.state(proposalId);
}
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public override(Governor, IGovernor) returns (uint256) {
return super.propose(targets, values, calldatas, description);
}
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) {
super._execute(proposalId, targets, values, calldatas, descriptionHash);
}
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) returns (uint256) {
return super._cancel(targets, values, calldatas, descriptionHash);
}
function _executor()
internal
view
override(Governor, GovernorTimelockControl)
returns (address)
{
return super._executor();
}
function supportsInterface(bytes4 interfaceId)
public
view
override(Governor, GovernorTimelockControl)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}

 

Also, Explore | Most Substantial DAO Use Cases for Businesses

 

Deploying script

 

It pulls the deployed contracts.  It will pull some accounts from the local development blockchain. Inside the migration script, we will assign these different accounts to different roles.

 

1 - Executor

2 - Proposer

3 - Voters (voter1, voter2, voter3, voter4, voter5)

 

Token name: Oodles Technologies

 

Token symbol: ODL

 

Then, you deploy the token, and give 50 tokens to each voter. After we have deployed the contract here, the executor will own the token, they own it so they will send 50 tokens to each voter. We configure Timelock delay for the votes, so how long do we have to wait until we have to wait after a proposal has passed? Governance can take some settings like quorum, the percentage of total supply to the people(tokens). Threshold, voting period, voting delay for several blocks. Timelock will be the owner of the Treasury contract. Once the proposal is executed, TimelockControl is responsible for calling the function. We give Timelock some ether (say 25 ether) and we deploy it and we tell who the executor is and how much funds we want to send in that transaction. Finally, we want to transfer ownership of the Treasury to the Timelock address. We assign roles to different accounts using the _grantRole function of Timelock.

 

Code

 

const Token = artifacts.require("Token")
const Timelock = artifacts.require("TimeLock")
const Governance = artifacts.require("Governance")
const Treasury = artifacts.require("Treasury")
module.exports = async function (callback) {
const [executor, proposer, voter1, voter2, voter3, voter4, voter5] = await web3.eth.getAccounts()
let isReleased, funds, blockNumber, proposalState, vote
const amount = web3.utils.toWei('5', 'ether')
const token = await Token.deployed()
await token.delegate(voter1, { from: voter1 })
await token.delegate(voter2, { from: voter2 })
await token.delegate(voter3, { from: voter3 })
await token.delegate(voter4, { from: voter4 })
await token.delegate(voter5, { from: voter5 })
const treasury = await Treasury.deployed()
isReleased = await treasury.isReleased()
console.log(`Funds released? ${isReleased}`)
funds = await web3.eth.getBalance(treasury.address)
console.log(`Funds inside of treasury: ${web3.utils.fromWei(funds.toString(), 'ether')} ETH\n`)
const governance = await Governance.deployed()
const encodedFunction = await treasury.contract.methods.releaseFunds().encodeABI()
const description = "Release Funds from Treasury"
const tx = await governance.propose([treasury.address], [0], [encodedFunction], description, { from: proposer })
const id = tx.logs[0].args.proposalId
console.log(`Created Proposal: ${id.toString()}\n`)
proposalState = await governance.state.call(id)
console.log(`Current state of proposal: ${proposalState.toString()} (Pending) \n`)
const snapshot = await governance.proposalSnapshot.call(id)
console.log(`Proposal created on block ${snapshot.toString()}`)
const deadline = await governance.proposalDeadline.call(id)
console.log(`Proposal deadline on block ${deadline.toString()}\n`)
blockNumber = await web3.eth.getBlockNumber()
console.log(`Current blocknumber: ${blockNumber}\n`)
const quorum = await governance.quorum(blockNumber - 1)
console.log(`Number of votes required to pass: ${web3.utils.fromWei(quorum.toString(), 'ether')}\n`)
// Vote
console.log(`Casting votes...\n`)
// 0 = Against, 1 = For, 2 = Abstain
vote = await governance.castVote(id, 1, { from: voter1 })
vote = await governance.castVote(id, 1, { from: voter2 })
vote = await governance.castVote(id, 1, { from: voter3 })
vote = await governance.castVote(id, 0, { from: voter4 })
vote = await governance.castVote(id, 2, { from: voter5 })
// States: Pending, Active, Canceled, Defeated, Succeeded, Queued, Expired, Executed
proposalState = await governance.state.call(id)
console.log(`Current state of proposal: ${proposalState.toString()} (Active) \n`)
// NOTE: Transfer serves no purposes, it's just used to fast foward one block after the voting period ends
await token.transfer(proposer, amount, { from: executor })
const { againstVotes, forVotes, abstainVotes } = await governance.proposalVotes.call(id)
console.log(`Votes For: ${web3.utils.fromWei(forVotes.toString(), 'ether')}`)
console.log(`Votes Against: ${web3.utils.fromWei(againstVotes.toString(), 'ether')}`)
console.log(`Votes Neutral: ${web3.utils.fromWei(abstainVotes.toString(), 'ether')}\n`)
blockNumber = await web3.eth.getBlockNumber()
console.log(`Current blocknumber: ${blockNumber}\n`)
proposalState = await governance.state.call(id)
console.log(`Current state of proposal: ${proposalState.toString()} (Succeeded) \n`)
// Queue
const hash = web3.utils.sha3("Release Funds from Treasury")
await governance.queue([treasury.address], [0], [encodedFunction], hash, { from: executor })
proposalState = await governance.state.call(id)
console.log(`Current state of proposal: ${proposalState.toString()} (Queued) \n`)
// Execute
await governance.execute([treasury.address], [0], [encodedFunction], hash, { from: executor })
proposalState = await governance.state.call(id)
console.log(`Current state of proposal: ${proposalState.toString()} (Executed) \n`)
isReleased = await treasury.isReleased()
console.log(`Funds released? ${isReleased}`)
funds = await web3.eth.getBalance(treasury.address)
console.log(`Funds inside of treasury: ${web3.utils.fromWei(funds.toString(), 'ether')} ETH\n`)
callback()
}

 

Also, Check | AI-Driven Smart Contracts: Merging Intelligence with Automation

 

Conclusion:

 

DAOs are an effective and safe way to work with like-minded folks around the globe. Think of them like an internet-native business that's collectively owned and managed by its members. They have built-in treasuries that no one has the authority to access without the approval of the group.

 

Reference :

 

For complete code and folder structure refer to this GitHub repo :

 

https://github.com/deep-dev-engineer/GovernanceDAO

 

For a complete walkthrough of the governance contract and core, contracts refer to this link: https://docs.openzeppelin.com/contracts/4.x/api/governance

 

How to set up an on-chain governance guide: https://docs.openzeppelin.com/contracts/4.x/governance

 

If you want to build a DAO and looking for an experienced blockchain development company, connect with our skilled blockchain developers to get started. 

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

November 21, 2024 at 11:24 am

Your comment is awaiting moderation.

By using this site, you allow our use of cookies. For more information on the cookies we use and how to delete or block them, please read our cookie notice.

Chat with Us
Telegram Button
Youtube Button

Contact Us

Oodles | Blockchain Development Company

Name is required

Please enter a valid Name

Please enter a valid Phone Number

Please remove URL from text