From 809cf3d16e8e005d310decdec4118467d1a3ac1f Mon Sep 17 00:00:00 2001 From: Geoff Lee Date: Wed, 25 Aug 2021 13:29:32 +0900 Subject: [PATCH 1/3] merge poll-related functions into withdraw/stake --- contracts/mirror_gov/src/contract.rs | 132 +++++++++++++++++++++- contracts/mirror_gov/src/testing/tests.rs | 101 ++++++++++++++++- 2 files changed, 225 insertions(+), 8 deletions(-) diff --git a/contracts/mirror_gov/src/contract.rs b/contracts/mirror_gov/src/contract.rs index 9df483b1..f6d8f796 100644 --- a/contracts/mirror_gov/src/contract.rs +++ b/contracts/mirror_gov/src/contract.rs @@ -35,6 +35,8 @@ const MAX_POLLS_IN_PROGRESS: usize = 50; const POLL_EXECUTE_REPLY_ID: u64 = 1; +const MAX_CHECK_POLL_ITERATION: u32 = 3; + #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, @@ -74,7 +76,12 @@ pub fn instantiate( } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult { +pub fn execute( + mut deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> StdResult { match msg { ExecuteMsg::Receive(msg) => receive_cw20(deps, env, info, msg), ExecuteMsg::UpdateConfig { @@ -98,11 +105,21 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S voter_weight, snapshot_period, ), - ExecuteMsg::WithdrawVotingTokens { amount } => withdraw_voting_tokens(deps, info, amount), + ExecuteMsg::WithdrawVotingTokens { amount } => { + let res_poll = check_polls(deps.branch(), env)?; + let res_withdraw = withdraw_voting_tokens(deps, info, amount)?; + Ok(merge_response(res_withdraw, res_poll)) + } ExecuteMsg::WithdrawVotingRewards { poll_id } => { - withdraw_voting_rewards(deps, info, poll_id) + let res_poll = check_polls(deps.branch(), env)?; + let res_withdraw = withdraw_voting_rewards(deps, info, poll_id)?; + Ok(merge_response(res_withdraw, res_poll)) + } + ExecuteMsg::StakeVotingRewards { poll_id } => { + let res_poll = check_polls(deps.branch(), env)?; + let res_stake = stake_voting_rewards(deps, info, poll_id)?; + Ok(merge_response(res_stake, res_poll)) } - ExecuteMsg::StakeVotingRewards { poll_id } => stake_voting_rewards(deps, info, poll_id), ExecuteMsg::CastVote { poll_id, vote, @@ -652,9 +669,9 @@ pub fn snapshot_poll(deps: DepsMut, env: Env, poll_id: u64) -> StdResult config.snapshot_period { + if time_to_end > config.snapshot_period + current_seconds { return Err(StdError::generic_err("Cannot snapshot at this height")); } @@ -684,6 +701,109 @@ pub fn snapshot_poll(deps: DepsMut, env: Env, poll_id: u64) -> StdResult Response { + dest.add_submessages(src.messages) + .add_attributes(src.attributes) + .add_events(src.events) +} + +pub fn check_polls(mut deps: DepsMut, env: Env) -> StdResult { + let res_snapshot = check_snapshot_polls(deps.branch(), env.clone())?; + let res_end = check_end_polls(deps.branch(), env.clone())?; + let res_execute = check_execute_polls(deps.branch(), env.clone())?; + let mut res = merge_response(res_snapshot, res_end); + res = merge_response(res, res_execute); + Ok(res) +} + +pub fn check_execute_polls(mut deps: DepsMut, env: Env) -> StdResult { + let config: Config = config_read(deps.storage).load()?; + let polls: Vec = read_polls( + deps.storage, + Some(PollStatus::Passed), + None, + Some(MAX_CHECK_POLL_ITERATION), + None, + None, + ) + .unwrap(); + + let mut res = Response::default(); + polls + .iter() + .filter(|poll| { + (poll.end_time + config.effective_delay <= env.block.time.seconds()) + && (poll.execute_data != None) + }) + .all(|poll| { + res = merge_response( + res.clone(), + execute_poll(deps.branch(), env.clone(), poll.id).unwrap(), + ); + true + }); + + Ok(res) +} +pub fn check_snapshot_polls(mut deps: DepsMut, env: Env) -> StdResult { + let config: Config = config_read(deps.storage).load()?; + let polls: Vec = read_polls( + deps.storage, + Some(PollStatus::InProgress), + None, + Some(MAX_CHECK_POLL_ITERATION), + None, + None, + ) + .unwrap(); + + let mut res = Response::default(); + polls + .iter() + .filter(|poll| { + //time_to_end > config.snapshot_period + (poll.end_time <= config.snapshot_period + env.block.time.seconds()) + && poll.staked_amount.is_none() + }) + .all(|poll| { + res = merge_response( + res.clone(), + snapshot_poll(deps.branch(), env.clone(), poll.id).unwrap(), + ); + true + }); + + Ok(res) +} + +pub fn check_end_polls(mut deps: DepsMut, env: Env) -> StdResult { + let config: Config = config_read(deps.storage).load()?; + let polls: Vec = read_polls( + deps.storage, + Some(PollStatus::InProgress), + None, + Some(MAX_CHECK_POLL_ITERATION), + None, + None, + ) + .unwrap(); + + let mut res = Response::default(); + polls + .iter() + .filter(|poll| poll.end_time + config.effective_delay < env.block.time.seconds()) + .all(|poll| { + res = merge_response( + res.clone(), + end_poll(deps.branch(), env.clone(), poll.id).unwrap(), + ); + true + }); + + Ok(res) +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { diff --git a/contracts/mirror_gov/src/testing/tests.rs b/contracts/mirror_gov/src/testing/tests.rs index 7794a9f0..1fbfda1a 100644 --- a/contracts/mirror_gov/src/testing/tests.rs +++ b/contracts/mirror_gov/src/testing/tests.rs @@ -2243,6 +2243,13 @@ fn share_calculation_with_voter_rewards() { attr("action", "withdraw"), attr("recipient", TEST_VOTER), attr("amount", "100"), + attr("action", "snapshot_poll"), + attr("poll_id", "1"), + attr("staked_amount", "300"), + attr("action", "end_poll"), + attr("poll_id", "1"), + attr("rejected_reason", "Quorum not reached"), + attr("passed", "false"), ] ); @@ -2264,8 +2271,8 @@ fn share_calculation_with_voter_rewards() { ) .unwrap(); let stake_info: StakerResponse = from_binary(&res).unwrap(); - assert_eq!(stake_info.share, Uint128::new(100)); - assert_eq!(stake_info.balance, Uint128::new(200)); + assert_eq!(stake_info.share, Uint128::new(149)); + assert_eq!(stake_info.balance, Uint128::new(10000000200)); assert_eq!(stake_info.locked_balance, vec![]); } @@ -4457,3 +4464,93 @@ fn test_unstake_before_claiming_voting_rewards() { .load(deps.api.addr_canonicalize(TEST_VOTER).unwrap().as_slice()) .unwrap_err(); } + +#[test] +fn test_end_poll_by_other_tx() { + let mut deps = mock_dependencies(&[]); + let msg = InstantiateMsg { + mirror_token: VOTING_TOKEN.to_string(), + quorum: Decimal::percent(DEFAULT_QUORUM), + threshold: Decimal::percent(DEFAULT_THRESHOLD), + voting_period: DEFAULT_VOTING_PERIOD, + effective_delay: DEFAULT_EFFECTIVE_DELAY, + proposal_deposit: Uint128::new(DEFAULT_PROPOSAL_DEPOSIT), + voter_weight: Decimal::percent(50), // distribute 50% rewards to voters + snapshot_period: DEFAULT_SNAPSHOT_PERIOD, + }; + + let info = mock_info(TEST_CREATOR, &[]); + let _res = instantiate(deps.as_mut(), mock_env(), info, msg) + .expect("contract successfully handles InstantiateMsg"); + + let env = mock_env_height(0, 10001); + let info = mock_info(VOTING_TOKEN, &coins(2, VOTING_TOKEN)); + let msg = create_poll_msg("test".to_string(), "test".to_string(), None, None); + let execute_res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + + assert_create_poll_result( + 1, + env.block.time.plus_seconds(DEFAULT_VOTING_PERIOD).seconds(), + TEST_CREATOR, + execute_res, + deps.as_ref(), + ); + + let stake_amount = 10000000000u128; + + deps.querier.with_token_balances(&[( + &VOTING_TOKEN.to_string(), + &[( + &MOCK_CONTRACT_ADDR.to_string(), + &Uint128::new((stake_amount + DEFAULT_PROPOSAL_DEPOSIT) as u128), + )], + )]); + + let msg = ExecuteMsg::Receive(Cw20ReceiveMsg { + sender: TEST_VOTER.to_string(), + amount: Uint128::from(stake_amount), + msg: to_binary(&Cw20HookMsg::StakeVotingTokens {}).unwrap(), + }); + + let info = mock_info(VOTING_TOKEN, &[]); + let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); + + let msg = ExecuteMsg::CastVote { + poll_id: 1, + vote: VoteOption::Yes, + amount: Uint128::from(stake_amount), + }; + let env = mock_env_height(0, 10000); + let info = mock_info(TEST_VOTER, &[]); + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + deps.querier.with_token_balances(&[( + &VOTING_TOKEN.to_string(), + &[( + &MOCK_CONTRACT_ADDR.to_string(), + &Uint128::new((stake_amount + DEFAULT_PROPOSAL_DEPOSIT + 100u128) as u128), + )], + )]); + + let info = mock_info(TEST_VOTER, &[]); + let msg = ExecuteMsg::WithdrawVotingTokens { + amount: Some(Uint128::from(5u128)), + }; + + let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); + assert_eq!( + res.attributes, + vec![ + attr("action", "withdraw"), + attr("recipient", TEST_VOTER), + attr("amount", "5"), + attr("action", "snapshot_poll"), + attr("poll_id", "1"), + attr("staked_amount", "10000000100"), + attr("action", "end_poll"), + attr("poll_id", "1"), + attr("rejected_reason", ""), + attr("passed", "true"), + ] + ); +} From 96ffadaf7c89d3290547083802b33e867183b165 Mon Sep 17 00:00:00 2001 From: Geoff Lee Date: Wed, 25 Aug 2021 15:19:12 +0900 Subject: [PATCH 2/3] to pass clippy test --- contracts/mirror_gov/src/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/mirror_gov/src/contract.rs b/contracts/mirror_gov/src/contract.rs index f6d8f796..2a3e26f8 100644 --- a/contracts/mirror_gov/src/contract.rs +++ b/contracts/mirror_gov/src/contract.rs @@ -711,7 +711,7 @@ fn merge_response(dest: Response, src: Response) -> Response { pub fn check_polls(mut deps: DepsMut, env: Env) -> StdResult { let res_snapshot = check_snapshot_polls(deps.branch(), env.clone())?; let res_end = check_end_polls(deps.branch(), env.clone())?; - let res_execute = check_execute_polls(deps.branch(), env.clone())?; + let res_execute = check_execute_polls(deps.branch(), env())?; let mut res = merge_response(res_snapshot, res_end); res = merge_response(res, res_execute); Ok(res) From c57c69dced9611c5ffb504b552a6d5547b1db0b1 Mon Sep 17 00:00:00 2001 From: Geoff Lee Date: Wed, 25 Aug 2021 15:22:10 +0900 Subject: [PATCH 3/3] typo fix --- contracts/mirror_gov/src/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/mirror_gov/src/contract.rs b/contracts/mirror_gov/src/contract.rs index 2a3e26f8..a2fa76d8 100644 --- a/contracts/mirror_gov/src/contract.rs +++ b/contracts/mirror_gov/src/contract.rs @@ -711,7 +711,7 @@ fn merge_response(dest: Response, src: Response) -> Response { pub fn check_polls(mut deps: DepsMut, env: Env) -> StdResult { let res_snapshot = check_snapshot_polls(deps.branch(), env.clone())?; let res_end = check_end_polls(deps.branch(), env.clone())?; - let res_execute = check_execute_polls(deps.branch(), env())?; + let res_execute = check_execute_polls(deps.branch(), env)?; let mut res = merge_response(res_snapshot, res_end); res = merge_response(res, res_execute); Ok(res)