diff --git a/artifacts/checksums.txt b/artifacts/checksums.txt index e10eb3d..b2a0b88 100644 --- a/artifacts/checksums.txt +++ b/artifacts/checksums.txt @@ -1,5 +1,5 @@ 0406c776d73eb75dfa160be3094f857559350c8b7c203f9bfbfd144a73a7a19a nexus_airdrop.wasm b911bcadb0dfb9fcdef75f16ce20528eefcecc672da53509f6dbacc0238c5611 nexus_community.wasm -14d8e381717727fae0961ef5f1af14138b53c65a603e016c237aabde4215f7f8 nexus_governance.wasm +bc3ad8dd228d2708f44539e5f4ec982b8a5afc189888c8d12485fc98a111b7fa nexus_governance.wasm 8648559a23699a88d9c99f088373ecbbd14df807f3b5388131b64cedc769ad3f nexus_staking.wasm 1e910c3696b81b03d5ce33b7acf506527cb23ad5978cdf21d212ef481a41a7b1 nexus_vesting.wasm diff --git a/artifacts/nexus_governance.wasm b/artifacts/nexus_governance.wasm index d9c7b47..65ce677 100644 Binary files a/artifacts/nexus_governance.wasm and b/artifacts/nexus_governance.wasm differ diff --git a/contracts/governance/src/commands.rs b/contracts/governance/src/commands.rs index eba4c63..d01af4a 100644 --- a/contracts/governance/src/commands.rs +++ b/contracts/governance/src/commands.rs @@ -19,6 +19,7 @@ use crate::{ }; use cw20::Cw20ExecuteMsg; +#[allow(clippy::too_many_arguments)] pub fn update_config( deps: DepsMut, mut current_config: Config, @@ -29,6 +30,7 @@ pub fn update_config( timelock_period: Option, proposal_deposit: Option, snapshot_period: Option, + psi_nexprism_staking: Option, ) -> StdResult { if let Some(ref owner) = owner { current_config.owner = deps.api.addr_validate(owner)?; @@ -58,6 +60,14 @@ pub fn update_config( current_config.snapshot_period = snapshot_period; } + if let Some(psi_nexprism_staking) = psi_nexprism_staking { + current_config.psi_nexprism_staking = if !psi_nexprism_staking.is_empty() { + Some(deps.api.addr_validate(&psi_nexprism_staking)?) + } else { + None + } + } + store_config(deps.storage, ¤t_config)?; Ok(Response::default()) } @@ -93,7 +103,22 @@ pub fn stake_voting_tokens( store_state(deps.storage, &state)?; store_bank(deps.storage, &sender, &token_manager)?; - Ok(Response::new().add_attributes(vec![ + let mut resp = Response::new(); + + if let Some(ref psi_nexprism_staking) = config.psi_nexprism_staking { + resp = resp.add_submessage(SubMsg::new(WasmMsg::Execute { + contract_addr: psi_nexprism_staking.to_string(), + msg: to_binary(&crate::nexus_prism::ExecuteMsg::StakeOperator { + msg: crate::nexus_prism::StakeOperatorMsg::IncreaseBalance { + staker: sender.to_string(), + amount, + }, + })?, + funds: vec![], + })); + } + + Ok(resp.add_attributes(vec![ ("action", "staking"), ("sender", &sender.to_string()), ("share", &share.to_string()), @@ -288,7 +313,7 @@ pub fn end_poll(deps: DepsMut, env: Env, poll_id: u64) -> StdResult { .add_attributes(vec![ ("action", "end_poll"), ("poll_id", &poll_id.to_string()), - ("rejected_reason", &rejected_reason.to_string()), + ("rejected_reason", rejected_reason), ("passed", &passed.to_string()), ])) } @@ -557,7 +582,7 @@ pub fn withdraw_voting_tokens( state.total_share = Uint128::from(total_share - withdraw_share); store_state(deps.storage, &state)?; - Ok(Response::new() + let mut resp = Response::new() .add_message(WasmMsg::Execute { contract_addr: config.psi_token.to_string(), msg: to_binary(&Cw20ExecuteMsg::Transfer { @@ -566,11 +591,24 @@ pub fn withdraw_voting_tokens( })?, funds: vec![], }) - .add_attributes(vec![ - ("action", "withdraw"), - ("recipient", &user_address.to_string()), - ("amount", &withdraw_amount.to_string()), - ])) + .add_attribute("action", "withdraw") + .add_attribute("recipient", &user_address.to_string()) + .add_attribute("amount", &withdraw_amount.to_string()); + + if let Some(psi_nexprism_staking) = config.psi_nexprism_staking { + resp = resp.add_submessage(SubMsg::new(WasmMsg::Execute { + contract_addr: psi_nexprism_staking.to_string(), + msg: to_binary(&crate::nexus_prism::ExecuteMsg::StakeOperator { + msg: crate::nexus_prism::StakeOperatorMsg::DecreaseBalance { + staker: user_address.to_string(), + amount: Uint128::new(withdraw_amount), + }, + })?, + funds: vec![], + })); + } + + Ok(resp) } } else { Err(StdError::generic_err("Nothing staked")) @@ -590,7 +628,7 @@ fn compute_locked_balance( if poll.status != PollStatus::InProgress { // remove voter info from the poll - remove_poll_voter(storage, *poll_id, &voter); + remove_poll_voter(storage, *poll_id, voter); } poll.status == PollStatus::InProgress diff --git a/contracts/governance/src/contract.rs b/contracts/governance/src/contract.rs index 6fea1a1..95d4de8 100644 --- a/contracts/governance/src/contract.rs +++ b/contracts/governance/src/contract.rs @@ -43,6 +43,10 @@ pub fn instantiate( timelock_period: msg.timelock_period, proposal_deposit: msg.proposal_deposit, snapshot_period: msg.snapshot_period, + psi_nexprism_staking: msg + .psi_nexprism_staking + .map(|addr| deps.api.addr_validate(&addr)) + .transpose()?, }; let state = State { @@ -86,6 +90,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S timelock_period, proposal_deposit, snapshot_period, + psi_nexprism_staking, } => commands::update_config( deps, config, @@ -96,6 +101,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S timelock_period, proposal_deposit, snapshot_period, + psi_nexprism_staking, ), } } @@ -205,6 +211,10 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { } #[entry_point] -pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { +pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> StdResult { + let mut config = load_config(deps.storage)?; + config.psi_nexprism_staking = Some(deps.api.addr_validate(&msg.psi_nexprism_staking)?); + store_config(deps.storage, &config)?; + Ok(Response::default()) } diff --git a/contracts/governance/src/lib.rs b/contracts/governance/src/lib.rs index cae7ee3..02103ef 100644 --- a/contracts/governance/src/lib.rs +++ b/contracts/governance/src/lib.rs @@ -1,5 +1,6 @@ pub mod commands; pub mod contract; +pub mod nexus_prism; pub mod querier; pub mod queries; pub mod state; diff --git a/contracts/governance/src/nexus_prism.rs b/contracts/governance/src/nexus_prism.rs new file mode 100644 index 0000000..9a88be8 --- /dev/null +++ b/contracts/governance/src/nexus_prism.rs @@ -0,0 +1,16 @@ +use cosmwasm_std::Uint128; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ExecuteMsg { + StakeOperator { msg: StakeOperatorMsg }, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum StakeOperatorMsg { + IncreaseBalance { staker: String, amount: Uint128 }, + DecreaseBalance { staker: String, amount: Uint128 }, +} diff --git a/contracts/governance/src/queries.rs b/contracts/governance/src/queries.rs index 6c6989f..5109cac 100644 --- a/contracts/governance/src/queries.rs +++ b/contracts/governance/src/queries.rs @@ -26,6 +26,7 @@ pub fn query_config(deps: Deps) -> StdResult { timelock_period: config.timelock_period, proposal_deposit: config.proposal_deposit, snapshot_period: config.snapshot_period, + psi_nexprism_staking: config.psi_nexprism_staking.map(|addr| addr.to_string()), }) } diff --git a/contracts/governance/src/state.rs b/contracts/governance/src/state.rs index 2c25ca1..05ace77 100644 --- a/contracts/governance/src/state.rs +++ b/contracts/governance/src/state.rs @@ -30,6 +30,7 @@ pub struct Config { pub timelock_period: u64, pub proposal_deposit: Uint128, pub snapshot_period: u64, + pub psi_nexprism_staking: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -87,7 +88,7 @@ impl Poll { true }; - return !execute_messages_is_empty || !migration_messages_is_empty; + !execute_messages_is_empty || !migration_messages_is_empty } } diff --git a/contracts/governance/src/tests/tests.rs b/contracts/governance/src/tests/tests.rs index e9d8c24..004d15f 100644 --- a/contracts/governance/src/tests/tests.rs +++ b/contracts/governance/src/tests/tests.rs @@ -28,6 +28,7 @@ pub struct MigrateMsg { } const VOTING_TOKEN: &str = "voting_token"; +const PSI_NEXPRISM_STAKING: &str = "psi_nexprism_staking"; const TEST_CREATOR: &str = "creator"; const TEST_VOTER: &str = "voter1"; const TEST_VOTER_2: &str = "voter2"; @@ -47,6 +48,7 @@ fn mock_init(deps: &mut OwnedDeps) { timelock_period: DEFAULT_TIMELOCK_PERIOD, proposal_deposit: Uint128::new(DEFAULT_PROPOSAL_DEPOSIT), snapshot_period: DEFAULT_FIX_PERIOD, + psi_nexprism_staking: Some(PSI_NEXPRISM_STAKING.to_owned()), }; let env = mock_env(); @@ -66,6 +68,7 @@ fn mock_init(deps: &mut OwnedDeps) { timelock_period: DEFAULT_TIMELOCK_PERIOD, proposal_deposit: Uint128::new(DEFAULT_PROPOSAL_DEPOSIT), snapshot_period: DEFAULT_FIX_PERIOD, + psi_nexprism_staking: Some(Addr::unchecked(PSI_NEXPRISM_STAKING)), } ); @@ -89,6 +92,7 @@ fn mock_init(deps: &mut OwnedDeps) { timelock_period: DEFAULT_TIMELOCK_PERIOD, proposal_deposit: Uint128::new(DEFAULT_PROPOSAL_DEPOSIT), snapshot_period: DEFAULT_FIX_PERIOD, + psi_nexprism_staking: Some(Addr::unchecked(PSI_NEXPRISM_STAKING)), } ); @@ -156,6 +160,7 @@ fn fails_init_invalid_quorum() { timelock_period: DEFAULT_TIMELOCK_PERIOD, proposal_deposit: Uint128::new(DEFAULT_PROPOSAL_DEPOSIT), snapshot_period: DEFAULT_FIX_PERIOD, + psi_nexprism_staking: Some(PSI_NEXPRISM_STAKING.to_owned()), }; let res = instantiate(deps.as_mut(), env, info, msg); @@ -178,6 +183,7 @@ fn fails_init_invalid_threshold() { timelock_period: DEFAULT_TIMELOCK_PERIOD, proposal_deposit: Uint128::new(DEFAULT_PROPOSAL_DEPOSIT), snapshot_period: DEFAULT_FIX_PERIOD, + psi_nexprism_staking: Some(PSI_NEXPRISM_STAKING.to_owned()), }; let res = instantiate(deps.as_mut(), env, info, msg); @@ -1806,19 +1812,32 @@ fn happy_days_withdraw_voting_tokens() { }; let execute_res = execute(deps.as_mut(), env, info, msg.clone()).unwrap(); - let msg = execute_res.messages.get(0).expect("no message"); + let msgs = execute_res.messages; assert_eq!( - msg, - &SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: VOTING_TOKEN.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: TEST_VOTER.to_string(), - amount: Uint128::from(11u128), - }) - .unwrap(), - funds: vec![], - })) + msgs, + vec![ + SubMsg::new(WasmMsg::Execute { + contract_addr: PSI_NEXPRISM_STAKING.to_string(), + msg: to_binary(&crate::nexus_prism::ExecuteMsg::StakeOperator { + msg: crate::nexus_prism::StakeOperatorMsg::DecreaseBalance { + staker: TEST_VOTER.to_string(), + amount: Uint128::from(11u128), + }, + }) + .unwrap(), + funds: vec![], + }), + SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: VOTING_TOKEN.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: TEST_VOTER.to_string(), + amount: Uint128::from(11u128), + }) + .unwrap(), + funds: vec![], + })) + ], ); let state: State = load_state(&mut deps.storage).unwrap(); @@ -1876,19 +1895,32 @@ fn happy_days_withdraw_voting_tokens_all() { }; let execute_res = execute(deps.as_mut(), env, info, msg.clone()).unwrap(); - let msg = execute_res.messages.get(0).expect("no message"); + let msgs = execute_res.messages; assert_eq!( - msg, - &SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: VOTING_TOKEN.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: TEST_VOTER.to_string(), - amount: Uint128::from(22u128), - }) - .unwrap(), - funds: vec![], - })) + msgs, + vec![ + SubMsg::new(WasmMsg::Execute { + contract_addr: PSI_NEXPRISM_STAKING.to_string(), + msg: to_binary(&crate::nexus_prism::ExecuteMsg::StakeOperator { + msg: crate::nexus_prism::StakeOperatorMsg::DecreaseBalance { + staker: TEST_VOTER.to_string(), + amount: Uint128::from(22u128), + }, + }) + .unwrap(), + funds: vec![], + }), + SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: VOTING_TOKEN.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: TEST_VOTER.to_string(), + amount: Uint128::from(22u128), + }) + .unwrap(), + funds: vec![], + })) + ] ); let state: State = load_state(deps.as_ref().storage).unwrap(); @@ -2220,6 +2252,23 @@ fn happy_days_stake_voting_tokens() { let env = mock_env(); let info = mock_info(VOTING_TOKEN, &[]); let execute_res = execute(deps.as_mut(), env, info, msg.clone()).unwrap(); + + let msg = execute_res.messages[0].clone(); + + assert_eq!( + msg, + SubMsg::new(WasmMsg::Execute { + contract_addr: PSI_NEXPRISM_STAKING.to_string(), + msg: to_binary(&crate::nexus_prism::ExecuteMsg::StakeOperator { + msg: crate::nexus_prism::StakeOperatorMsg::IncreaseBalance { + staker: TEST_VOTER.to_string(), + amount: Uint128::from(11u128), + }, + }) + .unwrap(), + funds: vec![], + }), + ); assert_stake_tokens_result(11, 0, 11, 0, execute_res, &deps); } @@ -2456,6 +2505,7 @@ fn update_config() { timelock_period: None, proposal_deposit: None, snapshot_period: None, + psi_nexprism_staking: None, }, }; @@ -2484,6 +2534,7 @@ fn update_config() { timelock_period: Some(20000u64), proposal_deposit: Some(Uint128::new(123u128)), snapshot_period: Some(11), + psi_nexprism_staking: Some("staking".to_owned()), }, }; @@ -2500,6 +2551,7 @@ fn update_config() { assert_eq!(20000u64, config.timelock_period); assert_eq!(123u128, config.proposal_deposit.u128()); assert_eq!(11u64, config.snapshot_period); + assert_eq!(Some("staking".to_owned()), config.psi_nexprism_staking); // Unauthorzied err let env = mock_env(); @@ -2513,6 +2565,7 @@ fn update_config() { timelock_period: None, proposal_deposit: None, snapshot_period: None, + psi_nexprism_staking: None, }, }; diff --git a/packages/services/src/governance.rs b/packages/services/src/governance.rs index 5898296..0bda829 100644 --- a/packages/services/src/governance.rs +++ b/packages/services/src/governance.rs @@ -13,6 +13,7 @@ pub struct InstantiateMsg { pub timelock_period: u64, pub proposal_deposit: Uint128, pub snapshot_period: u64, + pub psi_nexprism_staking: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -66,6 +67,7 @@ pub enum GovernanceMsg { timelock_period: Option, proposal_deposit: Option, snapshot_period: Option, + psi_nexprism_staking: Option, }, } @@ -137,6 +139,7 @@ pub struct ConfigResponse { pub timelock_period: u64, pub proposal_deposit: Uint128, pub snapshot_period: u64, + pub psi_nexprism_staking: Option, } #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema)] @@ -233,4 +236,6 @@ impl fmt::Display for VoteOption { } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct MigrateMsg {} +pub struct MigrateMsg { + pub psi_nexprism_staking: String, +}