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.
- 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!(),
}
}
Let's start our first implementation
Pass all the
deps
,env
, andinfo
variables to eachexecute
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!(),
}
}
- Place the following after the
execute
function and before thequery
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!()
}
- 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 {},
}
- Open
src/contract.rs
and add the following code. This adds anif
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.
- load a poll and check if it exists.
- update a user's ballot and if they have already voted take one away from their old vote option.
- increment the user's new vote option.
- 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