Repository: public-awesome/launchpad
Pull request #522 (2023-03-09 23:23:35)

SG-610 allow zero fee mints

linear[bot]: <p><a href="https://linear.app/stargaze/issue/SG-610/allow-free-mints">SG-610 allow free mints</a></p>
codecov[bot]: ## [Codecov](https://codecov.io/gh/public-awesome/launchpad/pull/522?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=public-awesome) Report
Patch coverage: **`6.66`**% and project coverage change: **`-1.78`** :warning:
> Comparison is base [(`0b4c88b`)](https://codecov.io/gh/public-awesome/launchpad/commit/0b4c88bf50d3f6c131932fa6d22e87bc661589b1?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=public-awesome) 76.03% compared to head [(`4d7790b`)](https://codecov.io/gh/public-awesome/launchpad/pull/522?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=public-awesome) 74.25%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #522      +/-   ##
==========================================
- Coverage   76.03%   74.25%   -1.78%     
==========================================
  Files          60       60              
  Lines        2324     2381      +57     
==========================================
+ Hits         1767     1768       +1     
- Misses        557      613      +56     
```


| [Impacted Files](https://codecov.io/gh/public-awesome/launchpad/pull/522?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=public-awesome) | Coverage Δ | |
|---|---|---|
| [.../common\_setup/setup\_minter/vending\_minter/setup.rs](https://codecov.io/gh/public-awesome/launchpad/pull/522?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=public-awesome#diff-dGVzdC1zdWl0ZS9zcmMvY29tbW9uX3NldHVwL3NldHVwX21pbnRlci92ZW5kaW5nX21pbnRlci9zZXR1cC5ycw==) | `56.52% <0.00%> (-43.48%)` | :arrow_down: |
| [test-suite/src/common\_setup/templates.rs](https://codecov.io/gh/public-awesome/launchpad/pull/522?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=public-awesome#diff-dGVzdC1zdWl0ZS9zcmMvY29tbW9uX3NldHVwL3RlbXBsYXRlcy5ycw==) | `82.22% <0.00%> (-17.78%)` | :arrow_down: |
| [contracts/vending-minter/src/contract.rs](https://codecov.io/gh/public-awesome/launchpad/pull/522?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=public-awesome#diff-Y29udHJhY3RzL3ZlbmRpbmctbWludGVyL3NyYy9jb250cmFjdC5ycw==) | `85.58% <100.00%> (+0.02%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=public-awesome). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=public-awesome)

</details>

