use crate::nyxd::contract_traits::NymContractsProvider;
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Coin, Fee, SigningCosmWasmClient};
use crate::signing::signer::OfflineSigner;
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::gateway::GatewayConfigUpdate;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, NodeCostParams};
use nym_mixnet_contract_common::{Gateway, MixNode, NodeId};
use nym_vesting_contract_common::messages::ExecuteMsg as VestingExecuteMsg;
use nym_vesting_contract_common::{PledgeCap, VestingSpecification};
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait VestingSigningClient {
async fn execute_vesting_contract(
&self,
fee: Option<Fee>,
msg: VestingExecuteMsg,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn vesting_update_mixnode_cost_params(
&self,
new_costs: NodeCostParams,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::UpdateMixnodeCostParams { new_costs },
vec![],
)
.await
}
async fn vesting_update_mixnode_config(
&self,
new_config: MixNodeConfigUpdate,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = VestingExecuteMsg::UpdateMixnodeConfig { new_config };
self.execute_vesting_contract(fee, req, vec![]).await
}
async fn vesting_update_gateway_config(
&self,
new_config: GatewayConfigUpdate,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::UpdateGatewayConfig { new_config },
vec![],
)
.await
}
async fn update_mixnet_address(
&self,
address: &str,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = VestingExecuteMsg::UpdateMixnetAddress {
address: address.to_string(),
};
self.execute_vesting_contract(fee, req, vec![]).await
}
async fn vesting_track_decrease_pledge(
&self,
owner: String,
amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::TrackDecreasePledge {
owner,
amount: amount.into(),
},
Vec::new(),
)
.await
}
async fn vesting_bond_gateway(
&self,
gateway: Gateway,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = VestingExecuteMsg::BondGateway {
gateway,
owner_signature,
amount: pledge.into(),
};
self.execute_vesting_contract(fee, req, vec![]).await
}
async fn vesting_unbond_gateway(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
let req = VestingExecuteMsg::UnbondGateway {};
self.execute_vesting_contract(fee, req, vec![]).await
}
async fn vesting_track_unbond_gateway(
&self,
owner: &str,
amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = VestingExecuteMsg::TrackUnbondGateway {
owner: owner.to_string(),
amount: amount.into(),
};
self.execute_vesting_contract(fee, req, vec![]).await
}
async fn vesting_bond_mixnode(
&self,
mix_node: MixNode,
cost_params: NodeCostParams,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::BondMixnode {
mix_node,
cost_params,
owner_signature,
amount: pledge.into(),
},
vec![],
)
.await
}
async fn vesting_pledge_more(
&self,
additional_pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::PledgeMore {
amount: additional_pledge.into(),
},
vec![],
)
.await
}
async fn vesting_decrease_pledge(
&self,
decrease_by: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::DecreasePledge {
amount: decrease_by.into(),
},
vec![],
)
.await
}
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
let req = VestingExecuteMsg::UnbondMixnode {};
self.execute_vesting_contract(fee, req, vec![]).await
}
async fn vesting_track_unbond_mixnode(
&self,
owner: &str,
amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = VestingExecuteMsg::TrackUnbondMixnode {
owner: owner.to_string(),
amount: amount.into(),
};
self.execute_vesting_contract(fee, req, vec![]).await
}
async fn withdraw_vested_coins(
&self,
amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = VestingExecuteMsg::WithdrawVestedCoins {
amount: amount.into(),
};
self.execute_vesting_contract(fee, req, vec![]).await
}
async fn vesting_track_undelegation(
&self,
address: &str,
mix_id: NodeId,
amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::TrackUndelegation {
owner: address.to_string(),
mix_id,
amount: amount.into(),
},
vec![],
)
.await
}
async fn vesting_delegate_to_mixnode(
&self,
mix_id: NodeId,
amount: Coin,
on_behalf_of: Option<String>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::DelegateToMixnode {
mix_id,
amount: amount.into(),
on_behalf_of,
},
vec![],
)
.await
}
async fn vesting_undelegate_from_mixnode(
&self,
mix_id: NodeId,
on_behalf_of: Option<String>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::UndelegateFromMixnode {
mix_id,
on_behalf_of,
},
vec![],
)
.await
}
async fn create_periodic_vesting_account(
&self,
owner_address: &str,
staking_address: Option<String>,
vesting_spec: Option<VestingSpecification>,
amount: Coin,
cap: Option<PledgeCap>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = VestingExecuteMsg::CreateAccount {
owner_address: owner_address.to_string(),
staking_address,
vesting_spec,
cap,
};
self.execute_vesting_contract(fee, req, vec![amount]).await
}
async fn vesting_track_reward(
&self,
amount: Coin,
address: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::TrackReward {
amount: amount.into(),
address,
},
Vec::new(),
)
.await
}
async fn vesting_withdraw_operator_reward(
&self,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(fee, VestingExecuteMsg::ClaimOperatorReward {}, Vec::new())
.await
}
async fn vesting_withdraw_delegator_reward(
&self,
mix_id: NodeId,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::ClaimDelegatorReward { mix_id },
Vec::new(),
)
.await
}
async fn vesting_transfer_ownership(
&self,
to_address: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::TransferOwnership { to_address },
Vec::new(),
)
.await
}
async fn update_staking_address(
&self,
to_address: Option<String>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::UpdateStakingAddress { to_address },
Vec::new(),
)
.await
}
async fn update_locked_pledge_cap(
&self,
address: AccountId,
cap: PledgeCap,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::UpdateLockedPledgeCap {
address: address.to_string(),
cap,
},
Vec::new(),
)
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> VestingSigningClient for C
where
C: SigningCosmWasmClient + NymContractsProvider + Sync,
NyxdError: From<<Self as OfflineSigner>::Error>,
{
async fn execute_vesting_contract(
&self,
fee: Option<Fee>,
msg: VestingExecuteMsg,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError> {
let vesting_contract_address = &self
.vesting_contract_address()
.ok_or_else(|| NyxdError::unavailable_contract_address("vesting contract"))?;
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
let memo = msg.name().to_string();
let signer_address = &self.signer_addresses()[0];
self.execute(
signer_address,
vesting_contract_address,
&msg,
fee,
memo,
funds,
)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue};
use nym_vesting_contract_common::ExecuteMsg;
#[allow(dead_code)]
fn all_execute_variants_are_covered<C: VestingSigningClient + Send + Sync>(
client: C,
msg: VestingExecuteMsg,
) {
match msg {
VestingExecuteMsg::TrackReward { amount, address } => client
.vesting_track_reward(amount.into(), address, None)
.ignore(),
VestingExecuteMsg::ClaimOperatorReward {} => {
client.vesting_withdraw_operator_reward(None).ignore()
}
VestingExecuteMsg::ClaimDelegatorReward { mix_id } => client
.vesting_withdraw_delegator_reward(mix_id, None)
.ignore(),
VestingExecuteMsg::UpdateMixnodeCostParams { new_costs } => client
.vesting_update_mixnode_cost_params(new_costs, None)
.ignore(),
VestingExecuteMsg::UpdateMixnodeConfig { new_config } => client
.vesting_update_mixnode_config(new_config, None)
.ignore(),
VestingExecuteMsg::UpdateMixnetAddress { address } => {
client.update_mixnet_address(&address, None).ignore()
}
VestingExecuteMsg::DelegateToMixnode {
mix_id,
amount,
on_behalf_of,
} => client
.vesting_delegate_to_mixnode(mix_id, amount.into(), on_behalf_of, None)
.ignore(),
VestingExecuteMsg::UndelegateFromMixnode {
mix_id,
on_behalf_of,
} => client
.vesting_undelegate_from_mixnode(mix_id, on_behalf_of, None)
.ignore(),
VestingExecuteMsg::CreateAccount {
owner_address,
staking_address,
vesting_spec,
cap,
} => client
.create_periodic_vesting_account(
&owner_address,
staking_address,
vesting_spec,
mock_coin(),
cap,
None,
)
.ignore(),
VestingExecuteMsg::WithdrawVestedCoins { amount } => {
client.withdraw_vested_coins(amount.into(), None).ignore()
}
VestingExecuteMsg::TrackUndelegation {
owner,
mix_id,
amount,
} => client
.vesting_track_undelegation(&owner, mix_id, amount.into(), None)
.ignore(),
VestingExecuteMsg::BondMixnode {
mix_node,
cost_params,
owner_signature,
amount,
} => client
.vesting_bond_mixnode(mix_node, cost_params, owner_signature, amount.into(), None)
.ignore(),
VestingExecuteMsg::PledgeMore { amount } => {
client.vesting_pledge_more(amount.into(), None).ignore()
}
VestingExecuteMsg::DecreasePledge { amount } => {
client.vesting_decrease_pledge(amount.into(), None).ignore()
}
VestingExecuteMsg::UnbondMixnode {} => client.vesting_unbond_mixnode(None).ignore(),
VestingExecuteMsg::TrackUnbondMixnode { owner, amount } => client
.vesting_track_unbond_mixnode(&owner, amount.into(), None)
.ignore(),
VestingExecuteMsg::TrackDecreasePledge { owner, amount } => client
.vesting_track_decrease_pledge(owner, amount.into(), None)
.ignore(),
VestingExecuteMsg::BondGateway {
gateway,
owner_signature,
amount,
} => client
.vesting_bond_gateway(gateway, owner_signature, amount.into(), None)
.ignore(),
VestingExecuteMsg::UnbondGateway {} => client.vesting_unbond_gateway(None).ignore(),
VestingExecuteMsg::TrackUnbondGateway { owner, amount } => client
.vesting_track_unbond_gateway(&owner, amount.into(), None)
.ignore(),
VestingExecuteMsg::UpdateGatewayConfig { new_config } => client
.vesting_update_gateway_config(new_config, None)
.ignore(),
VestingExecuteMsg::TransferOwnership { to_address } => {
client.vesting_transfer_ownership(to_address, None).ignore()
}
VestingExecuteMsg::UpdateStakingAddress { to_address } => {
client.update_staking_address(to_address, None).ignore()
}
VestingExecuteMsg::UpdateLockedPledgeCap { address, cap } => client
.update_locked_pledge_cap(address.parse().unwrap(), cap, None)
.ignore(),
ExecuteMsg::TrackMigratedMixnode { .. } => "explicitly_ignored".ignore(),
ExecuteMsg::TrackMigratedDelegation { .. } => "explicitly_ignored".ignore(),
};
}
}