Repository: DA0-DA0/dao-contracts
Pull request #162 (2022-02-02 23:25:57)

cw3-flex-multisig fixes
Addresses #161 
Add the contract change, in the linked PR.
Also implement the tests they added.

JakeHartnell: Looks good! We need this for `cw3-dao` as well.
Callum-A: > Looks good! We need this for `cw3-dao` as well.

Whoops will address now

Patch:
diff --git a/contracts/cw3-dao/src/contract.rs b/contracts/cw3-dao/src/contract.rs
index c68dc7f1..ae6dad03 100644
--- a/contracts/cw3-dao/src/contract.rs
+++ b/contracts/cw3-dao/src/contract.rs
@@ -331,7 +331,7 @@ pub fn execute_execute(
     let mut prop = PROPOSALS.load(deps.storage, proposal_id)?;
     // We allow execution even after the proposal "expiration" as long as all vote come in before
     // that point. If it was approved on time, it can be executed any time.
-    if prop.status != Status::Passed {
+    if prop.current_status(&env.block) != Status::Passed {
         return Err(ContractError::WrongExecuteStatus {});
     }
 
diff --git a/contracts/cw3-multisig/src/contract.rs b/contracts/cw3-multisig/src/contract.rs
index 053a0d12..4493ad25 100644
--- a/contracts/cw3-multisig/src/contract.rs
+++ b/contracts/cw3-multisig/src/contract.rs
@@ -256,7 +256,7 @@ pub fn execute_vote(
 
 pub fn execute_execute(
     deps: DepsMut,
-    _env: Env,
+    env: Env,
     info: MessageInfo,
     proposal_id: u64,
 ) -> Result<Response, ContractError> {
@@ -265,7 +265,7 @@ pub fn execute_execute(
     let mut prop = PROPOSALS.load(deps.storage, proposal_id)?;
     // we allow execution even after the proposal "expiration" as long as all vote come in before
     // that point. If it was approved on time, it can be executed any time.
-    if prop.status != Status::Passed {
+    if prop.current_status(&env.block) != Status::Passed {
         return Err(ContractError::WrongExecuteStatus {});
     }
 


Test patch:
diff --git a/contracts/cw3-dao/src/tests.rs b/contracts/cw3-dao/src/tests.rs
index d0133db6..bcdff680 100644
--- a/contracts/cw3-dao/src/tests.rs
+++ b/contracts/cw3-dao/src/tests.rs
@@ -1202,9 +1202,101 @@ fn test_execute_works() {
 
     // In passing: Try to close Executed fails
     let err = app
-        .execute_contract(Addr::unchecked(OWNER), dao_addr, &closing, &[])
+        .execute_contract(Addr::unchecked(OWNER), dao_addr.clone(), &closing, &[])
         .unwrap_err();
     assert_eq!(ContractError::WrongCloseStatus {}, err.downcast().unwrap());
+
+    // Trying to execute something that was already executed fails
+    let err = app
+        .execute_contract(Addr::unchecked(SOMEBODY), dao_addr, &execution, &[])
+        .unwrap_err();
+    assert_eq!(
+        ContractError::WrongExecuteStatus {},
+        err.downcast().unwrap()
+    )
+}
+
+#[test]
+fn proposal_pass_on_expiration() {
+    let mut app = mock_app();
+
+    let voting_period = Duration::Time(2000000);
+    let threshold = Threshold::ThresholdQuorum {
+        threshold: Decimal::percent(51),
+        quorum: Decimal::percent(1),
+    };
+    let (dao_addr, _, _) = setup_test_case(
+        &mut app,
+        threshold,
+        voting_period,
+        coins(10, NATIVE_TOKEN_DENOM),
+        None,
+        None,
+    );
+
+    // Check balance to ensure we can prop
+    let contract_bal = app
+        .wrap()
+        .query_balance(&dao_addr, NATIVE_TOKEN_DENOM)
+        .unwrap();
+    assert_eq!(contract_bal, coin(10, NATIVE_TOKEN_DENOM));
+
+    let prop = pay_somebody_proposal();
+    let res = app
+        .execute_contract(Addr::unchecked(OWNER), dao_addr.clone(), &prop, &[])
+        .unwrap();
+
+    // Get prop id
+    let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
+
+    // Vote it, so it passes after voting period is over
+    let vote = ExecuteMsg::Vote(VoteMsg {
+        proposal_id,
+        vote: Vote::Yes,
+    });
+    let res = app
+        .execute_contract(Addr::unchecked(VOTER3), dao_addr.clone(), &vote, &[])
+        .unwrap();
+    assert_eq!(
+        res.custom_attrs(1),
+        [
+            ("action", "vote"),
+            ("sender", VOTER3),
+            ("proposal_id", proposal_id.to_string().as_str()),
+            ("status", "Open")
+        ]
+    );
+
+    // Wait until proposal period is done
+    app.update_block(|block| {
+        block.time = block.time.plus_seconds(2000000);
+        block.height = std::cmp::max(1, 2000000 / 5);
+    });
+
+    // Proposal should now be passed
+    let prop: ProposalResponse = app
+        .wrap()
+        .query_wasm_smart(&dao_addr, &QueryMsg::Proposal { proposal_id })
+        .unwrap();
+    assert_eq!(prop.status, Status::Passed);
+
+    // Now execute
+    let res = app
+        .execute_contract(
+            Addr::unchecked(SOMEBODY),
+            dao_addr,
+            &ExecuteMsg::Execute { proposal_id },
+            &[],
+        )
+        .unwrap();
+    assert_eq!(
+        res.custom_attrs(1),
+        [
+            ("action", "execute"),
+            ("sender", SOMEBODY),
+            ("proposal_id", proposal_id.to_string().as_str())
+        ]
+    );
 }
 
 #[test]
diff --git a/contracts/cw3-multisig/src/tests.rs b/contracts/cw3-multisig/src/tests.rs
index f18f5ff4..1bfadf90 100644
--- a/contracts/cw3-multisig/src/tests.rs
+++ b/contracts/cw3-multisig/src/tests.rs
@@ -1228,9 +1228,98 @@ fn test_execute_works() {
 
     // In passing: Try to close Executed fails
     let err = app
-        .execute_contract(Addr::unchecked(OWNER), multisig_addr, &closing, &[])
+        .execute_contract(Addr::unchecked(OWNER), multisig_addr.clone(), &closing, &[])
         .unwrap_err();
     assert_eq!(ContractError::WrongCloseStatus {}, err.downcast().unwrap());
+
+    // Trying to execute something that was already executed fails
+    let err = app
+        .execute_contract(Addr::unchecked(SOMEBODY), multisig_addr, &execution, &[])
+        .unwrap_err();
+    assert_eq!(
+        ContractError::WrongExecuteStatus {},
+        err.downcast().unwrap()
+    )
+}
+
+#[test]
+fn proposal_pass_on_expiration() {
+    let init_funds = coins(10, "BTC");
+    let mut app = mock_app(&init_funds);
+
+    let threshold = Threshold::ThresholdQuorum {
+        threshold: Decimal::percent(51),
+        quorum: Decimal::percent(1),
+    };
+    let voting_period = 2000000;
+    let (multsig_addr, _) = setup_test_case(
+        &mut app,
+        threshold,
+        Duration::Time(voting_period),
+        init_funds,
+        true,
+    );
+
+    // Check balance to ensure we can prop
+    let contract_bal = app.wrap().query_balance(&multsig_addr, "BTC").unwrap();
+    assert_eq!(contract_bal, coin(10, "BTC"));
+
+    let prop = pay_somebody_proposal();
+    let res = app
+        .execute_contract(Addr::unchecked(OWNER), multsig_addr.clone(), &prop, &[])
+        .unwrap();
+
+    // Get prop id
+    let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
+
+    // Vote it, so it passes after voting period is over
+    let vote = ExecuteMsg::Vote {
+        proposal_id,
+        vote: Vote::Yes,
+    };
+    let res = app
+        .execute_contract(Addr::unchecked(VOTER3), multsig_addr.clone(), &vote, &[])
+        .unwrap();
+    assert_eq!(
+        res.custom_attrs(1),
+        [
+            ("action", "vote"),
+            ("sender", VOTER3),
+            ("proposal_id", proposal_id.to_string().as_str()),
+            ("status", "Open")
+        ]
+    );
+
+    // Wait until proposal period is done
+    app.update_block(|block| {
+        block.time = block.time.plus_seconds(voting_period);
+        block.height = std::cmp::max(1, voting_period / 5);
+    });
+
+    // Proposal should now be passed
+    let prop: ProposalResponse = app
+        .wrap()
+        .query_wasm_smart(&multsig_addr, &QueryMsg::Proposal { proposal_id })
+        .unwrap();
+    assert_eq!(prop.status, Status::Passed);
+
+    // Now execute
+    let res = app
+        .execute_contract(
+            Addr::unchecked(SOMEBODY),
+            multsig_addr,
+            &ExecuteMsg::Execute { proposal_id },
+            &[],
+        )
+        .unwrap();
+    assert_eq!(
+        res.custom_attrs(1),
+        [
+            ("action", "execute"),
+            ("sender", SOMEBODY),
+            ("proposal_id", proposal_id.to_string().as_str())
+        ]
+    );
 }
 
 #[test]