Universal compound rewards message that anyone can call (#1387)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
test-all: test cargo-test-expensive
|
||||
test: build clippy-all cargo-test wasm fmt
|
||||
test-all: test cargo-test-expensive
|
||||
no-clippy: build cargo-test wasm fmt
|
||||
happy: fmt clippy-happy test
|
||||
clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet clippy-all-connect
|
||||
|
||||
@@ -1006,6 +1006,29 @@ impl<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
#[execute("mixnet")]
|
||||
fn _compound_reward(
|
||||
&self,
|
||||
operator: Option<String>,
|
||||
delegator: Option<String>,
|
||||
mix_identity: Option<IdentityKey>,
|
||||
proxy: Option<String>,
|
||||
fee: Option<Fee>,
|
||||
) -> (ExecuteMsg, Option<Fee>)
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
(
|
||||
ExecuteMsg::CompoundReward {
|
||||
operator,
|
||||
delegator,
|
||||
mix_identity,
|
||||
proxy,
|
||||
},
|
||||
fee,
|
||||
)
|
||||
}
|
||||
|
||||
#[execute("mixnet")]
|
||||
fn _compound_operator_reward(&self, fee: Option<Fee>) -> (ExecuteMsg, Option<Fee>)
|
||||
where
|
||||
|
||||
@@ -33,6 +33,12 @@ pub enum ExecuteMsg {
|
||||
CompoundDelegatorReward {
|
||||
mix_identity: IdentityKey,
|
||||
},
|
||||
CompoundReward {
|
||||
operator: Option<String>,
|
||||
delegator: Option<String>,
|
||||
mix_identity: Option<IdentityKey>,
|
||||
proxy: Option<String>,
|
||||
},
|
||||
BondMixnode {
|
||||
mix_node: MixNode,
|
||||
owner_signature: String,
|
||||
|
||||
@@ -112,6 +112,19 @@ pub fn execute(
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::CompoundReward {
|
||||
operator,
|
||||
delegator,
|
||||
mix_identity,
|
||||
proxy,
|
||||
} => crate::rewards::transactions::try_compound_reward(
|
||||
deps,
|
||||
env,
|
||||
operator,
|
||||
delegator,
|
||||
mix_identity,
|
||||
proxy,
|
||||
),
|
||||
ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
|
||||
try_update_rewarding_validator_address(deps, info, address)
|
||||
}
|
||||
@@ -280,7 +293,7 @@ pub fn execute(
|
||||
)
|
||||
}
|
||||
ExecuteMsg::ReconcileDelegations {} => {
|
||||
crate::delegations::transactions::try_reconcile_all_delegation_events(deps, info)
|
||||
crate::delegations::transactions::try_reconcile_all_delegation_events(deps)
|
||||
}
|
||||
ExecuteMsg::CheckpointMixnodes {} => {
|
||||
crate::mixnodes::transactions::try_checkpoint_mixnodes(
|
||||
|
||||
@@ -19,16 +19,7 @@ use mixnet_contract_common::{Delegation, IdentityKey};
|
||||
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
|
||||
use vesting_contract_common::one_ucoin;
|
||||
|
||||
pub fn try_reconcile_all_delegation_events(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
) -> Result<Response, ContractError> {
|
||||
let state = mixnet_params_storage::CONTRACT_STATE.load(deps.storage)?;
|
||||
// check if this is executed by the permitted validator, if not reject the transaction
|
||||
if info.sender != state.rewarding_validator_address {
|
||||
return Err(ContractError::Unauthorized);
|
||||
}
|
||||
|
||||
pub fn try_reconcile_all_delegation_events(deps: DepsMut<'_>) -> Result<Response, ContractError> {
|
||||
_try_reconcile_all_delegation_events(deps.storage, deps.api)
|
||||
}
|
||||
|
||||
@@ -1084,179 +1075,13 @@ mod tests {
|
||||
|
||||
#[cfg(test)]
|
||||
mod removing_mix_stake_delegation {
|
||||
use super::*;
|
||||
use crate::support::tests;
|
||||
use cosmwasm_std::coin;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
use crate::support::tests;
|
||||
|
||||
use super::*;
|
||||
|
||||
// TODO: Probably delete due to reconciliation logic
|
||||
//#[ignore]
|
||||
//#[test]
|
||||
//fn fails_if_delegation_never_existed() {
|
||||
// let mut deps = test_helpers::init_contract();
|
||||
// let env = mock_env();
|
||||
// let mixnode_owner = "bob";
|
||||
// let identity = test_helpers::add_mixnode(
|
||||
// mixnode_owner,
|
||||
// tests::fixtures::good_mixnode_pledge(),
|
||||
// deps.as_mut(),
|
||||
// );
|
||||
// let delegation_owner = Addr::unchecked("sender");
|
||||
// assert_eq!(
|
||||
// Err(ContractError::NoMixnodeDelegationFound {
|
||||
// identity: identity.clone(),
|
||||
// address: delegation_owner.to_string(),
|
||||
// }),
|
||||
// try_remove_delegation_from_mixnode(
|
||||
// deps.as_mut(),
|
||||
// env,
|
||||
// mock_info(delegation_owner.as_str(), &[]),
|
||||
// identity,
|
||||
// )
|
||||
// );
|
||||
//}
|
||||
|
||||
// TODO: Update to work with reconciliation
|
||||
//#[ignore]
|
||||
//#[test]
|
||||
//fn succeeds_if_delegation_existed() {
|
||||
// let mut deps = test_helpers::init_contract();
|
||||
// let mixnode_owner = "bob";
|
||||
// let env = mock_env();
|
||||
// let identity = test_helpers::add_mixnode(
|
||||
// mixnode_owner,
|
||||
// tests::fixtures::good_mixnode_pledge(),
|
||||
// deps.as_mut(),
|
||||
// );
|
||||
// let delegation_owner = Addr::unchecked("sender");
|
||||
// try_delegate_to_mixnode(
|
||||
// deps.as_mut(),
|
||||
// mock_env(),
|
||||
// mock_info(delegation_owner.as_str(), &coins(100, TEST_COIN_DENOM)),
|
||||
// identity.clone(),
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
// _try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
|
||||
|
||||
// let _delegation = query_mixnode_delegation(
|
||||
// &deps.storage,
|
||||
// &deps.api,
|
||||
// identity.clone(),
|
||||
// delegation_owner.clone().into_string(),
|
||||
// None,
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
// let expected_response = Response::new()
|
||||
// .add_message(BankMsg::Send {
|
||||
// to_address: delegation_owner.clone().into(),
|
||||
// amount: coins(100, TEST_COIN_DENOM),
|
||||
// })
|
||||
// .add_event(new_undelegation_event(
|
||||
// &delegation_owner,
|
||||
// &None,
|
||||
// &identity,
|
||||
// Uint128::new(100),
|
||||
// ));
|
||||
|
||||
// assert_eq!(
|
||||
// Ok(expected_response),
|
||||
// try_remove_delegation_from_mixnode(
|
||||
// deps.as_mut(),
|
||||
// env,
|
||||
// mock_info(delegation_owner.as_str(), &[]),
|
||||
// identity.clone(),
|
||||
// )
|
||||
// );
|
||||
// assert!(storage::delegations()
|
||||
// .may_load(
|
||||
// &deps.storage,
|
||||
// (identity.clone(), delegation_owner.as_bytes().to_vec(), 0),
|
||||
// )
|
||||
// .unwrap()
|
||||
// .is_none());
|
||||
|
||||
// // and total delegation is cleared
|
||||
// assert_eq!(
|
||||
// Uint128::zero(),
|
||||
// mixnodes_storage::TOTAL_DELEGATION
|
||||
// .load(&deps.storage, &identity)
|
||||
// .unwrap()
|
||||
// )
|
||||
//}
|
||||
|
||||
// TODO: Update to work with reconciliation
|
||||
//#[ignore]
|
||||
//#[test]
|
||||
//fn succeeds_if_delegation_existed_even_if_node_unbonded() {
|
||||
// let mut deps = test_helpers::init_contract();
|
||||
// let mixnode_owner = "bob";
|
||||
// let env = mock_env();
|
||||
// let identity = test_helpers::add_mixnode(
|
||||
// mixnode_owner,
|
||||
// tests::fixtures::good_mixnode_pledge(),
|
||||
// deps.as_mut(),
|
||||
// );
|
||||
// let delegation_owner = Addr::unchecked("sender");
|
||||
// try_delegate_to_mixnode(
|
||||
// deps.as_mut(),
|
||||
// mock_env(),
|
||||
// mock_info(delegation_owner.as_str(), &coins(100, TEST_COIN_DENOM)),
|
||||
// identity.clone(),
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
// _try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
|
||||
|
||||
// let _delegation = query_mixnode_delegation(
|
||||
// &deps.storage,
|
||||
// &deps.api,
|
||||
// identity.clone(),
|
||||
// delegation_owner.clone().into_string(),
|
||||
// None,
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
// let expected_response = Response::new()
|
||||
// .add_message(BankMsg::Send {
|
||||
// to_address: delegation_owner.clone().into(),
|
||||
// amount: coins(100, TEST_COIN_DENOM),
|
||||
// })
|
||||
// .add_event(new_undelegation_event(
|
||||
// &delegation_owner,
|
||||
// &None,
|
||||
// &identity,
|
||||
// Uint128::new(100),
|
||||
// ));
|
||||
|
||||
// try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
|
||||
|
||||
// assert_eq!(
|
||||
// Ok(expected_response),
|
||||
// try_remove_delegation_from_mixnode(
|
||||
// deps.as_mut(),
|
||||
// env,
|
||||
// mock_info(delegation_owner.as_str(), &[]),
|
||||
// identity.clone(),
|
||||
// )
|
||||
// );
|
||||
|
||||
// _try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
|
||||
|
||||
// assert!(test_helpers::read_delegation(
|
||||
// &deps.storage,
|
||||
// identity,
|
||||
// delegation_owner.as_bytes(),
|
||||
// mock_env().block.height
|
||||
// )
|
||||
// .is_none());
|
||||
//}
|
||||
|
||||
#[test]
|
||||
fn total_delegation_is_preserved_if_only_some_undelegate() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
|
||||
@@ -178,4 +178,6 @@ pub enum ContractError {
|
||||
last_update_time: u64,
|
||||
current_block_time: u64,
|
||||
},
|
||||
#[error("`mix_identity` is required when `delegator` is set")]
|
||||
MissingMixIdentity,
|
||||
}
|
||||
|
||||
@@ -372,6 +372,56 @@ pub fn try_compound_delegator_reward_on_behalf(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn try_compound_reward(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
operator: Option<String>,
|
||||
delegator: Option<String>,
|
||||
mix_identity: Option<IdentityKey>,
|
||||
proxy: Option<String>,
|
||||
) -> Result<Response, ContractError> {
|
||||
let proxy = proxy.and_then(|p| deps.api.addr_validate(&p).ok());
|
||||
if let Some(operator_address) = operator {
|
||||
let operator_address = deps.api.addr_validate(&operator_address)?;
|
||||
let reward = _try_compound_operator_reward(
|
||||
deps.storage,
|
||||
deps.api,
|
||||
env.block.height,
|
||||
&operator_address,
|
||||
proxy,
|
||||
)?;
|
||||
Ok(
|
||||
Response::default().add_event(new_compound_operator_reward_event(
|
||||
&operator_address,
|
||||
reward,
|
||||
)),
|
||||
)
|
||||
} else if let Some(delegator_address) = delegator {
|
||||
if mix_identity.is_none() {
|
||||
return Err(ContractError::MissingMixIdentity);
|
||||
}
|
||||
|
||||
let delegator_address = deps.api.addr_validate(&delegator_address)?;
|
||||
let reward = _try_compound_delegator_reward(
|
||||
env.block.height,
|
||||
deps,
|
||||
delegator_address.as_str(),
|
||||
mix_identity.as_ref().unwrap(),
|
||||
proxy,
|
||||
)?;
|
||||
Ok(
|
||||
Response::default().add_event(new_compound_delegator_reward_event(
|
||||
&delegator_address,
|
||||
&None,
|
||||
reward,
|
||||
&mix_identity.unwrap(),
|
||||
)),
|
||||
)
|
||||
} else {
|
||||
Ok(Response::default())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_compound_delegator_reward(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
|
||||
Reference in New Issue
Block a user