[:umbrella: View full report in Codecov by Sentry](https://codecov.io/gh/public-awesome/launchpad/pull/522?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=public-awesome).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=public-awesome).

github-actions[bot]: <h1>Cosm-Orc Gas Usage</h1> 
 
 No gas diff larger than 0.5% 
 
 <details><summary>Raw Report for 4e4f4b829dc6547031848c0668d5affe734e7289 </summary><br/> 

| Contract | Op Name | Gas Used | Gas Wanted | File | 
 | --- | --- | --- | --- | --- |
| vending_factory | Instantiate__factory_inst | 184187 | 253164 | e2e/src/helpers/helper.rs:35 |
| vending_factory | Store__Store | 2711196 | 4043654 | e2e/src/helpers/chain.rs:78 |
| vending_factory | Execute__factory_exec_minter_inst | 34028951 | 51020213 | e2e/src/tests/factory_test.rs:98 |
| vending_factory | Execute__factory_exec_minter_inst_w_trading_time | 3849061 | 5750388 | e2e/src/tests/factory_test.rs:244 |
| base_factory | Store__Store | 2485685 | 3705387 | e2e/src/helpers/chain.rs:78 |
| base_minter | Store__Store | 6893921 | 10317681 | e2e/src/helpers/chain.rs:78 |
| sg721_base | Store__Store | 7894024 | 11817836 | e2e/src/helpers/chain.rs:78 |
| sg721_metadata_onchain | Store__Store | 8322231 | 12460146 | e2e/src/helpers/chain.rs:78 |
| sg721_nt | Store__Store | 7419797 | 11106495 | e2e/src/helpers/chain.rs:78 |
| sg_eth_airdrop | Store__Store | 3235785 | 4830537 | e2e/src/helpers/chain.rs:78 |
| sg_splits | Store__Store | 2750573 | 4102719 | e2e/src/helpers/chain.rs:78 |
| sg_whitelist | Store__Store | 3476942 | 5192268 | e2e/src/helpers/chain.rs:78 |
| vending_minter | Store__Store | 8612976 | 12896264 | e2e/src/helpers/chain.rs:78 |
| whitelist_immutable | Store__Store | 2146762 | 3197003 | e2e/src/helpers/chain.rs:78 |
| multiple_contracts | Execute__minter_batch_exec_mint_token | 18782663 | 28149761 | e2e/src/tests/factory_test.rs:164 |
| multiple_contracts | Execute__minter_batch_exec_mint_token_w_trading_time | 1943632 | 2885105 | e2e/src/tests/factory_test.rs:314 |
</details>

Patch:
diff --git a/contracts/vending-minter/src/contract.rs b/contracts/vending-minter/src/contract.rs
index 69350ed8..8552d0f0 100644
--- a/contracts/vending-minter/src/contract.rs
+++ b/contracts/vending-minter/src/contract.rs
@@ -656,11 +656,14 @@ fn _execute_mint(
         let amount = mint_price.amount - network_fee;
         let payment_address = config.extension.payment_address;
         let seller = config.extension.admin;
-        let msg = BankMsg::Send {
-            to_address: payment_address.unwrap_or(seller).to_string(),
-            amount: vec![coin(amount.u128(), mint_price.denom)],
-        };
-        res = res.add_message(msg);
+        // Sending 0 coins fails, so only send if amount is non-zero
+        if !amount.is_zero() {
+            let msg = BankMsg::Send {
+                to_address: payment_address.unwrap_or(seller).to_string(),
+                amount: vec![coin(amount.u128(), mint_price.denom)],
+            };
+            res = res.add_message(msg);
+        }
         amount
     } else {
         Uint128::zero()


Test patch:
diff --git a/test-suite/src/vending_minter/tests.rs b/test-suite/src/vending_minter/tests.rs
index 92adcd6b..88a689ce 100644
--- a/test-suite/src/vending_minter/tests.rs
+++ b/test-suite/src/vending_minter/tests.rs
@@ -4,3 +4,4 @@ mod mint_and_burn;
 mod splits;
 mod trading_time;
 mod whitelist;
+mod zero_mint_price;
diff --git a/test-suite/src/vending_minter/tests/zero_mint_price.rs b/test-suite/src/vending_minter/tests/zero_mint_price.rs
new file mode 100644
index 00000000..28c0a130
--- /dev/null
+++ b/test-suite/src/vending_minter/tests/zero_mint_price.rs
@@ -0,0 +1,109 @@
+use crate::common_setup::contract_boxes::custom_mock_app;
+use crate::common_setup::msg::MinterSetupParams;
+use crate::common_setup::setup_accounts_and_block::setup_accounts;
+use crate::common_setup::setup_accounts_and_block::setup_block_time;
+use crate::common_setup::setup_minter::common::constants::CREATION_FEE;
+use crate::common_setup::setup_minter::common::minter_params::minter_params_all;
+use crate::common_setup::setup_minter::vending_minter::mock_params::{
+    mock_create_minter_init_msg, mock_init_extension, mock_params,
+};
+use crate::common_setup::setup_minter::vending_minter::setup::vending_minter_code_ids;
+use cosmwasm_std::{coin, coins, Addr, Timestamp};
+use cw721::TokensResponse;
+use cw_multi_test::Executor;
+use sg2::msg::Sg2ExecuteMsg;
+use sg2::tests::{mock_collection_params, mock_collection_params_1};
+use sg_std::{GENESIS_MINT_START_TIME, NATIVE_DENOM};
+use vending_minter::msg::ExecuteMsg;
+
+const MINT_PRICE: u128 = 0;
+
+#[test]
+fn zero_mint_price() {
+    let num_tokens = 2;
+    let mut app = custom_mock_app();
+    let (creator, buyer) = setup_accounts(&mut app);
+    let start_time = Timestamp::from_nanos(GENESIS_MINT_START_TIME);
+    let collection_params = mock_collection_params_1(Some(start_time));
+
+    let init_msg = vending_factory::msg::VendingMinterInitMsgExtension {
+        base_token_uri: "ipfs://aldkfjads".to_string(),
+        payment_address: None,
+        start_time: Timestamp::from_nanos(GENESIS_MINT_START_TIME),
+        num_tokens,
+        mint_price: coin(MINT_PRICE, NATIVE_DENOM),
+        per_address_limit: 1,
+        whitelist: Some("invalid address".to_string()),
+    };
+
+    let minter_params = minter_params_all(num_tokens, None, None, Some(init_msg));
+    let code_ids = vending_minter_code_ids(&mut app);
+
+    let setup_params: MinterSetupParams = MinterSetupParams {
+        router: &mut app,
+        minter_admin: creator,
+        num_tokens,
+        collection_params,
+        splits_addr: minter_params.splits_addr,
+        minter_code_id: code_ids.minter_code_id,
+        factory_code_id: code_ids.factory_code_id,
+        sg721_code_id: code_ids.sg721_code_id,
+        start_time: minter_params.start_time,
+        init_msg: minter_params.init_msg,
+    };
+
+    let minter_code_id = setup_params.minter_code_id;
+    let router = setup_params.router;
+    let factory_code_id = setup_params.factory_code_id;
+    let sg721_code_id = setup_params.sg721_code_id;
+    let minter_admin = setup_params.minter_admin;
+
+    let mut params = mock_params();
+    params.code_id = minter_code_id;
+    params.min_mint_price = coin(MINT_PRICE, NATIVE_DENOM);
+
+    let factory_addr = router
+        .instantiate_contract(
+            factory_code_id,
+            minter_admin.clone(),
+            &vending_factory::msg::InstantiateMsg { params },
+            &[],
+            "factory",
+            None,
+        )
+        .unwrap();
+
+    let mut init_msg = mock_init_extension(None, None);
+    init_msg.mint_price = coin(MINT_PRICE, NATIVE_DENOM);
+    let mut msg = mock_create_minter_init_msg(mock_collection_params(), init_msg);
+    msg.collection_params.code_id = sg721_code_id;
+    msg.collection_params.info.creator = minter_admin.to_string();
+    let creation_fee = coins(CREATION_FEE, NATIVE_DENOM);
+    let msg = Sg2ExecuteMsg::CreateMinter(msg);
+
+    let res = router.execute_contract(minter_admin, factory_addr, &msg, &creation_fee);
+    assert!(res.is_ok());
+
+    setup_block_time(router, GENESIS_MINT_START_TIME + 1, None);
+
+    // Mint succeeds
+    let minter_addr = Addr::unchecked("contract1");
+    let sg721 = Addr::unchecked("contract2");
+    let mint_msg = ExecuteMsg::Mint {};
+    let res = router.execute_contract(buyer.clone(), minter_addr, &mint_msg, &[]);
+    assert!(res.is_ok());
+
+    // Buyer has 1 token
+    let res: TokensResponse = router
+        .wrap()
+        .query_wasm_smart(
+            sg721,
+            &sg721_base::msg::QueryMsg::Tokens {
+                owner: buyer.to_string(),
+                start_after: None,
+                limit: None,
+            },
+        )
+        .unwrap();
+    assert_eq!(res.tokens.len(), 1);
+}