This report documents a critical integration error and four major logic vulnerabilities found in the premium_voting_v3.aleo smart contract and its deployment.
Cause: This is a frontend/wallet permission error. When using the Aleo SDK or Leo Wallet, the DApp must explicitly request permission for every program it intends to interact with during the connect() phase. If you only requested credits.aleo, any call to premium_voting_v3.aleo will be blocked by the wallet.
Fix (Frontend): Ensure your connection request includes the program ID:
await wallet.connect("testnet", ["credits.aleo", "premium_voting_v3.aleo"]);- Issue: The
vote_yesandvote_nofunctions only take aproposal_id. They do not check if theself.callerhas already voted. - Impact: A single user can vote infinitely many times on the same proposal, rendering the voting results meaningless.
- Issue: While
proposal_end_blocksis set during proposal creation, thefinalize_votefunctions never checkblock.heightagainst this mapping. - Impact: Users can continue voting on proposals indefinitely, even after the intended deadline has passed.
- Issue:
create_proposal_for_daousesMapping::setwithout checking if theproposal_idalready exists. - Impact: A malicious actor (or accidental call) can overwrite an active proposal, resetting all
yes_votesandno_votesback to zero and changing the end block.
- Issue: In
subscribe_paid, thestart_blockis hardcoded to0u32. - Impact: The
Subscriptionrecord does not accurately reflect when the subscription began, making it difficult to calculate true expiration ifduration_blocksis relative.
The following changes should be implemented to secure the contract:
- Add
voted_proposalsmapping: Trackhash(caller, proposal_id)to prevent double voting. - Add Expiry Check: Ensure
block.height <= Mapping::get(proposal_end_blocks, proposal_id). - Add Existence Check: Ensure
!Mapping::contains(proposals, proposal_id)before creating a new proposal.
See the updated premium_voting_v3.aleo source code in the repository for the implementation.