Building a Treasury Management DAO DApp on Conflux eSpace
Welcome back! In this blog, we’ll walk through building a treasury management decentralized application (DApp) powered by a Decentralized Autonomous Organization (DAO). We’ll deploy a smart contract on the Conflux eSpace testnet and cover the full workflow: creating clubs, contributing funds, proposing actions, voting, and executing proposals.
Prerequisites
Before you start, make sure you have:
- Node.js and npm
- MetaMask wallet (configured for Conflux eSpace testnet)
- Remix IDE or Hardhat for smart contract development
- Some testnet CFX (get it from the Conflux Faucet)
What Are We Building?
Our DApp enables users to:
- Create investment clubs
- Contribute testnet CFX
- Submit and vote on proposals
- Execute or close proposals based on votes
All actions are managed democratically through the DAO smart contract.
Key Smart Contract Features
Here’s a look at the most important parts of the Solidity contract:
Club Creation
function createClub(string memory name, string memory Cid) public returns (uint256) {
uint256 clubId = clubCounter + 1;
ClubLibrary.Club storage club = clubs[clubId];
club.id = clubId;
club.name = name;
club.memberCounter = 1;
club.CID = Cid;
club.members[msg.sender] = ClubLibrary.Member({ memberAddress: msg.sender, balance: 0 });
clubCounter = clubId;
return clubId;
}
- Anyone can create a club.
- The creator becomes the first member.
Joining and Contributing
function joinClub(uint256 clubId) public {
require(isClubIdExist(clubId), "The club does not exist");
require(isClubFull(clubId), "The club is full");
require(!isMemberOfClub(msg.sender, clubId), "Already a member");
club.members[msg.sender] = ClubLibrary.Member({ memberAddress: msg.sender, balance: 0 });
club.memberCounter += 1;
}
function contributeToClub(uint256 clubId) public payable {
require(isClubIdExist(clubId), "the club does not exist");
require(isMemberOfClub(msg.sender, clubId), "Not a member");
require(msg.value > 0, "Send CFX to contribute");
club.members[msg.sender].balance += msg.value;
club.pool += msg.value;
}
- Join clubs and contribute CFX to the shared pool.
Proposals and Voting
function createProposal(uint256 clubId, uint256 amount, address destination, string memory description, string memory Cid) public {
require(isClubIdExist(clubId), "the club does not exist");
require(isMemberOfClub(msg.sender, clubId), "Not a member");
uint256 proposalId = club.proposalCounter + 1;
ClubLibrary.Proposal storage proposal = clubs[clubId].proposals[proposalId];
proposal.id = proposalId;
proposal.creator = msg.sender;
proposal.amount = amount;
proposal.destination = destination;
proposal.status = "Pending";
proposal.description = description;
proposal.proposedAt = block.timestamp;
proposal.proposalExpireAt = block.timestamp + 5 minutes;
club.proposalCounter = proposalId;
}
- Members can propose actions (like fund transfers).
- Proposals are open for voting for 5 minutes.
function voteOnProposal(uint256 clubId, uint256 proposalId, bool vote) public {
require(isClubIdExist(clubId), "the club does not exist");
require(isVotingOn(clubId, proposalId), "Voting Period Finished");
require(isMemberOfClub(msg.sender, clubId), "Not a member");
require(!hasVoted(msg.sender, proposalId, clubId), "Already voted");
proposal.voted[msg.sender] = vote;
if (vote) proposal.votesFor += 1;
else proposal.votesAgainst += 1;
}
- Each member can vote once per proposal.
Executing and Closing Proposals
function executeProposal(uint256 clubId, uint256 proposalId) public payable {
require(isClubIdExist(clubId), "the club does not exist");
require(isMemberOfClub(msg.sender, clubId), "Not a member");
require(isValidExecutor(clubId, proposalId), "Only creator can execute");
require(club.pool >= proposal.amount, "Insufficient pool");
require(proposal.votesFor > proposal.votesAgainst, "Not approved");
require(!isVotingOn(clubId, proposalId), "Voting still active");
proposal.status = "Executed";
club.pool -= proposal.amount;
payable(proposal.destination).transfer(proposal.amount);
}
- Only the proposal creator can execute after voting ends and if approved.
Demo Workflow
- Compile and Deploy the contract to Conflux eSpace testnet.
- Create a Club – become the first member.
- Contribute Funds – add CFX to the club pool.
- Create a Proposal – suggest an action (e.g., transfer funds).
- Vote – members vote yes/no.
- Execute or Close – if approved, execute; otherwise, close.
Conclusion
This DApp demonstrates how DAOs can manage shared funds transparently and democratically. Next, we’ll build a front end for a user-friendly experience. Stay tuned!
Useful Links
- GitHub Repo
- Conflux eSpace Documentation
- Remix IDE
- MetaMask
- Conflux Faucet
- Solidity Documentation
- Hardhat
- OpenZeppelin Contracts
*Like this tutorial? Star the repo, subscribe for more blockchain content, and leave