• Discover
  • Partner with us
  • Chapters
  • Blog

Build on Internet Computer with ICP Rust CDK

Course Introduction

Introduction
Overview of the IC ecosystem and core differences
Introduction to the Internet Computer (IC) ecosystem
Overview of the IC architecture and its components
Setting up the development environment for IC

Introduction
Creating Smart Contract
State Management 1
State Management 2
Update and Query Functions
Updating the Candid File
Testing the Contract

Creating Smart Contract for Final Project
Creating Functions 1
Creating Functions 2
Creating Functions 3
Creating Candid File
Testing and Deploying Smart Contract
Overview of the Frontend for Final Project
Explaining Frontend Code

Final Project

Finalizing the Voting Smart Contract: end_proposal and vote Functions

In the previous video, we built the edit_proposal function. In this lesson, we finalize the smart contract by implementing the remaining two core update functions: end_proposal and vote. These complete the functionality of our decentralized voting system.

Function 1: end_proposal

This function deactivates a proposal, preventing further votes.

Logic Breakdown:

  • Retrieve the Proposal
  • Use the proposal key to fetch the entry from the state.
  • If the proposal does not exist, return a VoteError::NoSuchProposal.
  • Verify Ownership
  • Ensure the caller is the proposal’s creator.
  • If not, return a VoteError::AccessRejected.
  • Update State
  • Set is_active to false.
  • Reinsert the modified proposal into the state.
  • If insertion succeeds, return Ok(()); otherwise, return VoteError::UpdateError.

Sample Implementation Outline:

#[update]
fn end_proposal(key: u64) -> Result<(), VoteError> {
    PROPOSAL_MAP.with(|p| {
        let mut p = p.borrow_mut();
        let proposal = match p.get(&key) {
            Some(p) => p.clone(),
            None => return Err(VoteError::NoSuchProposal),
        };
        if ic_cdk::caller() != proposal.owner {
            return Err(VoteError::AccessRejected);
        }

        let mut updated = proposal;
        updated.is_active = false;

        let result = p.insert(key, updated);
        match result {
            Some(_) => Ok(()),
            None => Err(VoteError::UpdateError),
        }
    })
}

Function 2: vote

This function allows users to cast a vote (approve, reject, or pass) on an active proposal.

Logic Breakdown:

  • Retrieve the Proposal
  • Similar to the previous function, check for the proposal’s existence.
  • Return VoteError::NoSuchProposal if not found.
  • Check Voting Eligibility
  • Ensure the user has not already voted (by checking the voted list).
  • Ensure the proposal is still active.
  • If either check fails, return the appropriate VoteError.
  • Cast the Vote
  • Increment the appropriate vote count (approve, reject, or pass) based on the Choice enum.
  • Add the caller’s address to the voted vector to prevent double voting.
  • Update the State
  • Insert the updated proposal.
  • Return Ok(()) on success or VoteError::UpdateError on failure.

Sample Implementation Outline:

#[update]
fn vote(key: u64, choice: Choice) -> Result<(), VoteError> {
    PROPOSAL_MAP.with(|p| {
        let mut p = p.borrow_mut();
        let mut proposal = match p.get(&key) {
            Some(p) => p.clone(),
            None => return Err(VoteError::NoSuchProposal),
        };

        let caller = ic_cdk::caller();
        if proposal.voted.contains(&caller) {
            return Err(VoteError::AlreadyVoted);
        }
        if !proposal.is_active {
            return Err(VoteError::ProposalIsNotActive);
        }

        match choice {
            Choice::Approve => proposal.approve += 1,
            Choice::Reject => proposal.reject += 1,
            Choice::Pass => proposal.pass += 1,
        }

        proposal.voted.push(caller);

        let result = p.insert(key, proposal);
        match result {
            Some(_) => Ok(()),
            None => Err(VoteError::UpdateError),
        }
    })
}

Conclusion

With the addition of end_proposal and vote, your voting smart contract is now complete. Each function follows a consistent pattern:

  • Retrieve state data.
  • Validate it.
  • Apply logic.
  • Write it back safely.
  • Return a clear result or error.

This pattern ensures the contract is secure, maintainable, and easy to understand.

Previous
Next

Lesson discussion

Swap insights and ask questions about “Build on Internet Computer with ICP Rust CDK”.

Enroll to participate
Start the course to unlock the discussion. Enrolling helps us keep conversations relevant to learners.
WebsiteDiscoverPartner with UsBlogEvents
Discover
CoursesCircleRustSoliditySolanaWeb3 FundamentalsBlockchain Basics
CompanyAbout UsBrand GuidelineFAQsTerms of UsePrivacy PolicyGDPR NoticeCookies
Don't miss any update!

Disclaimer: The information, programs, and events provided on https://risein.com is strictly for upskilling and networking purposes related to the technical infrastructure of blockchain platforms. We do not provide financial or investment advice, nor do we make any representations regarding the value, profitability, or future price of any blockchain or cryptocurrency. Users are encouraged to conduct their own research and consult with licensed financial professionals before engaging in any investment activities. https://risein.com disclaims any responsibility for financial decisions made by users based on the information provided here.

© 2026 Rise In, All rights reserved