From 2f4be6dedcb4bac06f8612573ba265f1283e58d9 Mon Sep 17 00:00:00 2001 From: Drazen Urch Date: Tue, 31 May 2022 11:06:10 +0200 Subject: [PATCH] Add claim reward to vesting and mixnet contract (#1292) * Add claim reward to vesting contract * Add compound and claim methods to nymd client * Add TrackReward message * CHANGELOG --- CHANGELOG.md | 3 + .../validator-client/src/nymd/mod.rs | 83 ++++++++ .../src/nymd/traits/vesting_signing_client.rs | 96 +++++++++ .../mixnet-contract/src/events.rs | 27 +++ .../mixnet-contract/src/msg.rs | 11 ++ .../vesting-contract/src/events.rs | 5 + .../vesting-contract/src/messages.rs | 8 + contracts/mixnet/src/contract.rs | 26 +++ contracts/mixnet/src/error.rs | 4 +- contracts/mixnet/src/rewards/transactions.rs | 186 +++++++++++++++++- contracts/vesting/src/contract.rs | 41 +++- .../vesting/src/traits/bonding_account.rs | 2 + .../vesting/src/traits/delegating_account.rs | 6 + .../vesting/src/traits/vesting_account.rs | 1 + .../src/vesting/account/delegating_account.rs | 19 ++ .../account/mixnode_bonding_account.rs | 14 ++ .../src/vesting/account/vesting_account.rs | 7 + 17 files changed, 535 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d8b2387a3..927eb0fa5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ - wallet: added support for multiple accounts ([#1265]) - wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint. - mixnet-contract: Replace all naked `-` with `saturating_sub`. +- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292]) +- vesting-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292]) - validator-api: add Swagger to document the REST API ([#1249]). - validator-api: add `estimated_node_profit` and `estimated_operator_cost` to `reward-estimate` endpoint ([#1284]) - all: added network compilation target to `--help` (or `--version`) commands ([#1256]). @@ -34,6 +36,7 @@ [#1275]: https://github.com/nymtech/nym/pull/1275 [#1278]: https://github.com/nymtech/nym/pull/1278 [#1284]: https://github.com/nymtech/nym/pull/1284 +[#1292]: https://github.com/nymtech/nym/pull/1292 ## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04) diff --git a/common/client-libs/validator-client/src/nymd/mod.rs b/common/client-libs/validator-client/src/nymd/mod.rs index 03adeb8e5b..94a543dcb3 100644 --- a/common/client-libs/validator-client/src/nymd/mod.rs +++ b/common/client-libs/validator-client/src/nymd/mod.rs @@ -786,6 +786,89 @@ impl NymdClient { .await } + pub async fn compound_operator_reward( + &self, + fee: Option, + ) -> Result + where + C: SigningCosmWasmClient + Sync, + { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let req = ExecuteMsg::CompoundOperatorReward {}; + self.client + .execute( + self.address(), + self.mixnet_contract_address()?, + &req, + fee, + "MixnetContract::CompoundOperatorReward", + vec![], + ) + .await + } + + pub async fn claim_operator_reward(&self, fee: Option) -> Result + where + C: SigningCosmWasmClient + Sync, + { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let req = ExecuteMsg::ClaimOperatorReward {}; + self.client + .execute( + self.address(), + self.mixnet_contract_address()?, + &req, + fee, + "MixnetContract::ClaimOperatorReward", + vec![], + ) + .await + } + + pub async fn compound_delegator_reward( + &self, + mix_identity: IdentityKey, + fee: Option, + ) -> Result + where + C: SigningCosmWasmClient + Sync, + { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let req = ExecuteMsg::CompoundDelegatorReward { mix_identity }; + self.client + .execute( + self.address(), + self.mixnet_contract_address()?, + &req, + fee, + "MixnetContract::CompoundDelegatorReward", + vec![], + ) + .await + } + + pub async fn claim_delegator_reward( + &self, + mix_identity: IdentityKey, + fee: Option, + ) -> Result + where + C: SigningCosmWasmClient + Sync, + { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let req = ExecuteMsg::ClaimDelegatorReward { mix_identity }; + self.client + .execute( + self.address(), + self.mixnet_contract_address()?, + &req, + fee, + "MixnetContract::ClaimDelegatorReward", + vec![], + ) + .await + } + /// Announce a mixnode, paying a fee. pub async fn bond_mixnode( &self, diff --git a/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs b/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs index 073cfb7052..3fcf05a218 100644 --- a/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs +++ b/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs @@ -12,6 +12,28 @@ use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, Vesting #[async_trait] pub trait VestingSigningClient { + async fn vesting_claim_operator_reward( + &self, + fee: Option, + ) -> Result; + + async fn vesting_claim_delegator_reward( + &self, + mix_identity: IdentityKey, + fee: Option, + ) -> Result; + + async fn vesting_compound_operator_reward( + &self, + fee: Option, + ) -> Result; + + async fn vesting_compound_delegator_reward( + &self, + mix_identity: IdentityKey, + fee: Option, + ) -> Result; + async fn vesting_update_mixnode_config( &self, profix_margin_percent: u8, @@ -374,4 +396,78 @@ impl VestingSigningClient for NymdClient ) .await } + + async fn vesting_claim_operator_reward( + &self, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let req = VestingExecuteMsg::ClaimOperatorReward {}; + self.client + .execute( + self.address(), + self.vesting_contract_address()?, + &req, + fee, + "VestingContract::ClaimOperatorReward", + vec![], + ) + .await + } + + async fn vesting_compound_operator_reward( + &self, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let req = VestingExecuteMsg::CompoundOperatorReward {}; + self.client + .execute( + self.address(), + self.vesting_contract_address()?, + &req, + fee, + "VestingContract::CompoundOperatorReward", + vec![], + ) + .await + } + + async fn vesting_claim_delegator_reward( + &self, + mix_identity: IdentityKey, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let req = VestingExecuteMsg::ClaimDelegatorReward { mix_identity }; + self.client + .execute( + self.address(), + self.vesting_contract_address()?, + &req, + fee, + "VestingContract::ClaimDelegatorReward", + vec![], + ) + .await + } + + async fn vesting_compound_delegator_reward( + &self, + mix_identity: IdentityKey, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let req = VestingExecuteMsg::CompoundDelegatorReward { mix_identity }; + self.client + .execute( + self.address(), + self.vesting_contract_address()?, + &req, + fee, + "VestingContract::CompoundDelegatorReward", + vec![], + ) + .await + } } diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs index 8464fc6d4c..a2ed8ffe35 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs @@ -23,7 +23,9 @@ pub const CHANGE_REWARDED_SET_EVENT_TYPE: &str = "change_rewarded_set"; pub const ADVANCE_INTERVAL_EVENT_TYPE: &str = "advance_interval"; pub const ADVANCE_EPOCH_EVENT_TYPE: &str = "advance_epoch"; pub const COMPOUND_DELEGATOR_REWARD_EVENT_TYPE: &str = "compound_delegator_reward"; +pub const CLAIM_DELEGATOR_REWARD_EVENT_TYPE: &str = "claim_delegator_reward"; pub const COMPOUND_OPERATOR_REWARD_EVENT_TYPE: &str = "compound_operator_reward"; +pub const CLAIM_OPERATOR_REWARD_EVENT_TYPE: &str = "claim_operator_reward"; pub const SNAPSHOT_MIXNODES_EVENT: &str = "snapshot_mixnodes"; // attributes that are used in multiple places @@ -151,6 +153,11 @@ pub fn new_compound_operator_reward_event(owner: &Addr, amount: Uint128) -> Even event.add_attribute(AMOUNT_KEY, amount.to_string()) } +pub fn new_claim_operator_reward_event(owner: &Addr, amount: Uint128) -> Event { + let event = Event::new(CLAIM_OPERATOR_REWARD_EVENT_TYPE).add_attribute(OWNER_KEY, owner); + event.add_attribute(AMOUNT_KEY, amount.to_string()) +} + pub fn new_compound_delegator_reward_event( delegator: &Addr, proxy: &Option, @@ -171,6 +178,26 @@ pub fn new_compound_delegator_reward_event( .add_attribute(DELEGATOR_KEY, delegator) } +pub fn new_claim_delegator_reward_event( + delegator: &Addr, + proxy: &Option, + amount: Uint128, + mix_identity: IdentityKeyRef<'_>, +) -> Event { + let mut event = + Event::new(CLAIM_DELEGATOR_REWARD_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator); + + if let Some(proxy) = proxy { + event = event.add_attribute(PROXY_KEY, proxy) + } + + // coin implements Display trait and we use that implementation here + event + .add_attribute(AMOUNT_KEY, amount.to_string()) + .add_attribute(DELEGATION_TARGET_KEY, mix_identity) + .add_attribute(DELEGATOR_KEY, delegator) +} + pub fn new_undelegation_event( delegator: &Addr, proxy: &Option, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs index 8728df83d1..5c49215d9c 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs @@ -99,6 +99,17 @@ pub enum ExecuteMsg { }, // AdvanceCurrentInterval {}, AdvanceCurrentEpoch {}, + ClaimOperatorReward {}, + ClaimOperatorRewardOnBehalf { + owner: String, + }, + ClaimDelegatorReward { + mix_identity: IdentityKey, + }, + ClaimDelegatorRewardOnBehalf { + mix_identity: IdentityKey, + owner: String, + }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs index 85eb009ab2..93ca6814dd 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs @@ -20,6 +20,7 @@ pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixno pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond"; pub const TRACK_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond"; pub const TRACK_UNDELEGATION_EVENT_TYPE: &str = "track_undelegation"; +pub const TRACK_REWARD_EVENT_TYPE: &str = "track_reaward"; // attributes that are used in multiple places pub const OWNER_KEY: &str = "owner"; @@ -136,3 +137,7 @@ pub fn new_track_gateway_unbond_event() -> Event { pub fn new_track_undelegation_event() -> Event { Event::new(TRACK_UNDELEGATION_EVENT_TYPE) } + +pub fn new_track_reward_event() -> Event { + Event::new(TRACK_REWARD_EVENT_TYPE) +} diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs index 4ed3114b4d..e763e164f3 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs @@ -49,6 +49,14 @@ impl VestingSpecification { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { + TrackReward { + amount: Coin, + address: String, + }, + ClaimOperatorReward {}, + ClaimDelegatorReward { + mix_identity: String, + }, CompoundDelegatorReward { mix_identity: String, }, diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index 85a86bcfc3..240055062c 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -283,6 +283,32 @@ pub fn execute( info, ) } + ExecuteMsg::ClaimOperatorReward {} => { + crate::rewards::transactions::try_claim_operator_reward(deps, &env, &info) + } + ExecuteMsg::ClaimOperatorRewardOnBehalf { owner } => { + crate::rewards::transactions::try_claim_operator_reward_on_behalf( + deps, &env, &info, owner, + ) + } + ExecuteMsg::ClaimDelegatorReward { mix_identity } => { + crate::rewards::transactions::try_claim_delegator_reward( + deps, + &env, + &info, + &mix_identity, + ) + } + ExecuteMsg::ClaimDelegatorRewardOnBehalf { + mix_identity, + owner, + } => crate::rewards::transactions::try_claim_delegator_reward_on_behalf( + deps, + &env, + &info, + owner, + &mix_identity, + ), } } diff --git a/contracts/mixnet/src/error.rs b/contracts/mixnet/src/error.rs index b8af736b26..15e082f845 100644 --- a/contracts/mixnet/src/error.rs +++ b/contracts/mixnet/src/error.rs @@ -153,8 +153,8 @@ pub enum ContractError { #[from] source: MixnetContractError, }, - #[error("No rewards to claim for mixnode {identity} for delegate {delegate}")] - NoRewardsToClaim { identity: String, delegate: String }, + #[error("No rewards to claim for mixnode {identity} for {address}")] + NoRewardsToClaim { identity: String, address: String }, #[error("Epoch not initialized yet!")] EpochNotInitialized, diff --git a/contracts/mixnet/src/rewards/transactions.rs b/contracts/mixnet/src/rewards/transactions.rs index 2641b2f566..239b992fc9 100644 --- a/contracts/mixnet/src/rewards/transactions.rs +++ b/contracts/mixnet/src/rewards/transactions.rs @@ -15,9 +15,13 @@ use crate::mixnodes::storage::{self as mixnodes_storage, StoredMixnodeBond}; use crate::rewards::helpers; use crate::support::helpers::is_authorized; use config::defaults::DENOM; -use cosmwasm_std::{Addr, Api, Coin, DepsMut, Env, MessageInfo, Order, Response, Storage, Uint128}; +use cosmwasm_std::{ + coins, wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, MessageInfo, Order, Response, + Storage, Uint128, +}; use cw_storage_plus::Bound; use mixnet_contract_common::events::{ + new_claim_delegator_reward_event, new_claim_operator_reward_event, new_compound_delegator_reward_event, new_compound_operator_reward_event, new_mix_operator_rewarding_event, new_not_found_mix_operator_rewarding_event, new_too_fresh_bond_mix_operator_rewarding_event, new_zero_uptime_mix_operator_rewarding_event, @@ -27,6 +31,186 @@ use mixnet_contract_common::reward_params::{NodeEpochRewards, NodeRewardParams, use mixnet_contract_common::{Delegation, IdentityKey, RewardingStatus}; use mixnet_contract_common::RewardingResult; +use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::one_ucoin; + +// All four of the below methods need to do the following things: +// 1. Calculate currently available rewards +// 2. Send the rewards back to whoever claimed them +// 3. Set the LAST_CLAIMED_HEIGHT to the current height +pub fn try_claim_operator_reward( + deps: DepsMut<'_>, + env: &Env, + info: &MessageInfo, +) -> Result { + _try_claim_operator_reward(deps.storage, deps.api, env, &info.sender.to_string(), None) +} + +pub fn try_claim_operator_reward_on_behalf( + deps: DepsMut<'_>, + env: &Env, + info: &MessageInfo, + owner: String, +) -> Result { + _try_claim_operator_reward( + deps.storage, + deps.api, + env, + &owner, + Some(info.sender.clone()), + ) +} + +fn _try_claim_operator_reward( + storage: &mut dyn Storage, + api: &dyn Api, + env: &Env, + owner: &str, + proxy: Option, +) -> Result { + let owner = api.addr_validate(owner)?; + + let bond = match crate::mixnodes::storage::mixnodes() + .idx + .owner + .item(storage, owner.clone())? + { + Some(record) => record.1, + None => return Err(ContractError::NoAssociatedMixNodeBond { owner }), + }; + + if proxy != bond.proxy { + return Err(ContractError::ProxyMismatch { + existing: bond + .proxy + .map_or_else(|| "None".to_string(), |a| a.as_str().to_string()), + incoming: proxy.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()), + }); + } + + let reward = calculate_operator_reward(storage, api, &owner, &bond)?; + + OPERATOR_REWARD_CLAIMED_HEIGHT.save( + storage, + (owner.to_string(), bond.identity().to_string()), + &env.block.height, + )?; + + if reward.is_zero() { + return Err(ContractError::NoRewardsToClaim { + identity: bond.identity().to_string(), + address: owner.to_string(), + }); + } + + let return_tokens = BankMsg::Send { + to_address: proxy.as_ref().unwrap_or(&owner).to_string(), + amount: coins(reward.u128(), DENOM), + }; + + let mut response = Response::default() + .add_message(return_tokens) + .add_event(new_claim_operator_reward_event(&owner, reward)); + + if let Some(proxy) = proxy { + let msg = Some(VestingContractExecuteMsg::TrackReward { + address: owner.to_string(), + amount: Coin::new(reward.u128(), DENOM), + }); + + let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?; + response = response.add_message(wasm_msg); + } + + Ok(response) +} + +pub fn _try_claim_delegator_reward( + storage: &mut dyn Storage, + api: &dyn Api, + env: &Env, + owner: &str, + mix_identity: &str, + proxy: Option, +) -> Result { + let owner = api.addr_validate(owner)?; + + let key = mixnet_contract_common::delegation::generate_storage_key(&owner, proxy.as_ref()); + let reward = calculate_delegator_reward(storage, api, key.clone(), mix_identity)?; + + DELEGATOR_REWARD_CLAIMED_HEIGHT.save( + storage, + (key, mix_identity.to_string()), + &env.block.height, + )?; + + if reward.is_zero() { + return Err(ContractError::NoRewardsToClaim { + identity: mix_identity.to_string(), + address: owner.to_string(), + }); + } + + let return_tokens = BankMsg::Send { + to_address: proxy.as_ref().unwrap_or(&owner).to_string(), + amount: coins(reward.u128(), DENOM), + }; + + let mut response = + Response::default() + .add_message(return_tokens) + .add_event(new_claim_delegator_reward_event( + &owner, + &proxy, + reward, + mix_identity, + )); + + if let Some(proxy) = proxy { + let msg = Some(VestingContractExecuteMsg::TrackReward { + address: owner.to_string(), + amount: Coin::new(reward.u128(), DENOM), + }); + + let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?; + response = response.add_message(wasm_msg); + } + + Ok(response) +} + +pub fn try_claim_delegator_reward_on_behalf( + deps: DepsMut<'_>, + env: &Env, + info: &MessageInfo, + owner: String, + mix_identity: &str, +) -> Result { + _try_claim_delegator_reward( + deps.storage, + deps.api, + env, + &owner, + mix_identity, + Some(info.sender.clone()), + ) +} + +pub fn try_claim_delegator_reward( + deps: DepsMut<'_>, + env: &Env, + info: &MessageInfo, + mix_identity: &str, +) -> Result { + _try_claim_delegator_reward( + deps.storage, + deps.api, + env, + &info.sender.to_string(), + mix_identity, + None, + ) +} pub fn try_compound_operator_reward_on_behalf( deps: DepsMut, diff --git a/contracts/vesting/src/contract.rs b/contracts/vesting/src/contract.rs index e60fd0e2ef..e40feb27dd 100644 --- a/contracts/vesting/src/contract.rs +++ b/contracts/vesting/src/contract.rs @@ -13,7 +13,8 @@ use mixnet_contract_common::{Gateway, IdentityKey, MixNode}; use vesting_contract_common::events::{ new_ownership_transfer_event, new_periodic_vesting_account_event, new_staking_address_update_event, new_track_gateway_unbond_event, - new_track_mixnode_unbond_event, new_track_undelegation_event, new_vested_coins_withdraw_event, + new_track_mixnode_unbond_event, new_track_reward_event, new_track_undelegation_event, + new_vested_coins_withdraw_event, }; use vesting_contract_common::messages::{ ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification, @@ -46,6 +47,13 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { + ExecuteMsg::TrackReward { amount, address } => { + try_track_reward(deps, info, amount, &address) + } + ExecuteMsg::ClaimOperatorReward {} => try_claim_operator_reward(deps, info), + ExecuteMsg::ClaimDelegatorReward { mix_identity } => { + try_claim_delegator_reward(deps, info, mix_identity) + } ExecuteMsg::CompoundDelegatorReward { mix_identity } => { try_compound_delegator_reward(mix_identity, info, deps) } @@ -278,6 +286,20 @@ pub fn try_track_unbond_mixnode( Ok(Response::new().add_event(new_track_mixnode_unbond_event())) } +fn try_track_reward( + deps: DepsMut<'_>, + info: MessageInfo, + amount: Coin, + address: &str, +) -> Result { + if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? { + return Err(ContractError::NotMixnetContract(info.sender)); + } + let account = account_from_address(address, deps.storage, deps.api)?; + account.track_reward(amount, deps.storage)?; + Ok(Response::new().add_event(new_track_reward_event())) +} + fn try_track_undelegation( address: &str, mix_identity: IdentityKey, @@ -314,6 +336,23 @@ fn try_compound_delegator_reward( account.try_compound_delegator_reward(mix_identity, deps.storage) } +fn try_claim_operator_reward( + deps: DepsMut<'_>, + info: MessageInfo, +) -> Result { + let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; + account.try_claim_operator_reward(deps.storage) +} + +fn try_claim_delegator_reward( + deps: DepsMut<'_>, + info: MessageInfo, + mix_identity: String, +) -> Result { + let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; + account.try_claim_delegator_reward(mix_identity, deps.storage) +} + fn try_undelegate_from_mixnode( mix_identity: IdentityKey, info: MessageInfo, diff --git a/contracts/vesting/src/traits/bonding_account.rs b/contracts/vesting/src/traits/bonding_account.rs index ba9a7e2e81..1ce2690df9 100644 --- a/contracts/vesting/src/traits/bonding_account.rs +++ b/contracts/vesting/src/traits/bonding_account.rs @@ -8,6 +8,8 @@ pub trait MixnodeBondingAccount { storage: &dyn Storage, ) -> Result; + fn try_claim_operator_reward(&self, storage: &dyn Storage) -> Result; + fn try_bond_mixnode( &self, mix_node: MixNode, diff --git a/contracts/vesting/src/traits/delegating_account.rs b/contracts/vesting/src/traits/delegating_account.rs index c3b188a99a..174a9202ea 100644 --- a/contracts/vesting/src/traits/delegating_account.rs +++ b/contracts/vesting/src/traits/delegating_account.rs @@ -3,6 +3,12 @@ use cosmwasm_std::{Coin, Env, Response, Storage, Uint128}; use mixnet_contract_common::IdentityKey; pub trait DelegatingAccount { + fn try_claim_delegator_reward( + &self, + mix_identity: IdentityKey, + storage: &dyn Storage, + ) -> Result; + fn try_compound_delegator_reward( &self, mix_identity: IdentityKey, diff --git a/contracts/vesting/src/traits/vesting_account.rs b/contracts/vesting/src/traits/vesting_account.rs index b1d6687e47..bfb16c6aa9 100644 --- a/contracts/vesting/src/traits/vesting_account.rs +++ b/contracts/vesting/src/traits/vesting_account.rs @@ -73,4 +73,5 @@ pub trait VestingAccount { to_address: Option, storage: &mut dyn Storage, ) -> Result<(), ContractError>; + fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError>; } diff --git a/contracts/vesting/src/vesting/account/delegating_account.rs b/contracts/vesting/src/vesting/account/delegating_account.rs index 5ba3b16a5b..ccbacefdcc 100644 --- a/contracts/vesting/src/vesting/account/delegating_account.rs +++ b/contracts/vesting/src/vesting/account/delegating_account.rs @@ -13,6 +13,25 @@ use vesting_contract_common::one_ucoin; use super::Account; impl DelegatingAccount for Account { + fn try_claim_delegator_reward( + &self, + mix_identity: IdentityKey, + storage: &dyn Storage, + ) -> Result { + let msg = MixnetExecuteMsg::ClaimDelegatorRewardOnBehalf { + 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_compound_delegator_reward( &self, mix_identity: IdentityKey, diff --git a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs index 63d05dff6b..abbfe835ae 100644 --- a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs @@ -14,6 +14,20 @@ use vesting_contract_common::PledgeData; use super::Account; impl MixnodeBondingAccount for Account { + fn try_claim_operator_reward(&self, storage: &dyn Storage) -> Result { + let msg = MixnetExecuteMsg::ClaimOperatorRewardOnBehalf { + 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_compound_operator_reward( &self, storage: &dyn Storage, diff --git a/contracts/vesting/src/vesting/account/vesting_account.rs b/contracts/vesting/src/vesting/account/vesting_account.rs index f4528aded9..46da32e66d 100644 --- a/contracts/vesting/src/vesting/account/vesting_account.rs +++ b/contracts/vesting/src/vesting/account/vesting_account.rs @@ -8,6 +8,13 @@ use vesting_contract_common::{OriginalVestingResponse, Period}; use super::Account; impl VestingAccount for Account { + fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError> { + let current_balance = self.load_balance(storage)?; + let new_balance = current_balance + amount.amount; + self.save_balance(new_balance, storage)?; + Ok(()) + } + fn locked_coins( &self, block_time: Option,