exposed migration commands to nym-cli + clippy
This commit is contained in:
committed by
fmtabbara
parent
c4f7a1e09d
commit
9179f1c351
@@ -683,6 +683,24 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn migrate_vested_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::MigrateVestedMixNode {}, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn migrate_vested_delegation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::MigrateVestedDelegation { mix_id },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "contract-testing")]
|
||||
async fn testing_resolve_all_pending_events(
|
||||
&self,
|
||||
@@ -928,6 +946,12 @@ mod tests {
|
||||
MixnetExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, owner } => client
|
||||
.withdraw_delegator_reward_on_behalf(owner.parse().unwrap(), mix_id, None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::MigrateVestedMixNode { .. } => {
|
||||
client.migrate_vested_mixnode(None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::MigrateVestedDelegation { mix_id } => {
|
||||
client.migrate_vested_delegation(mix_id, None).ignore()
|
||||
}
|
||||
|
||||
#[cfg(feature = "contract-testing")]
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents { .. } => {
|
||||
|
||||
@@ -437,6 +437,7 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue};
|
||||
use nym_vesting_contract_common::ExecuteMsg;
|
||||
|
||||
// it's enough that this compiles and clippy is happy about it
|
||||
#[allow(dead_code)]
|
||||
@@ -560,6 +561,9 @@ mod tests {
|
||||
VestingExecuteMsg::UpdateLockedPledgeCap { address, cap } => client
|
||||
.update_locked_pledge_cap(address.parse().unwrap(), cap, None)
|
||||
.ignore(),
|
||||
// those will never be manually called by clients
|
||||
ExecuteMsg::TrackMigratedMixnode { .. } => "explicitly_ignored".ignore(),
|
||||
ExecuteMsg::TrackMigratedDelegation { .. } => "explicitly_ignored".ignore(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub mix_id: Option<MixId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn migrate_vested_delegation(args: Args, client: SigningClient) {
|
||||
let mix_id = match args.mix_id {
|
||||
Some(mix_id) => mix_id,
|
||||
None => {
|
||||
let identity_key = args
|
||||
.identity_key
|
||||
.expect("either mix_id or mix_identity has to be specified");
|
||||
let node_details = client
|
||||
.get_mixnode_details_by_identity(identity_key)
|
||||
.await
|
||||
.expect("contract query failed")
|
||||
.mixnode_details
|
||||
.expect("mixnode with the specified identity doesnt exist");
|
||||
node_details.mix_id()
|
||||
}
|
||||
};
|
||||
|
||||
let res = client
|
||||
.migrate_vested_delegation(mix_id, None)
|
||||
.await
|
||||
.expect("failed to migrate delegation!");
|
||||
|
||||
info!("migration result: {:?}", res)
|
||||
}
|
||||
@@ -7,6 +7,7 @@ pub mod rewards;
|
||||
|
||||
pub mod delegate_to_mixnode;
|
||||
pub mod delegate_to_multiple_mixnodes;
|
||||
pub mod migrate_vested_delegation;
|
||||
pub mod query_for_delegations;
|
||||
pub mod undelegate_from_mixnode;
|
||||
pub mod vesting_delegate_to_mixnode;
|
||||
@@ -35,4 +36,6 @@ pub enum MixnetDelegatorsCommands {
|
||||
DelegateVesting(vesting_delegate_to_mixnode::Args),
|
||||
/// Undelegate from a mixnode (when originally using locked tokens)
|
||||
UndelegateVesting(vesting_undelegate_from_mixnode::Args),
|
||||
/// Migrate the delegation to use liquid tokens
|
||||
MigrateVestedDelegation(migrate_vested_delegation::Args),
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ async fn print_delegation_events(events: Vec<PendingEpochEvent>, client: &Signin
|
||||
mix_id,
|
||||
amount,
|
||||
proxy,
|
||||
..
|
||||
} => {
|
||||
if owner.as_str() == client.nyxd.address().as_ref() {
|
||||
table.add_row(vec![
|
||||
@@ -111,6 +112,7 @@ async fn print_delegation_events(events: Vec<PendingEpochEvent>, client: &Signin
|
||||
owner,
|
||||
mix_id,
|
||||
proxy,
|
||||
..
|
||||
} => {
|
||||
if owner.as_str() == client.nyxd.address().as_ref() {
|
||||
table.add_row(vec![
|
||||
|
||||
+2
-13
@@ -8,7 +8,7 @@ use cosmwasm_std::Coin;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_mixnet_contract_common::construct_gateway_bonding_sign_payload;
|
||||
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -39,10 +39,6 @@ pub struct Args {
|
||||
)]
|
||||
pub amount: u128,
|
||||
|
||||
/// Indicates whether the gateway is going to get bonded via a vesting account
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
}
|
||||
@@ -74,15 +70,8 @@ pub async fn create_payload(args: Args, client: SigningClient) {
|
||||
};
|
||||
|
||||
let address = account_id_to_cw_addr(&client.address());
|
||||
let proxy = if args.with_vesting_account {
|
||||
Some(account_id_to_cw_addr(
|
||||
client.vesting_contract_address().unwrap(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payload = construct_gateway_bonding_sign_payload(nonce, address, proxy, coin, gateway);
|
||||
let payload = construct_gateway_bonding_sign_payload(nonce, address, coin, gateway);
|
||||
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
|
||||
println!("{}", args.output.format(&wrapper))
|
||||
}
|
||||
|
||||
@@ -5,33 +5,21 @@ use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
use nym_validator_client::nyxd::contract_traits::VestingSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// Label that is going to be used for creating the family
|
||||
#[arg(long)]
|
||||
pub family_label: String,
|
||||
|
||||
/// Indicates whether the family is going to get created via a vesting account
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
}
|
||||
|
||||
pub async fn create_family(args: Args, client: SigningClient) {
|
||||
info!("Create family");
|
||||
|
||||
let res = if args.with_vesting_account {
|
||||
client
|
||||
.vesting_create_family(args.family_label, None)
|
||||
.await
|
||||
.expect("failed to create family with vesting account")
|
||||
} else {
|
||||
client
|
||||
.create_family(args.family_label, None)
|
||||
.await
|
||||
.expect("failed to create family")
|
||||
};
|
||||
let res = client
|
||||
.create_family(args.family_label, None)
|
||||
.await
|
||||
.expect("failed to create family");
|
||||
|
||||
info!("Family creation result: {:?}", res);
|
||||
}
|
||||
|
||||
+3
-16
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::QueryClient;
|
||||
use crate::utils::{account_id_to_cw_addr, DataWrapper};
|
||||
use crate::utils::DataWrapper;
|
||||
use clap::Parser;
|
||||
use cosmrs::AccountId;
|
||||
use log::info;
|
||||
@@ -10,7 +10,7 @@ use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_mixnet_contract_common::construct_family_join_permit;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -18,10 +18,6 @@ pub struct Args {
|
||||
#[arg(long)]
|
||||
pub address: AccountId,
|
||||
|
||||
/// Indicates whether the member joining the family is going to use the vesting account for joining.
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
|
||||
// might as well validate the value when parsing the arguments
|
||||
/// Identity of the member for whom we're issuing the permit
|
||||
#[arg(long)]
|
||||
@@ -68,18 +64,9 @@ pub async fn create_family_join_permit_sign_payload(args: Args, client: QueryCli
|
||||
}
|
||||
};
|
||||
|
||||
// let address = account_id_to_cw_addr(&args.address);
|
||||
let proxy = if args.with_vesting_account {
|
||||
Some(account_id_to_cw_addr(
|
||||
client.vesting_contract_address().unwrap(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let head = FamilyHead::new(mixnode.bond_information.identity());
|
||||
|
||||
let payload = construct_family_join_permit(nonce, head, proxy, args.member.to_base58_string());
|
||||
let payload = construct_family_join_permit(nonce, head, args.member.to_base58_string());
|
||||
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
|
||||
println!("{}", args.output.format(&wrapper))
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
use nym_validator_client::nyxd::contract_traits::VestingSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -16,10 +15,6 @@ pub struct Args {
|
||||
#[arg(long)]
|
||||
pub family_head: identity::PublicKey,
|
||||
|
||||
/// Indicates whether the member joining the family is going to do so via the vesting contract
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
|
||||
/// Permission, as provided by the family head, for joining the family
|
||||
#[arg(long)]
|
||||
pub join_permit: MessageSignature,
|
||||
@@ -30,17 +25,10 @@ pub async fn join_family(args: Args, client: SigningClient) {
|
||||
|
||||
let family_head = FamilyHead::new(args.family_head.to_base58_string());
|
||||
|
||||
let res = if args.with_vesting_account {
|
||||
client
|
||||
.vesting_join_family(args.join_permit, family_head, None)
|
||||
.await
|
||||
.expect("failed to join family with vesting account")
|
||||
} else {
|
||||
client
|
||||
.join_family(args.join_permit, family_head, None)
|
||||
.await
|
||||
.expect("failed to join family")
|
||||
};
|
||||
let res = client
|
||||
.join_family(args.join_permit, family_head, None)
|
||||
.await
|
||||
.expect("failed to join family");
|
||||
|
||||
info!("Family join result: {:?}", res);
|
||||
}
|
||||
|
||||
@@ -7,17 +7,12 @@ use log::info;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
use nym_validator_client::nyxd::contract_traits::VestingSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// The head of the family that we intend to leave
|
||||
#[arg(long)]
|
||||
pub family_head: identity::PublicKey,
|
||||
|
||||
/// Indicates whether we joined the family via the vesting contract
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
}
|
||||
|
||||
pub async fn leave_family(args: Args, client: SigningClient) {
|
||||
@@ -25,17 +20,10 @@ pub async fn leave_family(args: Args, client: SigningClient) {
|
||||
|
||||
let family_head = FamilyHead::new(args.family_head.to_base58_string());
|
||||
|
||||
let res = if args.with_vesting_account {
|
||||
client
|
||||
.vesting_leave_family(family_head, None)
|
||||
.await
|
||||
.expect("failed to leave family with vesting account")
|
||||
} else {
|
||||
client
|
||||
.leave_family(family_head, None)
|
||||
.await
|
||||
.expect("failed to leave family")
|
||||
};
|
||||
let res = client
|
||||
.leave_family(family_head, None)
|
||||
.await
|
||||
.expect("failed to leave family");
|
||||
|
||||
info!("Family leave result: {:?}", res);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {}
|
||||
|
||||
pub async fn migrate_vested_mixnode(_args: Args, client: SigningClient) {
|
||||
let res = client
|
||||
.migrate_vested_mixnode(None)
|
||||
.await
|
||||
.expect("failed to migrate mixnode!");
|
||||
|
||||
info!("migration result: {:?}", res)
|
||||
}
|
||||
+2
-13
@@ -11,7 +11,7 @@ use nym_mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNode
|
||||
use nym_network_defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
};
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
use nym_validator_client::nyxd::CosmWasmCoin;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -52,10 +52,6 @@ pub struct Args {
|
||||
)]
|
||||
pub amount: u128,
|
||||
|
||||
/// Indicates whether the mixnode is going to get bonded via a vesting account
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
}
|
||||
@@ -100,16 +96,9 @@ pub async fn create_payload(args: Args, client: SigningClient) {
|
||||
};
|
||||
|
||||
let address = account_id_to_cw_addr(&client.address());
|
||||
let proxy = if args.with_vesting_account {
|
||||
Some(account_id_to_cw_addr(
|
||||
client.vesting_contract_address().unwrap(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payload =
|
||||
construct_mixnode_bonding_sign_payload(nonce, address, proxy, coin, mixnode, cost_params);
|
||||
construct_mixnode_bonding_sign_payload(nonce, address, coin, mixnode, cost_params);
|
||||
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
|
||||
println!("{}", args.output.format(&wrapper))
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ pub mod bond_mixnode;
|
||||
pub mod decrease_pledge;
|
||||
pub mod families;
|
||||
pub mod keys;
|
||||
pub mod migrate_vested_mixnode;
|
||||
pub mod mixnode_bonding_sign_payload;
|
||||
pub mod pledge_more;
|
||||
pub mod rewards;
|
||||
@@ -52,4 +53,6 @@ pub enum MixnetOperatorsMixnodeCommands {
|
||||
DecreasePledge(decrease_pledge::Args),
|
||||
/// Decrease pledge with locked tokens
|
||||
DecreasePledgeVesting(vesting_decrease_pledge::Args),
|
||||
/// Migrate the mixnode to use liquid tokens
|
||||
MigrateVestedNode(migrate_vested_mixnode::Args),
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ pub enum ExecuteMsg {
|
||||
|
||||
// vesting migration:
|
||||
MigrateVestedMixNode {},
|
||||
MigratedVestedDelegation {
|
||||
MigrateVestedDelegation {
|
||||
mix_id: MixId,
|
||||
},
|
||||
|
||||
@@ -388,7 +388,7 @@ impl ExecuteMsg {
|
||||
format!("withdrawing delegator reward from mixnode {mix_id} on behalf")
|
||||
}
|
||||
ExecuteMsg::MigrateVestedMixNode { .. } => "migrate vested mixnode".into(),
|
||||
ExecuteMsg::MigratedVestedDelegation { .. } => "migrate vested delegation".to_string(),
|
||||
ExecuteMsg::MigrateVestedDelegation { .. } => "migrate vested delegation".to_string(),
|
||||
|
||||
#[cfg(feature = "contract-testing")]
|
||||
ExecuteMsg::TestingResolveAllPendingEvents { .. } => {
|
||||
|
||||
@@ -1,239 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::support::helpers::{mix_coin, mix_coins, vesting_owner};
|
||||
use crate::support::setup::{TestSetup, MIX_DENOM};
|
||||
use cosmwasm_std::Addr;
|
||||
use cw_multi_test::Executor;
|
||||
use nym_contracts_common::Percent;
|
||||
use nym_mixnet_contract_common::error::MixnetContractError;
|
||||
use nym_mixnet_contract_common::{ContractStateParams, MixNodeCostParams};
|
||||
use nym_mixnet_contract_common::{MixOwnershipResponse, QueryMsg as MixnetQueryMsg};
|
||||
use nym_vesting_contract_common::{ExecuteMsg as VestingExecuteMsg, VestingContractError};
|
||||
|
||||
#[test]
|
||||
fn decrease_mixnode_pledge_from_vesting_account_with_minimum_pledge() {
|
||||
let mut test = TestSetup::new_simple();
|
||||
let vesting_account = "vesting-account";
|
||||
|
||||
// 0. get the minimum pledge amount
|
||||
let state_params: ContractStateParams = test
|
||||
.app
|
||||
.wrap()
|
||||
.query_wasm_smart(test.mixnet_contract(), &MixnetQueryMsg::GetStateParams {})
|
||||
.unwrap();
|
||||
let minimum_pledge = state_params.minimum_mixnode_pledge;
|
||||
|
||||
// 1. create vesting account
|
||||
let create_msg = VestingExecuteMsg::CreateAccount {
|
||||
owner_address: vesting_account.to_string(),
|
||||
staking_address: None,
|
||||
vesting_spec: None,
|
||||
cap: None,
|
||||
};
|
||||
|
||||
test.app
|
||||
.execute_contract(
|
||||
vesting_owner(),
|
||||
test.vesting_contract(),
|
||||
&create_msg,
|
||||
&mix_coins(1_000_000_000),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// 2. bond mixnode with the vesting account
|
||||
let pledge = minimum_pledge.clone();
|
||||
|
||||
let cost_params = MixNodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||
interval_operating_cost: mix_coin(40_000_000),
|
||||
};
|
||||
|
||||
let (mix_node, owner_signature) = test.valid_mixnode_with_sig(
|
||||
vesting_account,
|
||||
Some(test.vesting_contract()),
|
||||
cost_params.clone(),
|
||||
pledge.clone(),
|
||||
);
|
||||
|
||||
let bond_msg = VestingExecuteMsg::BondMixnode {
|
||||
mix_node,
|
||||
cost_params,
|
||||
owner_signature,
|
||||
amount: pledge.clone(),
|
||||
};
|
||||
test.app
|
||||
.execute_contract(
|
||||
Addr::unchecked(vesting_account),
|
||||
test.vesting_contract(),
|
||||
&bond_msg,
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// 3. try to decrease the pledge
|
||||
|
||||
// trying to decrease by a zero amount - not valid
|
||||
let decrease_pledge_msg = VestingExecuteMsg::DecreasePledge {
|
||||
amount: mix_coin(0),
|
||||
};
|
||||
let res_zero = test
|
||||
.app
|
||||
.execute_contract(
|
||||
Addr::unchecked(vesting_account),
|
||||
test.vesting_contract(),
|
||||
&decrease_pledge_msg,
|
||||
&[],
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
VestingContractError::EmptyFunds,
|
||||
res_zero.downcast().unwrap()
|
||||
);
|
||||
|
||||
// trying to go below the cap - also not valid
|
||||
let amount = mix_coin(50_000);
|
||||
let decrease_pledge_msg = VestingExecuteMsg::DecreasePledge {
|
||||
amount: amount.clone(),
|
||||
};
|
||||
let res_below = test
|
||||
.app
|
||||
.execute_contract(
|
||||
Addr::unchecked(vesting_account),
|
||||
test.vesting_contract(),
|
||||
&decrease_pledge_msg,
|
||||
&[],
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
MixnetContractError::InvalidPledgeReduction {
|
||||
current: pledge.amount,
|
||||
decrease_by: amount.amount,
|
||||
minimum: minimum_pledge.amount,
|
||||
denom: minimum_pledge.denom
|
||||
},
|
||||
res_below.downcast().unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decrease_mixnode_pledge_from_vesting_account_with_sufficient_pledge() {
|
||||
let mut test = TestSetup::new_simple();
|
||||
let vesting_account = "vesting-account";
|
||||
|
||||
// 1. create vesting account
|
||||
let create_msg = VestingExecuteMsg::CreateAccount {
|
||||
owner_address: vesting_account.to_string(),
|
||||
staking_address: None,
|
||||
vesting_spec: None,
|
||||
cap: None,
|
||||
};
|
||||
|
||||
test.app
|
||||
.execute_contract(
|
||||
vesting_owner(),
|
||||
test.vesting_contract(),
|
||||
&create_msg,
|
||||
&mix_coins(10_000_000_000),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// 2. bond mixnode with the vesting account
|
||||
let pledge = mix_coin(150_000_000);
|
||||
|
||||
let cost_params = MixNodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||
interval_operating_cost: mix_coin(40_000_000),
|
||||
};
|
||||
|
||||
let (mix_node, owner_signature) = test.valid_mixnode_with_sig(
|
||||
vesting_account,
|
||||
Some(test.vesting_contract()),
|
||||
cost_params.clone(),
|
||||
pledge.clone(),
|
||||
);
|
||||
|
||||
let bond_msg = VestingExecuteMsg::BondMixnode {
|
||||
mix_node,
|
||||
cost_params,
|
||||
owner_signature,
|
||||
amount: pledge,
|
||||
};
|
||||
test.app
|
||||
.execute_contract(
|
||||
Addr::unchecked(vesting_account),
|
||||
test.vesting_contract(),
|
||||
&bond_msg,
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// 3. try to decrease the pledge
|
||||
let before: MixOwnershipResponse = test
|
||||
.app
|
||||
.wrap()
|
||||
.query_wasm_smart(
|
||||
test.mixnet_contract(),
|
||||
&MixnetQueryMsg::GetOwnedMixnode {
|
||||
address: vesting_account.to_string(),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let balance_before = test
|
||||
.app
|
||||
.wrap()
|
||||
.query_balance(test.vesting_contract(), MIX_DENOM)
|
||||
.unwrap();
|
||||
assert_eq!(balance_before.amount.u128(), 9_850_000_000);
|
||||
|
||||
let decrease_pledge_msg = VestingExecuteMsg::DecreasePledge {
|
||||
amount: mix_coin(50_000_000),
|
||||
};
|
||||
test.app
|
||||
.execute_contract(
|
||||
Addr::unchecked(vesting_account),
|
||||
test.vesting_contract(),
|
||||
&decrease_pledge_msg,
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let after_decrease: MixOwnershipResponse = test
|
||||
.app
|
||||
.wrap()
|
||||
.query_wasm_smart(
|
||||
test.mixnet_contract(),
|
||||
&MixnetQueryMsg::GetOwnedMixnode {
|
||||
address: vesting_account.to_string(),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// note: nothing has changed with the pledge because the event hasn't been resolved yet!
|
||||
assert_eq!(before.address, after_decrease.address);
|
||||
let before_details = before.mixnode_details.unwrap();
|
||||
let after_details = after_decrease.mixnode_details.unwrap();
|
||||
assert_eq!(
|
||||
before_details.rewarding_details,
|
||||
after_details.rewarding_details
|
||||
);
|
||||
assert_eq!(
|
||||
before_details.bond_information,
|
||||
after_details.bond_information
|
||||
);
|
||||
|
||||
// but we have the pending change saved now!
|
||||
assert!(before_details.pending_changes.pledge_change.is_none());
|
||||
assert_eq!(Some(1), after_details.pending_changes.pledge_change);
|
||||
|
||||
// 4. resolve events
|
||||
test.advance_mixnet_epoch();
|
||||
|
||||
let balance_after = test
|
||||
.app
|
||||
.wrap()
|
||||
.query_balance(test.vesting_contract(), MIX_DENOM)
|
||||
.unwrap();
|
||||
assert_eq!(balance_after.amount.u128(), 9_900_000_000);
|
||||
}
|
||||
@@ -12,27 +12,33 @@ pub fn mixnet_owner() -> Addr {
|
||||
Addr::unchecked(MIXNET_OWNER)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn vesting_owner() -> Addr {
|
||||
Addr::unchecked(VESTING_OWNER)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rewarding_validator() -> Addr {
|
||||
Addr::unchecked(REWARDING_VALIDATOR)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn mix_coins(amount: u128) -> Vec<Coin> {
|
||||
coins(amount, MIX_DENOM)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn mix_coin(amount: u128) -> Coin {
|
||||
coin(amount, MIX_DENOM)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn test_rng() -> ChaCha20Rng {
|
||||
let dummy_seed = [42u8; 32];
|
||||
ChaCha20Rng::from_seed(dummy_seed)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn mixnet_contract_wrapper() -> Box<dyn Contract<Empty>> {
|
||||
Box::new(
|
||||
ContractWrapper::new(
|
||||
|
||||
@@ -26,6 +26,7 @@ pub const VESTING_OWNER: &str = "vesting-owner";
|
||||
pub const REWARDING_VALIDATOR: &str = "rewarding-validator";
|
||||
pub const MIX_DENOM: &str = "unym";
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct ContractInstantiationResult {
|
||||
mixnet_contract_address: Addr,
|
||||
vesting_contract_address: Addr,
|
||||
@@ -69,6 +70,7 @@ impl TestSetupBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct TestSetup {
|
||||
pub app: App,
|
||||
pub rng: ChaCha20Rng,
|
||||
@@ -76,6 +78,7 @@ pub struct TestSetup {
|
||||
pub mixnet_contract: Addr,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl TestSetup {
|
||||
pub fn new_simple() -> Self {
|
||||
TestSetup::new(Default::default(), fixtures::default_mixnet_init_msg())
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod decrease_mixnode_pledge;
|
||||
mod support;
|
||||
|
||||
@@ -257,7 +257,7 @@ pub fn execute(
|
||||
ExecuteMsg::MigrateVestedMixNode { .. } => {
|
||||
crate::vesting_migration::try_migrate_vested_mixnode(deps, info)
|
||||
}
|
||||
ExecuteMsg::MigratedVestedDelegation { mix_id } => {
|
||||
ExecuteMsg::MigrateVestedDelegation { mix_id } => {
|
||||
crate::vesting_migration::try_migrate_vested_delegation(deps, info, mix_id)
|
||||
}
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ pub(crate) fn unbond_mixnode(
|
||||
cleanup_post_unbond_mixnode_storage(deps.storage, env, &node_details)?;
|
||||
|
||||
let response = Response::new()
|
||||
.send_tokens(&owner, tokens.clone())
|
||||
.send_tokens(owner, tokens.clone())
|
||||
.add_event(new_mixnode_unbonding_event(created_at, mix_id));
|
||||
|
||||
Ok(response)
|
||||
@@ -889,7 +889,7 @@ mod tests {
|
||||
try_increase_pledge(
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
mock_info(owner, &*change.clone()),
|
||||
mock_info(owner, &change.clone()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -90,7 +90,6 @@ pub fn execute(
|
||||
ExecuteMsg::TrackDecreasePledge { owner, amount } => {
|
||||
try_track_decrease_mixnode_pledge(&owner, amount, info, deps)
|
||||
}
|
||||
ExecuteMsg::UnbondGateway {} => try_unbond_gateway(info, deps),
|
||||
ExecuteMsg::TrackUnbondGateway { owner, amount } => {
|
||||
try_track_unbond_gateway(&owner, amount, info, deps)
|
||||
}
|
||||
|
||||
Generated
+1
-22
@@ -656,25 +656,6 @@ dependencies = [
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete_fig"
|
||||
version = "4.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99fee1d30a51305a6c2ed3fc5709be3c8af626c9c958e04dd9ae94e27bcbce9f"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.7"
|
||||
@@ -3128,9 +3109,6 @@ dependencies = [
|
||||
name = "nym-bin-common"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
"const-str",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
@@ -3283,6 +3261,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"http 1.1.0",
|
||||
"nym-bin-common",
|
||||
"reqwest 0.12.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -134,6 +134,10 @@ pub enum BackendError {
|
||||
WalletValidatorConnectionFailed,
|
||||
#[error("No defined default validator URL")]
|
||||
WalletNoDefaultValidator,
|
||||
#[error(
|
||||
"this vesting operation has been disabled. please use the non-vesting variant instead."
|
||||
)]
|
||||
UnsupportedVestingOperation,
|
||||
|
||||
#[error(transparent)]
|
||||
WalletError {
|
||||
|
||||
@@ -12,7 +12,7 @@ use nym_mixnet_contract_common::{
|
||||
construct_mixnode_bonding_sign_payload, Gateway, GatewayBondingPayload, MixNode,
|
||||
MixNodeCostParams, SignableGatewayBondingMsg, SignableMixNodeBondingMsg,
|
||||
};
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
use nym_validator_client::nyxd::error::NyxdError;
|
||||
use nym_validator_client::nyxd::Coin;
|
||||
use nym_validator_client::DirectSigningHttpRpcValidatorClient;
|
||||
@@ -21,7 +21,6 @@ use nym_validator_client::DirectSigningHttpRpcValidatorClient;
|
||||
#[async_trait]
|
||||
pub(crate) trait AddressAndNonceProvider {
|
||||
async fn get_signing_nonce(&self) -> Result<Nonce, NyxdError>;
|
||||
fn vesting_contract_address(&self) -> Addr;
|
||||
fn cw_address(&self) -> Addr;
|
||||
}
|
||||
|
||||
@@ -31,30 +30,11 @@ impl AddressAndNonceProvider for DirectSigningHttpRpcValidatorClient {
|
||||
self.nyxd.get_signing_nonce(&self.nyxd.address()).await
|
||||
}
|
||||
|
||||
fn vesting_contract_address(&self) -> Addr {
|
||||
// the call to unchecked is fine here as we're converting directly from `AccountId`
|
||||
// which must have been a valid bech32 address
|
||||
Addr::unchecked(
|
||||
self.nyxd
|
||||
.vesting_contract_address()
|
||||
.expect("unknown vesting contract address")
|
||||
.as_ref(),
|
||||
)
|
||||
}
|
||||
|
||||
fn cw_address(&self) -> Addr {
|
||||
self.nyxd.cw_address()
|
||||
}
|
||||
}
|
||||
|
||||
fn proxy<P: AddressAndNonceProvider>(client: &P, vesting: bool) -> Option<Addr> {
|
||||
if vesting {
|
||||
Some(client.vesting_contract_address())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// since the message has to go back to the user due to the increasing nonce, we might as well sign the entire payload
|
||||
pub(crate) async fn create_mixnode_bonding_sign_payload<P: AddressAndNonceProvider>(
|
||||
client: &P,
|
||||
@@ -63,14 +43,15 @@ pub(crate) async fn create_mixnode_bonding_sign_payload<P: AddressAndNonceProvid
|
||||
pledge: Coin,
|
||||
vesting: bool,
|
||||
) -> Result<SignableMixNodeBondingMsg, BackendError> {
|
||||
if vesting {
|
||||
return Err(BackendError::UnsupportedVestingOperation);
|
||||
}
|
||||
let sender = client.cw_address();
|
||||
let proxy = proxy(client, vesting);
|
||||
let nonce = client.get_signing_nonce().await?;
|
||||
|
||||
Ok(construct_mixnode_bonding_sign_payload(
|
||||
nonce,
|
||||
sender,
|
||||
proxy,
|
||||
pledge.into(),
|
||||
mix_node,
|
||||
cost_params,
|
||||
@@ -85,6 +66,9 @@ pub(crate) async fn verify_mixnode_bonding_sign_payload<P: AddressAndNonceProvid
|
||||
vesting: bool,
|
||||
msg_signature: &MessageSignature,
|
||||
) -> Result<(), BackendError> {
|
||||
if vesting {
|
||||
return Err(BackendError::UnsupportedVestingOperation);
|
||||
}
|
||||
let identity_key = identity::PublicKey::from_base58_string(&mix_node.identity_key)?;
|
||||
let signature = identity::Signature::from_bytes(msg_signature.as_ref())?;
|
||||
|
||||
@@ -118,10 +102,12 @@ pub(crate) async fn create_gateway_bonding_sign_payload<P: AddressAndNonceProvid
|
||||
pledge: Coin,
|
||||
vesting: bool,
|
||||
) -> Result<SignableGatewayBondingMsg, BackendError> {
|
||||
if vesting {
|
||||
return Err(BackendError::UnsupportedVestingOperation);
|
||||
}
|
||||
let payload = GatewayBondingPayload::new(gateway);
|
||||
let sender = client.cw_address();
|
||||
let proxy = proxy(client, vesting);
|
||||
let content = ContractMessageContent::new(sender, proxy, vec![pledge.into()], payload);
|
||||
let content = ContractMessageContent::new(sender, vec![pledge.into()], payload);
|
||||
let nonce = client.get_signing_nonce().await?;
|
||||
|
||||
Ok(SignableMessage::new(nonce, content))
|
||||
@@ -134,6 +120,9 @@ pub(crate) async fn verify_gateway_bonding_sign_payload<P: AddressAndNonceProvid
|
||||
vesting: bool,
|
||||
msg_signature: &MessageSignature,
|
||||
) -> Result<(), BackendError> {
|
||||
if vesting {
|
||||
return Err(BackendError::UnsupportedVestingOperation);
|
||||
}
|
||||
let identity_key = identity::PublicKey::from_base58_string(&gateway.identity_key)?;
|
||||
let signature = identity::Signature::from_bytes(msg_signature.as_ref())?;
|
||||
|
||||
@@ -170,7 +159,6 @@ mod tests {
|
||||
|
||||
struct MockClient {
|
||||
address: Addr,
|
||||
vesting_contract: Addr,
|
||||
signing_nonce: Nonce,
|
||||
}
|
||||
|
||||
@@ -180,10 +168,6 @@ mod tests {
|
||||
Ok(self.signing_nonce)
|
||||
}
|
||||
|
||||
fn vesting_contract_address(&self) -> Addr {
|
||||
self.vesting_contract.clone()
|
||||
}
|
||||
|
||||
fn cw_address(&self) -> Addr {
|
||||
self.address.clone()
|
||||
}
|
||||
@@ -211,7 +195,6 @@ mod tests {
|
||||
let dummy_account = Addr::unchecked("n16t2umcd83zjpl5puyuuq6lgmy4p3qedjd8ynn6");
|
||||
let dummy_client = MockClient {
|
||||
address: dummy_account,
|
||||
vesting_contract: Addr::unchecked("n17tj0a0w6v7r2dc54rnkzfza6s8hxs87rj273a5"),
|
||||
signing_nonce: 42,
|
||||
};
|
||||
|
||||
@@ -315,7 +298,6 @@ mod tests {
|
||||
let dummy_account = Addr::unchecked("n16t2umcd83zjpl5puyuuq6lgmy4p3qedjd8ynn6");
|
||||
let dummy_client = MockClient {
|
||||
address: dummy_account,
|
||||
vesting_contract: Addr::unchecked("n17tj0a0w6v7r2dc54rnkzfza6s8hxs87rj273a5"),
|
||||
signing_nonce: 42,
|
||||
};
|
||||
|
||||
|
||||
@@ -34,7 +34,10 @@ pub(crate) async fn execute(
|
||||
}
|
||||
nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::List(args) => {
|
||||
nym_cli_commands::validator::mixnet::delegators::query_for_delegations::execute(args, create_signing_client_with_nym_api(global_args, network_details)?).await
|
||||
}
|
||||
},
|
||||
nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::MigrateVestedDelegation(args) => {
|
||||
nym_cli_commands::validator::mixnet::delegators::migrate_vested_delegation::migrate_vested_delegation(args, create_signing_client(global_args, network_details)?).await
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -48,7 +48,10 @@ pub(crate) async fn execute(
|
||||
nym_cli_commands::validator::mixnet::operators::mixnode::MixnetOperatorsMixnodeCommands::DecreasePledgeVesting(args) => {
|
||||
nym_cli_commands::validator::mixnet::operators::mixnode::vesting_decrease_pledge::vesting_decrease_pledge(args, create_signing_client(global_args, network_details)?).await
|
||||
}
|
||||
_ => unreachable!(),
|
||||
nym_cli_commands::validator::mixnet::operators::mixnode::MixnetOperatorsMixnodeCommands::MigrateVestedNode(args) => {
|
||||
nym_cli_commands::validator::mixnet::operators::mixnode::migrate_vested_mixnode::migrate_vested_mixnode(args, create_signing_client(global_args, network_details)?).await
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user