Building a Treasury Management DAO DApp on Conflux eSpace Testnet

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:


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

  1. Compile and Deploy the contract to Conflux eSpace testnet.
  2. Create a Club – become the first member.
  3. Contribute Funds – add CFX to the club pool.
  4. Create a Proposal – suggest an action (e.g., transfer funds).
  5. Vote – members vote yes/no.
  6. 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

*Like this tutorial? Star the repo, subscribe for more blockchain content, and leave