I must admit, I've been neglecting some items. This chapter aims to remedy them and provide a couple of other options to improve the contract.
You've probably noticed some warnings when running console commands such as cargo test
.
Let's start correcting them:
Clippy provides a linter and if you've been pushing your project to github you'll notice a workflow defined which calls this command. The workflow will probably be failing unless you've been keeping on top of this yourself.
So here's how you can run it yourself:
cargo clippy --all-targets -- --D warnings
Run that from your root repo and you'll get an output with any complaints:
error: useless use of `vec!`
--> src/contract.rs:366:42
|
366 | let mut deps = mock_dependencies(&vec![]);
| ^^^^^^^ help: you can use a slice directly: `&[]`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
error: useless use of `vec!`
--> src/contract.rs:368:37
|
368 | let info = mock_info(ADDR1, &vec![]);
| ^^^^^^^ help: you can use a slice directly: `&[]`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
error: redundant clone
--> src/contract.rs:430:59
|
430 | let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> src/contract.rs:430:55
|
430 | let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
| ^^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
error: useless use of `vec!`
--> src/contract.rs:406:42
|
406 | let mut deps = mock_dependencies(&vec![]);
| ^^^^^^^ help: you can use a slice directly: `&[]`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
error: useless use of `vec!`
--> src/contract.rs:408:37
|
408 | let info = mock_info(ADDR1, &vec![]);
| ^^^^^^^ help: you can use a slice directly: `&[]`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
error: could not compile `cw-starter` due to 5 previous errors
It shows tips on how to resolve them so I'm not going to cover that (and there's a lot). Instead, I am simply going to correct them myself. The corrected code is available in code/cw-starter
however I recommend you resolve these lints yourself and get in the habit of running cargo clippy
yourself.
Cargo also provides a formatter, cw-template
's workflows also check for this.
To format your project simply run from the root:
cargo fmt
I do this often when I'm developing and always before pushing to my Git.
After this you should be able to run all these commands without warnings:
cargo clippy --all-targets -- --D warnings
cargo fmt
cargo test
cargo wasm
cargo schema
Rust provides some of the best toolings for linting, you'd be a fool not to use it!
You may notice in our execute
code we often clone the strings as the values are "moved" in Rust terms. We can fix this by using references where we store strings in storage. For example, keys for our maps can become (in src/state.rs
):
// Previous code omitted
pub const POLLS: Map<&str, Poll> = Map::new("polls");
pub const BALLOTS: Map<(Addr, &str), Ballot> = Map::new("ballots");
These changes are visible under the code/cw-starter
part of this chapter, effectively where we previously used the String
directly we simply need to prefix it with &
for example:
// Before
let poll = POLLS.may_load(deps.storage, poll_id)?;
BALLOTS.update(
deps.storage,
(info.sender, poll_id),
|ballot| -> StdResult<Ballot> {
// Action code omitted
},
)?
// After
let poll = POLLS.may_load(deps.storage, &poll_id)?;
BALLOTS.update(
deps.storage,
(info.sender, &poll_id),
|ballot| -> StdResult<Ballot> {
// Action code omitted
},
)?
Again there are a lot of changes to be made but your IDE should error where it hasn't been updated and the fixes are simple. This should be easy for you now!
Don't forget to check clippy
and fmt
after performing these upgrades!
Now, these changes won't be reflected under code/cw-template
as these are optional and subjective.
It's down to you to now expand the functionality of your contract! Here are some ideas:
- Charge users a 1 token fee for creating a poll to prevent spam.
- Hints
- The expected token denom must be stored in the global config items.
info
has a variable calledinfo.funds
which will contain aVec<Coin>
think what we need to do to check that funds are sent and are of the correct type.- Add an error if funds are not sent!
- Investigate how we can send funds in our unit-tests, and look at the second parameter (currently an empty vec) in our
mock_info
calls. - Test for this error!
- Hints
- Add a boolean field on a poll so it can be closed. Users cannot vote on a closed poll and the program will throw an error if they attempt to.
- Hints
- We will need to modify our
Poll
type. - We will need to modify our
Vote
execute endpoint to error if a poll is closed. - We will also need to add a new endpoint allowing the creator of a poll to close it.
- The global admin stored in the config should also be able to close polls.
- Error if anyone else tries to close a poll.
- Add test cases for all use-cases listed above.
- We will need to modify our
- Hints
- When a poll is closed return the 1 token fee to the creator so it acts as a deposit and not a payment.
- Hints
- You will need to look into CosmosMsgs and the
.add_message()
method on yourResponse
. - This is getting complex, feel free to ask for help in discords!
- You will need to look into CosmosMsgs and the
- Hints
Well, we've reached the end of the first part of the contract development: next up building a dApp and providing a usable front-end.
There'll be one more chapter teaching you how to set up a local deployment to make testing your contract much easier with your front end.
I'd like to thank you all for making it this far and for the support I have received recently. If this tutorial has helped even just one person it makes it so worth it.
Thanks again all, see you in the next part :).