From 058d01c5d7992a80a0e1cd3015a9e17032d30b1d Mon Sep 17 00:00:00 2001 From: bragaz Date: Wed, 18 Nov 2020 16:19:52 +0100 Subject: [PATCH] fixed other contract bugs finished to fix integration tests --- README.md | 101 +++++++----------------------------------- src/contract.rs | 19 ++++---- src/contract_tests.rs | 65 ++++++++++++++------------- src/state.rs | 2 +- src/state_tests.rs | 2 +- tests/integration.rs | 50 ++++++++++++++------- 6 files changed, 96 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index 4806b6d..d2be3df 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,21 @@ -# CosmWasm Starter Pack +# Bonsai contract + +This is a contract that I made for study purposes of the cosmWASM framework. +The idea behind it is a bonsai 🌳 shop platform, nothing serious, probably it contains some bugs too (🐜). + +CosmWASM allows you to create, compile and build smart contracts on a +cosmosSDK based blockchain. -This is a template to build smart contracts in Rust to run inside a -[Cosmos SDK](https://github.com/cosmos/cosmos-sdk) module on all chains that enable it. To understand the framework better, please read the overview in the [cosmwasm repo](https://github.com/CosmWasm/cosmwasm/blob/master/README.md), and dig into the [cosmwasm docs](https://www.cosmwasm.com). -This assumes you understand the theory and just want to get coding. - -## Creating a new repo from template - -Assuming you have a recent version of rust and cargo installed (via [rustup](https://rustup.rs/)), -then the following should get you a new repo to start a contract: - -First, install -[cargo-generate](https://github.com/ashleygwilliams/cargo-generate). -Unless you did that before, run this line now: - -```sh -cargo install cargo-generate --features vendored-openssl -``` - -Now, use it to create your new contract. -Go to the folder in which you want to place it and run: - -**0.10 (latest)** - -```sh -cargo generate --git https://github.com/CosmWasm/cosmwasm-template.git --name PROJECT_NAME -``` - -**0.9** - -```sh -cargo generate --git https://github.com/CosmWasm/cosmwasm-template.git --branch 0.9 --name PROJECT_NAME -``` - -**0.8** - -```sh -cargo generate --git https://github.com/CosmWasm/cosmwasm-template.git --branch 0.8 --name PROJECT_NAME -``` - -You will now have a new folder called `PROJECT_NAME` (I hope you changed that to something else) -containing a simple working contract and build system that you can customize. - -## Create a Repo - -After generating, you have a initialized local git repo, but no commits, and no remote. -Go to a server (eg. github) and create a new upstream repo (called `YOUR-GIT-URL` below). -Then run the following: - -```sh -# this is needed to create a valid Cargo.lock file (see below) -cargo check -git checkout -b master # in case you generate from non-master -git add . -git commit -m 'Initial Commit' -git remote add origin YOUR-GIT-URL -git push -u origin master -``` - -## CI Support - -We have template configurations for both [GitHub Actions](.github/workflows/Basic.yml) -and [Circle CI](.circleci/config.yml) in the generated project, so you can -get up and running with CI right away. - -One note is that the CI runs all `cargo` commands -with `--locked` to ensure it uses the exact same versions as you have locally. This also means -you must have an up-to-date `Cargo.lock` file, which is not auto-generated. -The first time you set up the project (or after adding any dep), you should ensure the -`Cargo.lock` file is updated, so the CI will test properly. This can be done simply by -running `cargo check` or `cargo unit-test`. - -## Using your project - -Once you have your custom repo, you should check out [Developing](./Developing.md) to explain -more on how to run tests and develop code. Or go through the -[online tutorial](https://www.cosmwasm.com/docs/getting-started/intro) to get a better feel -of how to develop. - -[Publishing](./Publishing.md) contains useful information on how to publish your contract -to the world, once you are ready to deploy it on a running blockchain. And -[Importing](./Importing.md) contains information about pulling in other contracts or crates -that have been published. -Please replace this README file with information about your specific project. You can keep -the `Developing.md` and `Publishing.md` files as useful referenced, but please set some -proper description in the README. +# How to try it +1. [Install the requested components and set up the environment](https://docs.cosmwasm.com/getting-started/installation.html). +2. Clone the project: from your CLI `git clone https://github.com/bragaz/wasm-test-contract.git` +3. Move inside the project folder: `cd ../wasm-test-contract` +4. Run the docker command to compile the contract: `docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/rust-optimizer:0.10.4` +5. Inside the contract `helper.ts` file there is an example of how to try out the contract diff --git a/src/contract.rs b/src/contract.rs index dfba7a3..9b104b8 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -23,7 +23,7 @@ pub fn init( msg: InitMsg, ) -> Result { // set_contract_version(&mut deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - let bonsai_list = BonsaiList::grow_bonsais(msg.number, env.block.height, msg.price.clone()); + let bonsai_list = BonsaiList::grow_bonsais(msg.number, env.block.height, msg.price); bonsai_store(&mut deps.storage).save(&bonsai_list)?; let mut res = InitResponse::default(); res.attributes = vec![attr("action", "grown_bonsais")]; @@ -105,12 +105,16 @@ pub fn handle_buy_bonsai( }; // check if the gardener has enough funds to buy the bonsai - let denom = deps.querier.query_bonded_denom()?; - let balance = deps.querier.query_balance(&info.sender, &denom.as_str())?; - deps.api.debug(info.sender.clone().as_str()); - if balance.amount < bonsai.price.amount { - deps.api.debug(balance.amount.clone().to_string().as_str()); - deps.api.debug(bonsai.price.amount.clone().to_string().as_str()); + let sent_funds = info.sent_funds.first().ok_or_else(|| { + MyCustomError::Std(StdError::generic_err("No funds to complete the purchase")) + })?; + if sent_funds.denom == bonsai.price.denom { + if sent_funds.amount < bonsai.price.amount { + return Err(MyCustomError::Std(StdError::generic_err( + "Insufficient funds to buy the bonsai", + ))); + } + } else { return Err(MyCustomError::Std(StdError::generic_err( "Insufficient funds to buy the bonsai", ))); @@ -124,7 +128,6 @@ pub fn handle_buy_bonsai( let canonical_addr = &deps.api.canonical_address(&info.sender)?; // todo check if it's possible to use may_update - gardeners_store(&mut deps.storage).load(canonical_addr.as_slice())?; gardeners_store(&mut deps.storage).update::<_, StdError>( canonical_addr.as_slice(), |gardener| { diff --git a/src/contract_tests.rs b/src/contract_tests.rs index d78690c..89e1918 100644 --- a/src/contract_tests.rs +++ b/src/contract_tests.rs @@ -4,12 +4,13 @@ use crate::state::{bonsai_store_readonly, gardeners_store, Bonsai, Gardener}; use assert::equal; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockQuerier}; use cosmwasm_std::{ - attr, coin, Api, BankMsg, Coin, Decimal, Env, Extern, HandleResponse, HumanAddr, Querier, - Storage, Validator, + attr, coin, coins, Api, BankMsg, Coin, Decimal, Env, Extern, HandleResponse, HumanAddr, + MessageInfo, Querier, Storage, Validator, }; use rand::seq::SliceRandom; const DEFAULT_VALIDATOR: &str = "default-validator"; +const BOND_DENOM: &str = "bonsai"; // Mock validator constructor fn sample_validator>(addr: U) -> Validator { @@ -42,6 +43,7 @@ fn set_balance(querier: &mut MockQuerier, addr: HumanAddr, balance: Vec) { fn setup_test( deps: &mut Extern, env: &Env, + info: MessageInfo, bonsai_price: Coin, bonsai_number: u64, ) { @@ -49,8 +51,7 @@ fn setup_test( price: bonsai_price, number: bonsai_number, }; - let sender_addr = HumanAddr::from("addr0001"); - init(deps, env.clone(), mock_info(&sender_addr, &[]), init_msg).unwrap(); + init(deps, env.clone(), info, init_msg).unwrap(); } // return a random bonsai id @@ -89,11 +90,11 @@ fn test_become_gardener_works() { let mut deps = mock_dependencies(&[]); let sender_addr = HumanAddr::from("addr0001"); - let bond_denom = "bonsai"; - let bonsai_price = coin(10, bond_denom); + let bonsai_price = coin(10, BOND_DENOM); let bonsai_height = 100; let env = mock_env_height(bonsai_height); - setup_test(&mut deps, &env, bonsai_price.clone(), 10); + let info = mock_info(sender_addr.clone(), &coins(25, BOND_DENOM)); + setup_test(&mut deps, &env, info.clone(), bonsai_price.clone(), 10); let mut exp_res = HandleResponse::default(); exp_res.attributes = vec![ @@ -104,7 +105,7 @@ fn test_become_gardener_works() { let msg = HandleMsg::BecomeGardener { name: String::from("leo"), }; - let res = handle(&mut deps, env.clone(), mock_info(&sender_addr, &[]), msg); + let res = handle(&mut deps, env.clone(), info.clone(), msg); // verify it not fails assert!(res.is_ok()); @@ -117,19 +118,18 @@ fn test_buy_bonsai_works() { let mut deps = mock_dependencies(&[]); let sender_addr = HumanAddr::from("addr0001"); - let bond_denom = "bonsai"; - let bonsai_price = coin(10, bond_denom); + let bonsai_price = coin(10, BOND_DENOM); let bonsai_height = 100; let env = mock_env_height(bonsai_height); - let info = mock_info(sender_addr, &[]); + let info = mock_info(sender_addr.clone(), &coins(25, BOND_DENOM)); // setup test environment - setup_test(&mut deps, &env, bonsai_price.clone(), 10); - set_validator(&mut deps.querier, bond_denom); + setup_test(&mut deps, &env, info.clone(), bonsai_price.clone(), 10); + set_validator(&mut deps.querier, BOND_DENOM); set_balance( &mut deps.querier, info.sender.clone(), - vec![coin(1000, bond_denom)], + vec![coin(1000, BOND_DENOM)], ); let canonical_addr = &deps.api.canonical_address(&info.sender.clone()).unwrap(); @@ -155,7 +155,7 @@ fn test_buy_bonsai_works() { let msg = HandleMsg::BuyBonsai { b_id: bonsai_id }; - let res = handle(&mut deps, env.clone(), mock_info(&info.sender, &[]), msg); + let res = handle(&mut deps, env.clone(), info, msg); assert!(res.is_ok()); assert_eq!(exp_res, res.unwrap()) @@ -167,19 +167,18 @@ fn test_sell_bonsai_works() { let sender_addr = HumanAddr::from("addr0001"); let buyer_addr = HumanAddr::from("addr0002"); - let bond_denom = "bonsai"; - let bonsai_price = coin(10, bond_denom); + let bonsai_price = coin(10, BOND_DENOM); let bonsai_height = 100; let env = mock_env_height(bonsai_height); - let info = mock_info(sender_addr, &[]); + let info = mock_info(sender_addr.clone(), &coins(25, BOND_DENOM)); // setup test environment - setup_test(&mut deps, &env, bonsai_price.clone(), 10); - set_validator(&mut deps.querier, bond_denom); + setup_test(&mut deps, &env, info.clone(), bonsai_price.clone(), 10); + set_validator(&mut deps.querier, BOND_DENOM); set_balance( &mut deps.querier, buyer_addr.clone(), - vec![coin(1000, bond_denom)], + vec![coin(1000, BOND_DENOM)], ); let bonsai = query_bonsais(&deps) @@ -228,17 +227,16 @@ fn test_cut_bonsai_works() { let mut deps = mock_dependencies(&[]); let sender_addr = HumanAddr::from("addr0001"); - let bond_denom = "bonsai"; - let bonsai_price = coin(10, bond_denom); + let bonsai_price = coin(10, BOND_DENOM); let bonsai_height = 100; let env = mock_env_height(bonsai_height); - let info = mock_info(sender_addr, &[]); + let info = mock_info(sender_addr.clone(), &coins(25, BOND_DENOM)); // setup test environment - setup_test(&mut deps, &env, bonsai_price.clone(), 10); + setup_test(&mut deps, &env, info.clone(), bonsai_price.clone(), 10); let canonical_addr = &deps.api.canonical_address(&info.sender.clone()).unwrap(); - let bonsai = Bonsai::new(10,bonsai_height, bonsai_price); + let bonsai = Bonsai::new(10, bonsai_height, bonsai_price); let gardener = Gardener::new( "leo".to_string(), canonical_addr.clone(), @@ -272,8 +270,11 @@ fn query_bonsais_works() { let mut deps = mock_dependencies(&[]); let bonsai_price = coin(10, "bonsai"); let bonsai_height = 100; + let sender_addr = HumanAddr::from("addr0001"); + let env = mock_env_height(bonsai_height); - setup_test(&mut deps, &env, bonsai_price.clone(), 10); + let info = mock_info(sender_addr.clone(), &coins(25, BOND_DENOM)); + setup_test(&mut deps, &env, info.clone(), bonsai_price.clone(), 10); let bonsais = query_bonsais(&deps).unwrap(); @@ -286,11 +287,12 @@ fn query_gardener_works() { let sender_addr = HumanAddr::from("addr0001"); let bonsai_price = coin(10, "bonsai"); let bonsai_height = 100; + let info = mock_info(sender_addr.clone(), &coins(25, BOND_DENOM)); let env = mock_env_height(bonsai_height); - setup_test(&mut deps, &env, bonsai_price.clone(), 10); + setup_test(&mut deps, &env, info.clone(), bonsai_price.clone(), 10); - let bonsai = Bonsai::new(10,bonsai_height, bonsai_price); + let bonsai = Bonsai::new(10, bonsai_height, bonsai_price); let canonical_addr = &deps.api.canonical_address(&sender_addr).unwrap(); let gardener = Gardener::new("leo".to_string(), canonical_addr.clone(), vec![bonsai]); @@ -310,9 +312,10 @@ fn query_all_gardeners_works() { let bonsai_height = 100; let env = mock_env_height(bonsai_height); - setup_test(&mut deps, &env, bonsai_price.clone(), 10); + let info = mock_info(sender_addr.clone(), &coins(25, BOND_DENOM)); + setup_test(&mut deps, &env, info.clone(), bonsai_price.clone(), 10); - let bonsai = Bonsai::new(10,bonsai_height, bonsai_price); + let bonsai = Bonsai::new(10, bonsai_height, bonsai_price); let canonical_addr = &deps.api.canonical_address(&sender_addr).unwrap(); let other_addr = HumanAddr::from("addr0002"); let other_addr = &deps.api.canonical_address(&other_addr).unwrap(); diff --git a/src/state.rs b/src/state.rs index 36ae4d8..0131b26 100644 --- a/src/state.rs +++ b/src/state.rs @@ -43,7 +43,7 @@ impl BonsaiList { bonsais.push(Bonsai::new(i, birth_date, price.clone())); i += 1; } - BonsaiList{ bonsais } + BonsaiList { bonsais } } } diff --git a/src/state_tests.rs b/src/state_tests.rs index d9da6a7..0642832 100644 --- a/src/state_tests.rs +++ b/src/state_tests.rs @@ -10,7 +10,7 @@ fn new_bonsai() { price: coin(145, "testCoin"), }; - let cur_bonsai = Bonsai::new(1,100, exp_bonsai.price.clone()); + let cur_bonsai = Bonsai::new(1, 100, exp_bonsai.price.clone()); exp_bonsai.id = cur_bonsai.id.clone(); diff --git a/tests/integration.rs b/tests/integration.rs index d30cdfb..5617562 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -17,13 +17,16 @@ //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) use cosmwasm_std::{ - attr, coin, coins, from_binary, from_slice, BankMsg, Coin, Env, - HandleResponse, HumanAddr, InitResponse, MessageInfo, QueryResponse, + attr, coin, coins, from_binary, from_slice, BankMsg, Coin, Env, HandleResponse, HumanAddr, + InitResponse, MessageInfo, QueryResponse, }; use cosmwasm_storage::to_length_prefixed; -use cosmwasm_vm::testing::{handle, init, mock_env, mock_info, mock_instance, query, MockApi, MockQuerier, MockStorage, mock_instance_with_balances}; +use cosmwasm_vm::testing::{ + handle, init, mock_env, mock_info, mock_instance, mock_instance_with_balances, query, MockApi, + MockQuerier, MockStorage, +}; use cosmwasm_vm::{Instance, Storage}; -use my_first_contract::msg::{HandleMsg, InitMsg, QueryMsg}; +use my_first_contract::msg::{AllGardenersResponse, HandleMsg, InitMsg, QueryMsg}; use my_first_contract::state::{BonsaiList, Gardener, BONSAI_KEY}; use rand::seq::SliceRandom; @@ -63,13 +66,23 @@ fn get_random_bonsai_id(deps: &mut Instance) rand_bonsai.id } -fn become_gardener(name: String, info: MessageInfo, env: Env, deps: &mut Instance) -> HandleResponse { +fn become_gardener( + name: String, + info: MessageInfo, + env: Env, + deps: &mut Instance, +) -> HandleResponse { let msg = HandleMsg::BecomeGardener { name }; let res: HandleResponse = handle(deps, env.clone(), info, msg).unwrap(); res } -fn buy_bonsai(bonsai_id: u64, info: MessageInfo, env: Env, deps: &mut Instance) -> HandleResponse { +fn buy_bonsai( + bonsai_id: u64, + info: MessageInfo, + env: Env, + deps: &mut Instance, +) -> HandleResponse { let msg = HandleMsg::BuyBonsai { b_id: bonsai_id }; let res: HandleResponse = handle(deps, env, info, msg).unwrap(); res @@ -141,8 +154,8 @@ fn test_buy_bonsai_works() { let sender_addr = HumanAddr::from("addr0001"); let bonsai_price = coin(10, BOND_DENOM); - let mut deps = mock_instance_with_balances(WASM, - &[(&sender_addr.clone(), &coins(1000, BOND_DENOM))]); + let mut deps = + mock_instance_with_balances(WASM, &[(&sender_addr.clone(), &coins(1000, BOND_DENOM))]); let env = mock_env_height(100); let info = mock_info(sender_addr, &coins(15, BOND_DENOM)); @@ -151,7 +164,6 @@ fn test_buy_bonsai_works() { let _res = become_gardener("leo".to_string(), info.clone(), env.clone(), &mut deps); - let bonsai_id = get_random_bonsai_id(&mut deps); let exp_res = HandleResponse { @@ -184,9 +196,9 @@ fn test_sell_bonsai_works() { let sender_addr = HumanAddr::from("addr0001"); let buyer_addr = HumanAddr::from("addr0002"); - let bonsai_price = coin(10, BOND_DENOM); + let bonsai_price = coin(0, BOND_DENOM); let env = mock_env_height(100); - let info = mock_info(sender_addr, &coins(1000, BOND_DENOM)); + let info = mock_info(sender_addr.clone(), &coins(1000, BOND_DENOM)); // setup test environment setup_test(&mut deps, &env, info.clone(), bonsai_price.clone(), 10); @@ -207,14 +219,20 @@ fn test_sell_bonsai_works() { let query_res: QueryResponse = query(&mut deps, env.clone(), QueryMsg::GetGardeners {}).unwrap(); - let gardeners: Vec = from_binary(&query_res).unwrap(); - assert_eq!(2, gardeners.len()); + let all_gardeners_result: AllGardenersResponse = from_binary(&query_res).unwrap(); + assert_eq!(2, all_gardeners_result.gardeners.len()); let msg = HandleMsg::SellBonsai { recipient: buyer_addr.clone(), b_id: bonsai_id, }; - let res: HandleResponse = handle(&mut deps, env.clone(), info.clone(), msg).unwrap(); + let res: HandleResponse = handle( + &mut deps, + env.clone(), + mock_info(sender_addr.clone(), &coins(1000, BOND_DENOM)), + msg, + ) + .unwrap(); let mut exp_res = HandleResponse::default(); exp_res.attributes = vec![ @@ -246,9 +264,7 @@ fn test_cut_bonsai_works() { let _res = buy_bonsai(bonsai_id, info.clone(), env.clone(), &mut deps); - let msg = HandleMsg::CutBonsai { - b_id: bonsai_id, - }; + let msg = HandleMsg::CutBonsai { b_id: bonsai_id }; let res: HandleResponse = handle(&mut deps, env.clone(), info.clone(), msg).unwrap();