Sign In / Up

Implementing Error Handling and the edit_proposal Function in Our Smart Contract

In our previous lesson, we built the initial query and update functions for our proposal system. In this lesson, we enhance our system by introducing robust error handling using enums, and we implement the logic for editing proposals.

Step 1: Defining Enumerations for Logic and Errors

Choice Enum

We define the Choice enum to represent a vote on a proposal. It includes:

  • Approve
  • Reject
  • Pass

Each of these choices will be used to update the corresponding vote count in a proposal.

#[derive(CandidType, Deserialize)]
pub enum Choice {
    Approve,
    Reject,
    Pass,
}

VoteError Enum

We introduce VoteError to handle various error scenarios in a structured manner:

#[derive(CandidType, Deserialize)]
pub enum VoteError {
    AlreadyVoted,
    ProposalIsNotActive,
    NoSuchProposal,
    AccessRejected,
    UpdateError,
}

These help signal issues such as unauthorized access, missing proposals, or unknown update failures.

Step 2: Implementing the edit_proposal Update Function

Only the creator (owner) of a proposal can edit it. Here's how the function works:

#[update]
fn edit_proposal(key: u64, new_data: CreateProposal) -> Result<(), VoteError> {
    PROPOSAL_MAP.with(|p| {
        let mut p = p.borrow_mut();

        // Retrieve existing proposal
        let old = match p.get(&key) {
            Some(proposal) => proposal.clone(),
            None => return Err(VoteError::NoSuchProposal),
        };

        // Check if the caller is the owner
        if ic_cdk::caller() != old.owner {
            return Err(VoteError::AccessRejected);
        }

        // Build updated proposal using the new description and is_active fields
        let updated = Proposal {
            description: new_data.description,
            approve: old.approve,
            reject: old.reject,
            pass: old.pass,
            is_active: new_data.is_active,
            voted: old.voted.clone(),
            owner: old.owner,
        };

        // Insert the updated proposal
        let result = p.insert(key, updated);

        match result {
            Some(_) => Ok(()),
            None => Err(VoteError::UpdateError),
        }
    })
}

Step 3: Summary of Logic

  1. Retrieve Proposal: Check if the proposal with the given key exists. If not, return NoSuchProposal.
  2. Verify Ownership: Ensure that the caller is the original creator. If not, return AccessRejected.
  3. Update Only Relevant Fields: Only description and is_active fields are updated; all others are preserved to maintain integrity.
  4. Insert Updated Proposal: Replace the existing proposal in the map and handle potential errors.

Lesson discussion

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

Be the first to start the discussion

Ask a question or share your thoughts about this lesson.