As DAOs (Decentralized Autonomous Organizations, Organizações Autônomas Descentralizadas) são a base do ecossistema descentralizado. As DAOs são organizações governadas por contratos inteligentes e cujos processos de tomada de decisão são executados de forma descentralizada. Neste artigo, vamos nos aprofundar nos conceitos avançados do Solidity para criar uma DAO do zero._
Visão geral da estrutura de uma DAO
Nossa DAO consistirá nos seguintes componentes
- Um contrato inteligente que representa a própria DAO.
- Um token que representa o poder de voto e a propriedade dentro da DAO.
- Um mecanismo de proposta para enviar e votar em propostas.
- Uma tesouraria para gerenciar fundos e executar propostas que tenham sido aprovadas.
Pré-requisitos
Para seguir este tutorial, você deve ter um conhecimento básico de Solidity, Ethereum e do ambiente de desenvolvimento Truffle.
Etapa 1: Criação do token da DAO
Primeiro, vamos criar um novo token ERC20 para a nossa DAO. Esse token será usado para representar o poder de voto dentro da organização.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract DAOToken is ERC20 {
constructor(uint256 initialSupply) ERC20("DAO Token", "DAO") {
_mint(msg.sender, initialSupply);
}
}
Neste contrato, estamos usando a biblioteca OpenZeppelin para criar um novo token ERC20. O construtor recebe o suprimento inicial como parâmetro e cria os tokens para o implantador.
Etapa 2: Criação do contrato da DAO
Em seguida, vamos criar o contrato principal da DAO. Crie um novo arquivo chamado DAO.sol
// SPDX-License-Identifier: MIT
prasgma solidity ^0.8.0;
import "./DAOToken.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract DAO is Ownable {
using EnumerableSet for EnumerableSet.AddressSet;
// O contrato do token da DAO
DAOToken public daoToken;
// A quantidade mínima de tokens necessária para criar uma proposta
uint256 public constant MIN_PROPOSAL_THRESHOLD = 1000 * 10**18;
// A quantidade mínima de tokens necessária para votar em uma proposta
uint256 public constant MIN_VOTING_THRESHOLD = 100 * 10**18;
// Estrutura da proposta
struct Proposal {
uint256 id;
address proposer;
string description;
uint256 amount;
address payable recipient;
uint256 startTime;
uint256 endTime;
uint256 yesVotes;
uint256 noVotes;
EnumerableSet.AddressSet voters;
bool executed;
}
// Lista de todas as propostas
Proposal[] public proposals;
// Mapping para verificar se um endereço tem uma proposta ativa
mapping(address => bool) public activeProposals;
// Evento para uma nova proposta
event NewProposal(uint256 indexed proposalId, address indexed proposer, string description);
// Evento para a execução de uma proposta
event ProposalExecuted(uint256 indexed proposalId, address indexed proposer, address indexed recipient, uint256 amount);
constructor(DAOToken _daoToken) {
daoToken = _daoToken;
}
// Função para criar uma nova proposta
function createProposal(string memory _description, uint256 _amount, address payable _recipient) external {
require(daoToken.balanceOf(msg.sender) >= MIN_PROPOSAL_THRESHOLD, "Insufficient tokens to create proposal");
require(!activeProposals[msg.sender], "You already have an active proposal");
Proposal memory newProposal = Proposal({
id: proposals.length,
proposer: msg.sender,
description: _description,
amount: _amount,
recipient: _recipient,
startTime: block.timestamp,
endTime: block.timestamp + 7 days,
yesVotes: 0,
noVotes: 0,
voters: new EnumerableSet.AddressSet(),
executed: false
});
proposals.push(newProposal);
activeProposals[msg.sender] = true;
emit NewProposal(newProposal.id, msg.sender, _description);
}
// Função para votar em uma proposta
function vote(uint256 _proposalId, bool _support) external {
require(daoToken.balanceOf(msg.sender) >= MIN_VOTING_THRESHOLD, "Insufficient tokens to vote");
Proposal storage proposal = proposals[_proposalId];
require(block.timestamp >= proposal.startTime && block.timestamp <= proposal.endTime, "Invalid voting period");
require(!proposal.voters.contains(msg.sender), "You have already voted on this proposal");
uint256 voterWeight = daoToken.balanceOf(msg.sender);
if (_support) {
proposal.yesVotes += voterWeight;
} else {
proposal.noVotes += voterWeight;
}
proposal.voters.add(msg.sender);
}
// Função para executar uma proposta
function executeProposal(uint256 _proposalId) external {
Proposal storage proposal = proposals[_proposalId];
require(!proposal.executed, "Proposal has already been executed");
require(block.timestamp > proposal.endTime, "Voting period is still ongoing");
require(proposal.yesVotes > proposal.noVotes, "Proposal has not reached majority support");
proposal.recipient.transfer(proposal.amount);
proposal.executed = true;
activeProposals[proposal.proposer] = false;
emit ProposalExecuted(_proposalId, proposal.proposer, proposal.recipient, proposal.amount);
}
// Função de retirada de fundos da DAO
function withdraw(uint256 _amount) external onlyOwner {
payable(owner()).transfer(_amount);
}
// Função de fallback para aceitar Ether
receive() external payable {}
}
Neste contrato, definimos a estrutura principal da DAO
- O contrato de token da DAO é importado e armazenado como uma variável.
- Uma estrutura de proposta é definida com os campos necessários, como ID da proposta, o propositor, descrição, valor, destinatário e detalhes da votação.
- Uma lista armazena todas as propostas e um mapeamento mantém o controle das propostas ativas.
- São criadas funções para lidar com a criação, votação e execução de propostas.
Etapa 3: implantando e testando a DAO
Agora que criamos o contrato da DAO, vamos implantá-lo e testar sua funcionalidade. Primeiro, vamos criar um arquivo de migração para nossos contratos
const DAOToken = artifacts.require("DAOToken"); const DAO = artifacts.require("DAO");
module.exports = async function (deployer) {
// Implantar o contrato DAOToken com um fornecimento inicial de 1.000.000 tokens
await deployer.deploy(DAOToken, "1000000" + "0".repeat(18));
const daoTokenInstance = await DAOToken.deployed();
// Implantar o contrato da DAO com uma referência ao contrato DAOToken
await deployer.deploy(DAO, daoTokenInstance.address);
const daoInstance = await DAO.deployed();
};
Agora vamos adicionar alguns testes para garantir que nosso contrato da DAO esteja funcionando conforme o esperado.
const { assert } = require("chai");
const { expectRevert, time } = require("@openzeppelin/test-helpers");
const DAOToken = artifacts.require("DAOToken");
const DAO = artifacts.require("DAO");
contract("DAO", ([deployer, user1, user2, recipient]) => {
beforeEach(async () => {
this.daoToken = await DAOToken.new("1000000" + "0".repeat(18), { from: deployer });
this.dao = await DAO.new(this.daoToken.address, { from: deployer });
});
it("should create a proposal and vote on it", async () => {
// Transferência de tokens para o usuário1
await this.daoToken.transfer(user1, "1100" + "0".repeat(18), { from: deployer });
// Usuário1 cria uma proposta
await this.dao.createProposal("Fund project X", "100" + "0".repeat(18), recipient, {
from: user1,
});
//Verifiqua os detalhes da proposta
const proposal = await this.dao.proposals(0);
assert.equal(proposal.id, "0");
assert.equal(proposal.proposer, user1);
assert.equal(proposal.description, "Fund project X");
assert.equal(proposal.amount, "100" + "0".repeat(18));
assert.equal(proposal.recipient, recipient);
// Usuário1 vota na proposta
await this.dao.vote(0, true, { from: user1 });
// Verifica os resultados da votação
assert.equal((await this.dao.proposals(0)).yesVotes, "1100" + "0".repeat(18));
assert.equal((await this.dao.proposals(0)).noVotes, "0");
// Avanço rápido para depois do período de votação
await time.increase(time.duration.days(8));
// Executa a proposta
await this.dao.executeProposal(0, { from: user1 });
// Verifica se a proposta foi executada
assert.equal((await this.dao.proposals(0)).executed, true);
});
it("should not allow a user with insufficient tokens to create a proposal", async () => {
// Tentativa de criar uma proposta com tokens insuficientes
await expectRevert(
this.dao.createProposal("Fund project X", "100" + "0".repeat(18), recipient, { from: user2 }),
"Insufficient tokens to create proposal"
);
});
// Adicione mais testes conforme necessário
});
Neste arquivo de teste, abordamos dois testes principais
- Criar uma proposta e votar nela.
- Garantir que um usuário com tokens insuficientes não possa criar uma proposta. Você pode executar os testes usando o seguinte comando:
Etapa 4: Interagindo com a DAO
Você pode usar uma interface da Web para interagir com a DAO implantada.
Os principais componentes da interface da Web podem incluir
- Um painel que exibe o saldo do token da DAO e a lista de propostas.
- Um formulário para criar uma nova proposta.
- Uma interface de votação para votar em propostas existentes.
- Um botão para executar propostas aprovadas.
Conclusão
O potencial da Web 3.0 e dos aplicativos descentralizados é vasto e oferece oportunidades interessantes para os desenvolvedores moldarem o futuro da Internet. Ao aproveitar o poder da tecnologia blockchain, podemos criar aplicativos mais seguros, transparentes e resilientes que capacitam os usuários e promovem a descentralização.
Como desenvolvedor, você tem a chance de contribuir para o crescimento do ecossistema descentralizado e criar soluções inovadoras para resolver problemas do mundo real. Eu o incentivo a explorar as inúmeras possibilidades que as tecnologias web3 oferecem, desde finanças descentralizadas (DeFi) e tokens não fungíveis (NFTs) até armazenamento descentralizado e gerenciamento de identidade.
Artigo escrito por Faucet Consumer. A versão original pode ser encontrada aqui. Traduzido e adaptado por Dimitris Calixto.
Top comments (0)