Skip to main content

Execute

This is where we will be writing core execution logic to do two things: First, execute the create poll function and then execute the voting function.

01 Execute create_poll function

Let's take a look at src/contract.rs and more specifically that unimplemented execution function.

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
_deps: DepsMut,
_env: Env,
_info: MessageInfo,
_msg: ExecuteMsg,
) -> Result<Response, ContractError> {
unimplemented!()
}

This function needs to become a switch case (matching case) and redirect appropriate messages to appropriate function calls.

  1. Let's match the msg variable for all its different types.
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut, // removed _ as needed later
env: Env, // removed _ as needed later
info: MessageInfo, // removed _ as needed later
msg: ExecuteMsg, // remove _ as used now
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::CreatePoll {
poll_id,
question,
options,
} => unimplemented!(),
ExecuteMsg::Vote { poll_id, vote } => unimplemented!(),
}
}
  1. Let's start our first implementation

    Pass all the deps, env, and info variables to each execute function and provide all the relevant information.

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut, // removed _ as needed later
env: Env, // removed _ as needed later
info: MessageInfo, // removed _ as needed later
msg: ExecuteMsg, // remove _ as used now
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::CreatePoll {
poll_id,
question,
options,
} => execute_create_poll(deps, env, info, poll_id, question, options),
ExecuteMsg::Vote { poll_id, vote } => unimplemented!(),
}
}
  1. Place the following after the execute function and before the query function:
fn execute_create_poll(
deps: DepsMut,
_env: Env, // _env as we won't be using it
info: MessageInfo,
poll_id: String,
question: String,
options: Vec<String>,
) -> Result<Response, ContractError> {
unimplemented!()
}
  1. Let's add a new error called TooManyOptions. This is prompted if there are >10 options selected.

Open the src/errors.rs file.

use cosmwasm_std::StdError;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ContractError {
#[error("{0}")]
Std(#[from] StdError),

#[error("Unauthorized")]
Unauthorized {},

#[error("Too many poll options")]
TooManyOptions {},
}
  1. Open src/contract.rs and add the following code. This adds an if statement to handle the contract error and constructs the poll.
fn execute_create_poll(
deps: DepsMut,
env: Env,
info: MessageInfo,
poll_id: String,
question: String,
options: Vec<String>,
) -> Result<Response, ContractError> {
if options.len() > 10 {
return Err(ContractError::TooManyOptions {});
}

let mut opts: Vec<(String, u64)> = vec![];
for option in options {
opts.push((option, 0));
}

let poll = Poll {
creator: info.sender,
question,
options: opts
};

POLLS.save(deps.storage, poll_id, &poll)?;

Ok(Response::new())
}

02 Execute voting function

Let's write the code for calling the execute_vote function.

These are the steps for what we are trying to achieve.

  1. load a poll and check if it exists.
  2. update a user's ballot and if they have already voted take one away from their old vote option.
  3. increment the user's new vote option.
  4. save the poll's new state as well as the ballot.
// Previous code omitted
fn execute_vote(
deps: DepsMut,
_env: Env,
info: MessageInfo,
poll_id: String,
vote: String,
) -> Result<Response, ContractError> {
let poll = POLLS.may_load(deps.storage, poll_id.clone())?;

match poll {
Some(mut poll) => { // The poll exists
BALLOTS.update(
deps.storage,
(info.sender, poll_id.clone()),
|ballot| -> StdResult<Ballot> {
match ballot {
Some(ballot) => {
// We need to revoke their old vote
// Find the position
let position_of_old_vote = poll
.options
.iter()
.position(|option| option.0 == ballot.option)
.unwrap();
// Decrement by 1
poll.options[position_of_old_vote].1 -= 1;
// Update the ballot
Ok(Ballot { option: vote.clone() })
}
None => {
// Simply add the ballot
Ok(Ballot { option: vote.clone() })
}
}
},
)?;

// Find the position of the new vote option and increment it by 1
let position = poll
.options
.iter()
.position(|option| option.0 == vote);
if position.is_none() {
return Err(ContractError::Unauthorized {});
}
let position = position.unwrap();
poll.options[position].1 += 1;

// Save the update
POLLS.save(deps.storage, poll_id, &poll)?;
Ok(Response::new())
},
None => Err(ContractError::Unauthorized {}), // The poll does not exist so we just error
}
}
// Following code omitted