Bugfix/delegation reconcile (#1219)

* Passing proxy value when attempting to compound delegator reward

* Do not attempt to delegate reward to mixnode if its zero

* Additional guards against sending 0 tokens

* Removed sign of sloppiness

* Fixes to rewards and delegation events storage

* Remove block count check, epoch cannot be advanced while in progress

* Add reward compound ops to vesting contract

* Migration to remove 0 value delegationns

Co-authored-by: durch <durch@users.noreply.github.com>
This commit is contained in:
Jędrzej Stuczyński
2022-04-25 13:01:45 +02:00
committed by GitHub
parent 9fb980dc5e
commit 8318002b0a
18 changed files with 201 additions and 105 deletions
@@ -264,13 +264,14 @@ impl<C> Client<C> {
&self,
address: String,
mix_identity: IdentityKey,
proxy: Option<String>,
) -> Result<u128, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self
.nymd
.get_delegator_rewards(address, mix_identity)
.get_delegator_rewards(address, mix_identity, proxy)
.await?
.u128())
}
@@ -329,6 +329,7 @@ impl<C> NymdClient<C> {
&self,
address: String,
mix_identity: IdentityKey,
proxy: Option<String>,
) -> Result<Uint128, NymdError>
where
C: CosmWasmClient + Sync,
@@ -336,6 +337,7 @@ impl<C> NymdClient<C> {
let request = QueryMsg::QueryDelegatorReward {
address,
mix_identity,
proxy,
};
self.client
.query_contract_smart(self.mixnet_contract_address()?, &request)
@@ -171,6 +171,7 @@ pub enum QueryMsg {
QueryDelegatorReward {
address: String,
mix_identity: IdentityKey,
proxy: Option<String>,
},
GetPendingDelegationEvents {
owner_address: String,
@@ -49,6 +49,10 @@ impl VestingSpecification {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
CompoundDelegatorReward {
mix_identity: String,
},
CompoundOperatorReward {},
UpdateMixnodeConfig {
profit_margin_percent: u8,
},
+33 -1
View File
@@ -7,6 +7,7 @@ use crate::delegations::queries::query_mixnode_delegation;
use crate::delegations::queries::{
query_mixnode_delegations_paged, query_pending_delegation_events,
};
use crate::delegations::storage::delegations;
use crate::error::ContractError;
use crate::gateways::queries::query_gateways_paged;
use crate::gateways::queries::query_owns_gateway;
@@ -32,6 +33,7 @@ use crate::rewards::storage as rewards_storage;
use cosmwasm_std::{
entry_point, to_binary, Addr, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Uint128,
};
use mixnet_contract_common::mixnode::DelegationEvent;
use mixnet_contract_common::{
ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg,
};
@@ -364,10 +366,12 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
QueryMsg::QueryDelegatorReward {
address,
mix_identity,
proxy,
} => to_binary(&crate::rewards::queries::query_delegator_reward(
deps,
address,
mix_identity,
proxy,
)?),
QueryMsg::GetPendingDelegationEvents {
owner_address,
@@ -389,7 +393,35 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
}
#[entry_point]
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
// if there exists any delegation of 0 value, remove it
let zero_delegations = delegations()
.range(deps.storage, None, None, cosmwasm_std::Order::Ascending)
.filter_map(|r| r.ok())
.filter(|(_k, v)| v.amount.amount == Uint128::zero())
.map(|(k, _v)| k)
.collect::<Vec<_>>();
for delegation in &zero_delegations {
delegations().remove(deps.storage, delegation.clone())?;
}
// similarly, if there exists any event that is scheduled to insert/remove delegation with 0 value, purge it
let delegate_events_to_purge = crate::delegations::storage::PENDING_DELEGATION_EVENTS
.range(deps.storage, None, None, cosmwasm_std::Order::Ascending)
.filter_map(|r| r.ok())
.filter(|(_k, v)| match v {
DelegationEvent::Delegate(d) => d.amount.amount == Uint128::zero(),
_ => false,
})
.map(|(k, _v)| k)
.collect::<Vec<_>>();
for delegation_event in delegate_events_to_purge {
crate::delegations::storage::PENDING_DELEGATION_EVENTS
.remove(deps.storage, delegation_event);
}
Ok(Default::default())
}
@@ -28,13 +28,12 @@ pub fn try_reconcile_all_delegation_events(
return Err(ContractError::Unauthorized);
}
_try_reconcile_all_delegation_events(deps.storage, deps.api)
_try_reconcile_all_delegation_events(deps.storage)
}
// TODO: Error handling?
pub(crate) fn _try_reconcile_all_delegation_events(
storage: &mut dyn Storage,
api: &dyn Api,
) -> Result<Response, ContractError> {
let pending_delegation_events = PENDING_DELEGATION_EVENTS
.range(storage, None, None, Order::Ascending)
@@ -46,12 +45,15 @@ pub(crate) fn _try_reconcile_all_delegation_events(
for (key, delegation_event) in pending_delegation_events {
match delegation_event {
DelegationEvent::Delegate(delegation) => {
// if for some reason the delegation is zero, don't do anything since it should be a no-op anyway
if delegation.amount.amount == Uint128::zero() {
continue;
}
let event = try_reconcile_delegation(storage, delegation)?;
response = response.add_event(event);
}
DelegationEvent::Undelegate(pending_undelegate) => {
let undelegate_response =
try_reconcile_undelegation(storage, api, &pending_undelegate)?;
let undelegate_response = try_reconcile_undelegation(storage, &pending_undelegate)?;
response = response.add_event(undelegate_response.event);
if let Some(msg) = undelegate_response.bank_msg {
response = response.add_message(msg);
@@ -240,7 +242,6 @@ pub struct ReconcileUndelegateResponse {
pub(crate) fn try_reconcile_undelegation(
storage: &mut dyn Storage,
api: &dyn Api,
pending_undelegate: &PendingUndelegate,
) -> Result<ReconcileUndelegateResponse, ContractError> {
let delegation_map = storage::delegations();
@@ -266,13 +267,10 @@ pub(crate) fn try_reconcile_undelegation(
});
}
let reward = crate::rewards::transactions::_try_compound_delegator_reward(
pending_undelegate.block_height(),
api,
let reward = crate::rewards::transactions::calculate_delegator_reward(
storage,
pending_undelegate.delegate().as_str(),
pending_undelegate.proxy_storage_key(),
&pending_undelegate.mix_identity(),
None,
)?;
// Might want to introduce paging here
@@ -318,13 +316,18 @@ pub(crate) fn try_reconcile_undelegation(
)?;
}
let bank_msg = BankMsg::Send {
to_address: pending_undelegate
.proxy()
.as_ref()
.unwrap_or(&pending_undelegate.delegate())
.to_string(),
amount: coins(total_delegation.u128(), DENOM),
// don't add a bank message if it would have resulted in attempting to send 0 tokens
let bank_msg = if total_delegation != Uint128::zero() {
Some(BankMsg::Send {
to_address: pending_undelegate
.proxy()
.as_ref()
.unwrap_or(&pending_undelegate.delegate())
.to_string(),
amount: coins(total_delegation.u128(), DENOM),
})
} else {
None
};
mixnodes_storage::TOTAL_DELEGATION.update::<_, ContractError>(
@@ -362,7 +365,7 @@ pub(crate) fn try_reconcile_undelegation(
);
Ok(ReconcileUndelegateResponse {
bank_msg: Some(bank_msg),
bank_msg,
wasm_msg,
event,
})
@@ -502,7 +505,7 @@ mod tests {
)
.is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let expected = Delegation::new(
delegation_owner.clone(),
@@ -581,7 +584,7 @@ mod tests {
)
.is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let expected = Delegation::new(
delegation_owner.clone(),
@@ -644,7 +647,7 @@ mod tests {
)
.unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
// let expected = Delegation::new(
// delegation_owner.clone(),
@@ -699,7 +702,7 @@ mod tests {
)
.unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
assert_eq!(
initial_height,
@@ -720,7 +723,7 @@ mod tests {
)
.unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let delegations = crate::delegations::queries::query_mixnode_delegation(
&deps.storage,
@@ -765,7 +768,7 @@ mod tests {
)
.unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
assert_eq!(
initial_height,
@@ -786,7 +789,7 @@ mod tests {
)
.unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
assert_eq!(
initial_height,
@@ -874,7 +877,7 @@ mod tests {
)
.is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let expected1 = Delegation::new(
delegation_owner.clone(),
@@ -939,7 +942,7 @@ mod tests {
identity.clone(),
)
.is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
// node's "total_delegation" is sum of both
assert_eq!(
@@ -969,7 +972,7 @@ mod tests {
)
.unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
@@ -1057,7 +1060,7 @@ mod tests {
)
.unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let _delegation = query_mixnode_delegation(
&deps.storage,
@@ -1127,7 +1130,7 @@ mod tests {
)
.unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let delegation = query_mixnode_delegation(
&deps.storage,
@@ -1162,7 +1165,7 @@ mod tests {
)
);
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
assert!(test_helpers::read_delegation(
&deps.storage,
@@ -1195,7 +1198,7 @@ mod tests {
)
.is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
assert!(try_delegate_to_mixnode(
deps.as_mut(),
@@ -1205,7 +1208,7 @@ mod tests {
)
.is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
// sender1 undelegates
try_remove_delegation_from_mixnode(
@@ -1216,7 +1219,7 @@ mod tests {
)
.unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
// but total delegation should still equal to what sender2 sent
// node's "total_delegation" is sum of both
assert_eq!(
+3
View File
@@ -158,4 +158,7 @@ pub enum ContractError {
#[error("Epoch not initialized yet!")]
EpochNotInitialized,
#[error("Invalid address: {0}")]
InvalidAddress(String),
}
@@ -43,16 +43,6 @@ pub fn try_write_rewarded_set(
}
let block_height = env.block.height;
if let Some(last_update) = storage::CURRENT_REWARDED_SET_HEIGHT.may_load(deps.storage)? {
if last_update + crate::constants::REWARDED_SET_REFRESH_BLOCKS > block_height {
return Err(ContractError::TooFrequentRewardedSetUpdate {
last_update,
minimum_delay: crate::constants::REWARDED_SET_REFRESH_BLOCKS,
current_height: block_height,
});
}
}
let num_nodes = rewarded_set.len();
storage::save_rewarded_set(deps.storage, block_height, active_set_size, rewarded_set)?;
@@ -184,21 +174,6 @@ mod tests {
// cannot be performed too soon after a previous update
env.block.height = last_update + 1;
assert_eq!(
Err(ContractError::TooFrequentRewardedSetUpdate {
last_update,
minimum_delay: crate::constants::REWARDED_SET_REFRESH_BLOCKS,
current_height: last_update + 1,
}),
try_write_rewarded_set(
deps.as_mut(),
env.clone(),
authorised_sender.clone(),
full_rewarded_set.clone(),
current_state.params.mixnode_active_set_size
)
);
// after successful rewarded set write, all internal storage structures are updated appropriately
env.block.height = last_update + crate::constants::REWARDED_SET_REFRESH_BLOCKS;
let expected_response = Response::new().add_event(new_change_rewarded_set_event(
+12 -6
View File
@@ -46,13 +46,19 @@ pub fn query_delegator_reward(
deps: Deps,
owner: String,
mix_identity: IdentityKey,
proxy: Option<String>,
) -> Result<Uint128, ContractError> {
let owner_address = deps.api.addr_validate(&owner)?;
super::transactions::calculate_delegator_reward(
deps.storage,
owner_address.as_str(),
&mix_identity,
)
let proxy = proxy.map(|p| {
deps.api
.addr_validate(&p)
.map_err(|_| ContractError::InvalidAddress(p))
.expect("proxy address is invalid")
});
let key = mixnet_contract_common::delegation::generate_storage_key(
&deps.api.addr_validate(&owner)?,
proxy.as_ref(),
);
super::transactions::calculate_delegator_reward(deps.storage, key, &mix_identity)
}
#[cfg(test)]
+2 -1
View File
@@ -14,7 +14,8 @@ pub(crate) const REWARD_POOL: Item<'_, Uint128> = Item::new("pool");
// TODO: Do we need a migration for this?
pub(crate) const REWARDING_STATUS: Map<'_, (u32, IdentityKey), RewardingStatus> = Map::new("rm");
pub(crate) const DELEGATOR_REWARD_CLAIMED_HEIGHT: Map<'_, (Address, IdentityKey), BlockHeight> =
// This has to be a byte vector due to proxy delegastions and rewarding
pub(crate) const DELEGATOR_REWARD_CLAIMED_HEIGHT: Map<'_, (Vec<u8>, IdentityKey), BlockHeight> =
Map::new("drc");
pub(crate) const OPERATOR_REWARD_CLAIMED_HEIGHT: Map<'_, (Address, IdentityKey), BlockHeight> =
Map::new("orc");
+34 -35
View File
@@ -1,7 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::storage::{self, epoch_reward_params_for_id, DELEGATOR_REWARD_CLAIMED_HEIGHT};
use super::storage::{
self, epoch_reward_params_for_id, DELEGATOR_REWARD_CLAIMED_HEIGHT,
OPERATOR_REWARD_CLAIMED_HEIGHT,
};
use crate::constants;
use crate::delegations::storage as delegations_storage;
use crate::delegations::transactions::_try_delegate_to_mixnode;
@@ -80,9 +83,9 @@ pub fn _try_compound_operator_reward(
block_height,
)?;
DELEGATOR_REWARD_CLAIMED_HEIGHT.save(
OPERATOR_REWARD_CLAIMED_HEIGHT.save(
storage,
(bond.identity().to_string(), owner.to_string()),
(owner.to_string(), bond.identity().to_string()),
&block_height,
)?;
@@ -198,31 +201,36 @@ pub fn _try_compound_delegator_reward(
mix_identity: &str,
proxy: Option<Addr>,
) -> Result<Uint128, ContractError> {
let reward = calculate_delegator_reward(storage, owner_address, mix_identity)?;
if _try_delegate_to_mixnode(
storage,
api,
block_height,
mix_identity,
owner_address,
Coin {
amount: reward,
denom: DENOM.to_string(),
},
proxy,
)
.is_ok()
let key = mixnet_contract_common::delegation::generate_storage_key(
&api.addr_validate(owner_address)?,
proxy.as_ref(),
);
let reward = calculate_delegator_reward(storage, key.clone(), mix_identity)?;
if reward != Uint128::zero()
&& _try_delegate_to_mixnode(
storage,
api,
block_height,
mix_identity,
owner_address,
Coin {
amount: reward,
denom: DENOM.to_string(),
},
proxy,
)
.is_ok()
{
// Node exists all is well, life goes on, if it does not exist we'll just return the reward to the caller as there is nothing to do on the bond
if let Some(mut bond) = mixnodes().may_load(storage, mix_identity)? {
bond.accumulated_rewards = Some(bond.accumulated_rewards() - reward);
mixnodes().save(storage, mix_identity, &bond, block_height)?;
}
};
}
DELEGATOR_REWARD_CLAIMED_HEIGHT.save(
storage,
(mix_identity.to_string(), owner_address.to_string()),
(key, mix_identity.to_string()),
&block_height,
)?;
@@ -234,20 +242,17 @@ pub fn _try_compound_delegator_reward(
// + last_reward_claimed height is correctly used
pub fn calculate_delegator_reward(
storage: &dyn Storage,
owner_address: &str,
key: Vec<u8>,
mix_identity: &str,
) -> Result<Uint128, ContractError> {
let last_claimed_height = storage::DELEGATOR_REWARD_CLAIMED_HEIGHT
.load(
storage,
(owner_address.to_string(), mix_identity.to_string()),
)
.load(storage, (key.clone(), mix_identity.to_string()))
.unwrap_or(0);
// Get delegations newer then last_claimed_height, it would be nice to also fold this into the iteration bellow but it should be ok for now, as
// I doubt folks refresh their delegations often
let delegations = delegations_storage::delegations()
.prefix((mix_identity.to_string(), owner_address.as_bytes().to_vec()))
.prefix((mix_identity.to_string(), key))
.range(storage, None, None, Order::Descending)
.filter_map(|record| record.ok())
.filter(|(height, _)| last_claimed_height <= *height)
@@ -866,11 +871,8 @@ pub mod tests {
)
.unwrap();
crate::delegations::transactions::_try_reconcile_all_delegation_events(
&mut deps.storage,
&deps.api,
)
.unwrap();
crate::delegations::transactions::_try_reconcile_all_delegation_events(&mut deps.storage)
.unwrap();
let info = mock_info(rewarding_validator_address.as_ref(), &[]);
env.block.height += 2 * constants::MINIMUM_BLOCK_AGE_FOR_REWARDING;
@@ -985,11 +987,8 @@ pub mod tests {
)
.unwrap();
crate::delegations::transactions::_try_reconcile_all_delegation_events(
&mut deps.storage,
&deps.api,
)
.unwrap();
crate::delegations::transactions::_try_reconcile_all_delegation_events(&mut deps.storage)
.unwrap();
let info = mock_info(rewarding_validator_address.as_ref(), &[]);
env.block.height += 2 * constants::MINIMUM_BLOCK_AGE_FOR_REWARDING;
+21
View File
@@ -46,6 +46,10 @@ pub fn execute(
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::CompoundDelegatorReward { mix_identity } => {
try_compound_delegator_reward(mix_identity, info, deps)
}
ExecuteMsg::CompoundOperatorReward {} => try_compound_operator_reward(info, deps),
ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
} => try_update_mixnode_config(profit_margin_percent, info, deps),
@@ -234,6 +238,14 @@ pub fn try_track_unbond_gateway(
Ok(Response::new().add_event(new_track_gateway_unbond_event()))
}
pub fn try_compound_operator_reward(
info: MessageInfo,
deps: DepsMut<'_>,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
account.try_compound_operator_reward(deps.storage)
}
pub fn try_bond_mixnode(
mix_node: MixNode,
owner_signature: String,
@@ -293,6 +305,15 @@ fn try_delegate_to_mixnode(
account.try_delegate_to_mixnode(mix_identity, amount, &env, deps.storage)
}
fn try_compound_delegator_reward(
mix_identity: IdentityKey,
info: MessageInfo,
deps: DepsMut<'_>,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
account.try_compound_delegator_reward(mix_identity, deps.storage)
}
fn try_undelegate_from_mixnode(
mix_identity: IdentityKey,
info: MessageInfo,
@@ -3,6 +3,11 @@ use cosmwasm_std::{Coin, Env, Response, Storage};
use mixnet_contract_common::{Gateway, MixNode};
pub trait MixnodeBondingAccount {
fn try_compound_operator_reward(
&self,
storage: &dyn Storage,
) -> Result<Response, ContractError>;
fn try_bond_mixnode(
&self,
mix_node: MixNode,
@@ -3,6 +3,12 @@ use cosmwasm_std::{Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::IdentityKey;
pub trait DelegatingAccount {
fn try_compound_delegator_reward(
&self,
mix_identity: IdentityKey,
storage: &dyn Storage,
) -> Result<Response, ContractError>;
fn try_delegate_to_mixnode(
&self,
mix_identity: IdentityKey,
@@ -13,6 +13,24 @@ use vesting_contract_common::one_ucoin;
use super::Account;
impl DelegatingAccount for Account {
fn try_compound_delegator_reward(
&self,
mix_identity: IdentityKey,
storage: &dyn Storage,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::CompoundDelegatorRewardOnBehalf {
owner: self.owner_address().to_string(),
mix_identity,
};
let compound_delegator_reward_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin()],
)?;
Ok(Response::new().add_message(compound_delegator_reward_msg))
}
fn try_delegate_to_mixnode(
&self,
mix_identity: IdentityKey,
@@ -14,6 +14,23 @@ use vesting_contract_common::PledgeData;
use super::Account;
impl MixnodeBondingAccount for Account {
fn try_compound_operator_reward(
&self,
storage: &dyn Storage,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::CompoundOperatorRewardOnBehalf {
owner: self.owner_address().into_string(),
};
let compound_operator_reward_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin()],
)?;
Ok(Response::new().add_message(compound_operator_reward_msg))
}
fn try_update_mixnode_config(
&self,
profit_margin_percent: u8,
@@ -71,11 +71,12 @@ pub async fn get_reverse_mix_delegations_paged(
pub async fn get_delegator_rewards(
address: String,
mix_identity: IdentityKey,
proxy: Option<String>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Uint128, BackendError> {
Ok(
nymd_client!(state)
.get_delegator_rewards(address, mix_identity)
.get_delegator_rewards(address, mix_identity, proxy)
.await?,
)
}
+2 -1
View File
@@ -157,6 +157,7 @@ impl<C> Client<C> {
&self,
address: String,
mix_identity: IdentityKey,
proxy: Option<String>,
) -> Result<u128, ValidatorClientError>
where
C: CosmWasmClient + Sync,
@@ -164,7 +165,7 @@ impl<C> Client<C> {
self.0
.read()
.await
.get_delegator_rewards(address, mix_identity)
.get_delegator_rewards(address, mix_identity, proxy)
.await
}