From fe33df723b032653b4227ff5ceceb1f6a2175eec Mon Sep 17 00:00:00 2001 From: Drazen Urch Date: Tue, 1 Feb 2022 18:10:33 +0100 Subject: [PATCH] Tag contract errors, and print out lines for easier QA (#1084) * Tag contract errors, and print out lines for easier QA * Allow updating mixnet address by admin * Add reply endpoint, and unbond callback * Extract vesting messages * Wrap up --- Cargo.lock | 6 ++ Makefile | 2 +- .../client-libs/validator-client/Cargo.toml | 1 + .../validator-client/src/nymd/fee/helpers.rs | 3 + .../src/nymd/traits/vesting_query_client.rs | 5 +- .../src/nymd/traits/vesting_signing_client.rs | 21 +++++- .../vesting-contract/.DS_Store | Bin 0 -> 6148 bytes .../vesting-contract/Cargo.toml | 5 ++ .../vesting-contract/src/lib.rs | 7 ++ .../vesting-contract}/src/messages.rs | 5 +- contracts/Cargo.lock | 7 +- contracts/mixnet/Cargo.toml | 2 +- .../mixnet/src/delegations/transactions.rs | 5 +- contracts/mixnet/src/error.rs | 68 +++++++++--------- contracts/mixnet/src/gateways/transactions.rs | 7 +- contracts/mixnet/src/mixnodes/transactions.rs | 11 +-- contracts/vesting/src/contract.rs | 55 +++++++++++--- contracts/vesting/src/errors.rs | 38 +++++----- contracts/vesting/src/lib.rs | 1 - contracts/vesting/src/support/tests.rs | 2 +- .../src/vesting/account/delegating_account.rs | 7 +- .../account/gateway_bonding_account.rs | 7 +- .../account/mixnode_bonding_account.rs | 7 +- contracts/vesting/src/vesting/mod.rs | 4 +- nym-wallet/Cargo.lock | 6 ++ nym-wallet/src/types/rust/operation.ts | 3 +- 26 files changed, 193 insertions(+), 92 deletions(-) create mode 100644 common/cosmwasm-smart-contracts/vesting-contract/.DS_Store rename {contracts/vesting => common/cosmwasm-smart-contracts/vesting-contract}/src/messages.rs (97%) diff --git a/Cargo.lock b/Cargo.lock index ef48626a09..f3d0b5aa78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7660,6 +7660,7 @@ dependencies = [ "url", "validator-api-requests", "vesting-contract", + "vesting-contract-common", ] [[package]] @@ -7734,7 +7735,12 @@ dependencies = [ name = "vesting-contract-common" version = "0.1.0" dependencies = [ + "config", "cosmwasm-std", + "cw-storage-plus", + "mixnet-contract-common", + "schemars", + "serde", ] [[package]] diff --git a/Makefile b/Makefile index 847e8332d3..acb839b943 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -all: clippy-all test fmt +all: clippy-all test wasm fmt happy: clippy-happy test fmt clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index 30da196e37..24a0753d6e 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -10,6 +10,7 @@ rust-version = "1.56" [dependencies] base64 = "0.13" mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" } +vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" } vesting-contract = { path = "../../../contracts/vesting" } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/common/client-libs/validator-client/src/nymd/fee/helpers.rs b/common/client-libs/validator-client/src/nymd/fee/helpers.rs index 5c5eae4a11..f069047120 100644 --- a/common/client-libs/validator-client/src/nymd/fee/helpers.rs +++ b/common/client-libs/validator-client/src/nymd/fee/helpers.rs @@ -45,6 +45,7 @@ pub enum Operation { AdvanceCurrentInterval, WriteRewardedSet, ClearRewardedSet, + UpdateMixnetAddress, } pub(crate) fn calculate_fee(gas_price: &GasPrice, gas_limit: Gas) -> Coin { @@ -85,6 +86,7 @@ impl fmt::Display for Operation { Operation::AdvanceCurrentInterval => f.write_str("AdvanceCurrentInterval"), Operation::WriteRewardedSet => f.write_str("WriteRewardedSet"), Operation::ClearRewardedSet => f.write_str("ClearRewardedSet"), + Operation::UpdateMixnetAddress => f.write_str("UpdateMixnetAddress"), } } } @@ -125,6 +127,7 @@ impl Operation { Operation::AdvanceCurrentInterval => 175_000u64.into(), Operation::WriteRewardedSet => 175_000u64.into(), Operation::ClearRewardedSet => 175_000u64.into(), + Operation::UpdateMixnetAddress => 80_000u64.into(), } } diff --git a/common/client-libs/validator-client/src/nymd/traits/vesting_query_client.rs b/common/client-libs/validator-client/src/nymd/traits/vesting_query_client.rs index 84346db67e..b6bd534ccb 100644 --- a/common/client-libs/validator-client/src/nymd/traits/vesting_query_client.rs +++ b/common/client-libs/validator-client/src/nymd/traits/vesting_query_client.rs @@ -6,9 +6,8 @@ use crate::nymd::error::NymdError; use crate::nymd::NymdClient; use async_trait::async_trait; use cosmwasm_std::{Coin, Timestamp}; -use vesting_contract::messages::QueryMsg as VestingQueryMsg; -use vesting_contract::vesting::Account; -use vesting_contract::vesting::PledgeData; +use vesting_contract::vesting::{Account, PledgeData}; +use vesting_contract_common::messages::QueryMsg as VestingQueryMsg; #[async_trait] pub trait VestingQueryClient { 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 c4a62a6a7e..229e38752a 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 @@ -9,10 +9,12 @@ use crate::nymd::{cosmwasm_coin_to_cosmos_coin, NymdClient}; use async_trait::async_trait; use cosmwasm_std::Coin; use mixnet_contract_common::{Gateway, IdentityKey, IdentityKeyRef, MixNode}; -use vesting_contract::messages::{ExecuteMsg as VestingExecuteMsg, VestingSpecification}; +use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, VestingSpecification}; #[async_trait] pub trait VestingSigningClient { + async fn update_mixnet_address(&self, address: &str) -> Result; + async fn vesting_bond_gateway( &self, gateway: Gateway, @@ -297,4 +299,21 @@ impl VestingSigningClient for NymdClient ) .await } + + async fn update_mixnet_address(&self, address: &str) -> Result { + let fee = self.operation_fee(Operation::UpdateMixnetAddress); + let req = VestingExecuteMsg::UpdateMixnetAddress { + address: address.to_string(), + }; + self.client + .execute( + self.address(), + self.vesting_contract_address()?, + &req, + fee, + "VestingContract::UpdateMixnetAddress", + vec![], + ) + .await + } } diff --git a/common/cosmwasm-smart-contracts/vesting-contract/.DS_Store b/common/cosmwasm-smart-contracts/vesting-contract/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5172429f264de2441865cb4700216d4256da9242 GIT binary patch literal 6148 zcmeH~J!%6%427R!7lt%jx}3%b$PET#pTHLgIFQEJ;E>dF^gR7ES*H$5cmnB-G%I%Z zD|S`@Z2$T80!#olbXV*=%*>dt@PRwdU#I)^a=X5>;#J@&VrHyNnC;iLL0pQvfVyTmjO&;ssLc!1UOG})p;=82 zR;?Ceh}WZ?+UmMqI#RP8R>OzYoz15hnq@nzF`-!xQ4j$Um=RcIKKc27r2jVm&svm< zfC&6E0=7P!4tu^-ovjbA=k?dB`g+i*aXG_}p8zI)6mRKa+;6_1_R^8c3Qa!(fk8n8 H{*=HsM+*^= literal 0 HcmV?d00001 diff --git a/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml b/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml index 278244b8f0..c5829ec703 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml @@ -7,3 +7,8 @@ edition = "2021" [dependencies] cosmwasm-std = "1.0.0-beta3" +mixnet-contract-common = { path = "../mixnet-contract" } +serde = { version = "1.0", features = ["derive"] } +schemars = "0.8" +cw-storage-plus = "0.11.1" +config = {path = "../../config"} diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs index 002130e5fd..1230a985b1 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs @@ -1,4 +1,11 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use config::defaults::DENOM; +use cosmwasm_std::Coin; pub mod events; +pub mod messages; + +pub fn one_unym() -> Coin { + Coin::new(1, DENOM) +} diff --git a/contracts/vesting/src/messages.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs similarity index 97% rename from contracts/vesting/src/messages.rs rename to common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs index 24ebfd000c..dbd88042f5 100644 --- a/contracts/vesting/src/messages.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub struct InitMsg { - pub(crate) mixnet_contract_address: String, + pub mixnet_contract_address: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -49,6 +49,9 @@ impl VestingSpecification { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { + UpdateMixnetAddress { + address: String, + }, DelegateToMixnode { mix_identity: IdentityKey, amount: Coin, diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 11626897ca..864a01cdab 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -855,7 +855,7 @@ dependencies = [ "thiserror", "time 0.3.6", "vergen", - "vesting-contract", + "vesting-contract-common", ] [[package]] @@ -1557,7 +1557,12 @@ dependencies = [ name = "vesting-contract-common" version = "0.1.0" dependencies = [ + "config", "cosmwasm-std", + "cw-storage-plus", + "mixnet-contract-common", + "schemars", + "serde", ] [[package]] diff --git a/contracts/mixnet/Cargo.toml b/contracts/mixnet/Cargo.toml index e57152339f..ad4a677d14 100644 --- a/contracts/mixnet/Cargo.toml +++ b/contracts/mixnet/Cargo.toml @@ -17,8 +17,8 @@ crate-type = ["cdylib", "rlib"] [dependencies] mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } +vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } config = { path = "../../common/config"} -vesting-contract = { path = "../vesting" } cosmwasm-std = "1.0.0-beta3" cosmwasm-storage = "1.0.0-beta3" diff --git a/contracts/mixnet/src/delegations/transactions.rs b/contracts/mixnet/src/delegations/transactions.rs index 9d060e891c..8570228557 100644 --- a/contracts/mixnet/src/delegations/transactions.rs +++ b/contracts/mixnet/src/delegations/transactions.rs @@ -9,7 +9,8 @@ use cosmwasm_std::{coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, Messa use cw_storage_plus::PrimaryKey; use mixnet_contract_common::events::{new_delegation_event, new_undelegation_event}; use mixnet_contract_common::{Delegation, IdentityKey}; -use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::one_unym; fn validate_delegation_stake(mut delegation: Vec) -> Result { // check if anything was put as delegation @@ -212,7 +213,7 @@ pub(crate) fn _try_remove_delegation_from_mixnode( amount: old_delegation.amount.clone(), }); - let track_undelegation_msg = wasm_execute(proxy, &msg, coins(0, DENOM))?; + let track_undelegation_msg = wasm_execute(proxy, &msg, vec![one_unym()])?; response = response.add_message(track_undelegation_msg); } diff --git a/contracts/mixnet/src/error.rs b/contracts/mixnet/src/error.rs index ef12953200..ff4d93bd16 100644 --- a/contracts/mixnet/src/error.rs +++ b/contracts/mixnet/src/error.rs @@ -12,113 +12,113 @@ use thiserror::Error; /// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. #[derive(Error, Debug, PartialEq)] pub enum ContractError { - #[error("{0}")] + #[error("MIXNET ({}): {0}", line!())] Std(#[from] StdError), - #[error("Not enough funds sent for mixnode bond. (received {received}, minimum {minimum})")] + #[error("MIXNET ({}): Not enough funds sent for mixnode bond. (received {received}, minimum {minimum})", line!())] InsufficientMixNodeBond { received: u128, minimum: u128 }, - #[error("Mixnode ({identity}) does not exist")] + #[error("MIXNET ({}): Mixnode ({identity}) does not exist", line!())] MixNodeBondNotFound { identity: IdentityKey }, - #[error("Not enough funds sent for gateway bond. (received {received}, minimum {minimum})")] + #[error("MIXNET ({}): Not enough funds sent for gateway bond. (received {received}, minimum {minimum})", line!())] InsufficientGatewayBond { received: u128, minimum: u128 }, - #[error("{owner} does not seem to own any mixnodes")] + #[error("MIXNET ({}): {owner} does not seem to own any mixnodes", line!())] NoAssociatedMixNodeBond { owner: Addr }, - #[error("{owner} does not seem to own any gateways")] + #[error("MIXNET ({}): {owner} does not seem to own any gateways", line!())] NoAssociatedGatewayBond { owner: Addr }, - #[error("Unauthorized")] + #[error("MIXNET ({}): Unauthorized", line!())] Unauthorized, - #[error("Wrong coin denomination, you must send {}", DENOM)] + #[error("MIXNET ({}): Wrong coin denomination, you must send {}", line!(), DENOM)] WrongDenom, - #[error("Received multiple coin types during staking")] + #[error("MIXNET ({}): Received multiple coin types during staking", line!())] MultipleDenoms, - #[error("No coin was sent for the bonding, you must send {}", DENOM)] + #[error("MIXNET ({}): No coin was sent for the bonding, you must send {}", line!(), DENOM)] NoBondFound, - #[error("Provided active set size is bigger than the rewarded set")] + #[error("MIXNET ({}): Provided active set size is bigger than the rewarded set", line!())] InvalidActiveSetSize, - #[error("Provided active set size is zero")] + #[error("MIXNET ({}): Provided active set size is zero", line!())] ZeroActiveSet, - #[error("Provided rewarded set size is zero")] + #[error("MIXNET ({}): Provided rewarded set size is zero", line!())] ZeroRewardedSet, - #[error("This address has already bonded a mixnode")] + #[error("MIXNET ({}): This address has already bonded a mixnode", line!())] AlreadyOwnsMixnode, - #[error("This address has already bonded a gateway")] + #[error("MIXNET ({}): This address has already bonded a gateway", line!())] AlreadyOwnsGateway, - #[error("Mixnode with this identity already exists. Its owner is {owner}")] + #[error("MIXNET ({}): Mixnode with this identity already exists. Its owner is {owner}", line!())] DuplicateMixnode { owner: Addr }, - #[error("Gateway with this identity already exists. Its owner is {owner}")] + #[error("MIXNET ({}): Gateway with this identity already exists. Its owner is {owner}", line!())] DuplicateGateway { owner: Addr }, - #[error("No funds were provided for the delegation")] + #[error("MIXNET ({}): No funds were provided for the delegation", line!())] EmptyDelegation, - #[error("Could not find any delegation information associated with mixnode {identity} for {address}")] + #[error("MIXNET ({}): Could not find any delegation information associated with mixnode {identity} for {address}", line!())] NoMixnodeDelegationFound { identity: IdentityKey, address: Addr, }, - #[error("We tried to remove more funds then are available in the Reward pool. Wanted to remove {to_remove}, but have only {reward_pool}")] + #[error("MIXNET ({}): We tried to remove more funds then are available in the Reward pool. Wanted to remove {to_remove}, but have only {reward_pool}", line!())] OutOfFunds { to_remove: u128, reward_pool: u128 }, - #[error("Received invalid interval id. Expected {expected}, received {received}")] + #[error("MIXNET ({}): Received invalid interval id. Expected {expected}, received {received}", line!())] InvalidIntervalId { received: u32, expected: u32 }, - #[error("Mixnode {identity} has already been rewarded during the current rewarding interval")] + #[error("MIXNET ({}): Mixnode {identity} has already been rewarded during the current rewarding interval", line!())] MixnodeAlreadyRewarded { identity: IdentityKey }, - #[error("Some of mixnodes {identity} delegators are still pending reward")] + #[error("MIXNET ({}): Some of mixnodes {identity} delegators are still pending reward", line!())] DelegatorsPendingReward { identity: IdentityKey }, - #[error("Mixnode's {identity} operator has not been rewarded yet - cannot perform delegator rewarding until that happens")] + #[error("MIXNET ({}): Mixnode's {identity} operator has not been rewarded yet - cannot perform delegator rewarding until that happens", line!())] MixnodeOperatorNotRewarded { identity: IdentityKey }, - #[error("Proxy address mismatch, expected {existing}, got {incoming}")] + #[error("MIXNET ({}): Proxy address mismatch, expected {existing}, got {incoming}", line!())] ProxyMismatch { existing: String, incoming: String }, - #[error("Failed to recover ed25519 public key from its base58 representation - {0}")] + #[error("MIXNET ({}): Failed to recover ed25519 public key from its base58 representation - {0}", line!())] MalformedEd25519IdentityKey(String), - #[error("Failed to recover ed25519 signature from its base58 representation - {0}")] + #[error("MIXNET ({}): Failed to recover ed25519 signature from its base58 representation - {0}", line!())] MalformedEd25519Signature(String), - #[error("Provided ed25519 signature did not verify correctly")] + #[error("MIXNET ({}): Provided ed25519 signature did not verify correctly", line!())] InvalidEd25519Signature, - #[error("Profit margin percent needs to be an integer in range [0, 100], received {0}")] + #[error("MIXNET ({}): Profit margin percent needs to be an integer in range [0, 100], received {0}", line!())] InvalidProfitMarginPercent(u8), - #[error("Rewarded set height not set, was rewarding set determined?")] + #[error("MIXNET ({}): Rewarded set height not set, was rewarding set determined?", line!())] RewardSetHeightMapEmpty, - #[error("Received unexpected value for the active set. Got: {received}, expected: {expected}")] + #[error("MIXNET ({}): Received unexpected value for the active set. Got: {received}, expected: {expected}", line!())] UnexpectedActiveSetSize { received: u32, expected: u32 }, - #[error("Received unexpected value for the rewarded set. Got: {received}, expected at most: {expected}")] + #[error("MIXNET ({}): Received unexpected value for the rewarded set. Got: {received}, expected at most: {expected}", line!())] UnexpectedRewardedSetSize { received: u32, expected: u32 }, - #[error("There hasn't been sufficient delay since last rewarded set update. It was last updated at height {last_update}. The delay is {minimum_delay}. The current block height is {current_height}")] + #[error("MIXNET ({}): There hasn't been sufficient delay since last rewarded set update. It was last updated at height {last_update}. The delay is {minimum_delay}. The current block height is {current_height}", line!())] TooFrequentRewardedSetUpdate { last_update: u64, minimum_delay: u64, current_height: u64, }, - #[error("Can't change to the desired interval as it's not in progress yet. It starts at {interval_start} and finishes at {interval_end}, while the current block time is {current_block_time}")] + #[error("MIXNET ({}): Can't change to the desired interval as it's not in progress yet. It starts at {interval_start} and finishes at {interval_end}, while the current block time is {current_block_time}", line!())] IntervalNotInProgress { current_block_time: u64, interval_start: i64, diff --git a/contracts/mixnet/src/gateways/transactions.rs b/contracts/mixnet/src/gateways/transactions.rs index 6426b44357..f032f88efb 100644 --- a/contracts/mixnet/src/gateways/transactions.rs +++ b/contracts/mixnet/src/gateways/transactions.rs @@ -7,11 +7,12 @@ use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::support::helpers::{ensure_no_existing_bond, validate_node_identity_signature}; use config::defaults::DENOM; use cosmwasm_std::{ - coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128, + wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128, }; use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event}; use mixnet_contract_common::{Gateway, GatewayBond, Layer}; -use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::one_unym; pub fn try_add_gateway( deps: DepsMut, @@ -176,7 +177,7 @@ pub(crate) fn _try_remove_gateway( amount: gateway_bond.pledge_amount(), }; - let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?; + let track_unbond_message = wasm_execute(proxy, &msg, vec![one_unym()])?; response = response.add_message(track_unbond_message); } diff --git a/contracts/mixnet/src/mixnodes/transactions.rs b/contracts/mixnet/src/mixnodes/transactions.rs index 252128324a..f6fbac8371 100644 --- a/contracts/mixnet/src/mixnodes/transactions.rs +++ b/contracts/mixnet/src/mixnodes/transactions.rs @@ -9,11 +9,12 @@ use crate::mixnodes::storage::StoredMixnodeBond; use crate::support::helpers::{ensure_no_existing_bond, validate_node_identity_signature}; use config::defaults::DENOM; use cosmwasm_std::{ - coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128, + wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128, }; use mixnet_contract_common::events::{new_mixnode_bonding_event, new_mixnode_unbonding_event}; use mixnet_contract_common::MixNode; -use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg; +use vesting_contract_common::one_unym; pub fn try_add_mixnode( deps: DepsMut, @@ -192,7 +193,7 @@ pub(crate) fn _try_remove_mixnode( // decrement layer count mixnet_params_storage::decrement_layer_count(deps.storage, mixnode_bond.layer)?; - let mut response = Response::new().add_message(return_tokens); + let mut response = Response::new(); if let Some(proxy) = &proxy { let msg = VestingContractExecuteMsg::TrackUnbondMixnode { @@ -200,10 +201,12 @@ pub(crate) fn _try_remove_mixnode( amount: mixnode_bond.pledge_amount(), }; - let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?; + let track_unbond_message = wasm_execute(proxy, &msg, vec![one_unym()])?; response = response.add_message(track_unbond_message); } + let response = response.add_message(return_tokens); + Ok(response.add_event(new_mixnode_unbonding_event( &owner, &proxy, diff --git a/contracts/vesting/src/contract.rs b/contracts/vesting/src/contract.rs index 46e17a339b..22647516c1 100644 --- a/contracts/vesting/src/contract.rs +++ b/contracts/vesting/src/contract.rs @@ -1,5 +1,4 @@ use crate::errors::ContractError; -use crate::messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification}; use crate::storage::{account_from_address, ADMIN, MIXNET_CONTRACT_ADDRESS}; use crate::traits::{ DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount, @@ -16,6 +15,9 @@ use vesting_contract_common::events::{ new_staking_address_update_event, new_track_gateway_unbond_event, new_track_mixnode_unbond_event, new_track_undelegation_event, new_vested_coins_withdraw_event, }; +use vesting_contract_common::messages::{ + ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification, +}; #[entry_point] pub fn instantiate( @@ -43,6 +45,9 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { + ExecuteMsg::UpdateMixnetAddress { address } => { + try_update_mixnet_address(address, info, deps) + } ExecuteMsg::DelegateToMixnode { mix_identity, amount, @@ -97,7 +102,20 @@ pub fn execute( } } -// Only owner +// Only contract admin, set at init +pub fn try_update_mixnet_address( + address: String, + info: MessageInfo, + deps: DepsMut, +) -> Result { + if info.sender != ADMIN.load(deps.storage)? { + return Err(ContractError::NotAdmin(info.sender.as_str().to_string())); + } + MIXNET_CONTRACT_ADDRESS.save(deps.storage, &address)?; + Ok(Response::default()) +} + +// Only contract owner of vesting account pub fn try_withdraw_vested_coins( amount: Coin, env: Env, @@ -311,14 +329,31 @@ fn try_create_periodic_vesting_account( periods, deps.storage, )?; - Ok( - Response::new().add_event(new_periodic_vesting_account_event( - &owner_address, - &coin, - &staking_address, - start_time, - )), - ) + + let mut response = Response::new(); + + let send_tokens_owner = BankMsg::Send { + to_address: owner_address.as_str().to_string(), + amount: vec![Coin::new(1_000_000, DENOM)], + }; + + response = response.add_message(send_tokens_owner); + + if let Some(staking_address) = staking_address.as_ref() { + let send_tokens_staking = BankMsg::Send { + to_address: staking_address.clone().as_str().to_string(), + amount: vec![Coin::new(1_000_000, DENOM)], + }; + + response = response.add_message(send_tokens_staking); + } + + Ok(response.add_event(new_periodic_vesting_account_event( + &owner_address, + &coin, + &staking_address, + start_time, + ))) } #[entry_point] diff --git a/contracts/vesting/src/errors.rs b/contracts/vesting/src/errors.rs index fa1b8fc5f7..fb9662b6c2 100644 --- a/contracts/vesting/src/errors.rs +++ b/contracts/vesting/src/errors.rs @@ -4,42 +4,42 @@ use thiserror::Error; #[derive(Error, Debug, PartialEq)] pub enum ContractError { - #[error("{0}")] + #[error("VESTING ({}): {0}", line!())] Std(#[from] StdError), - #[error("Account does not exist - {0}")] + #[error("VESTING ({}): Account does not exist - {0}", line!())] NoAccountForAddress(String), - #[error("Only admin can perform this action, {0} is not admin")] + #[error("VESTING ({}): Only admin can perform this action, {0} is not admin", line!())] NotAdmin(String), - #[error("Balance not found for existing account ({0}), this is a bug")] + #[error("VESTING ({}): Balance not found for existing account ({0}), this is a bug", line!())] NoBalanceForAddress(String), - #[error("Insufficient balance for address {0} -> {1}")] + #[error("VESTING ({}): Insufficient balance for address {0} -> {1}", line!())] InsufficientBalance(String, u128), - #[error("Insufficient spendable balance for address {0} -> {1}")] + #[error("VESTING ({}): Insufficient spendable balance for address {0} -> {1}", line!())] InsufficientSpendable(String, u128), #[error( - "Only delegation owner can perform delegation actions, {0} is not the delegation owner" - )] + "VESTING ({}):Only delegation owner can perform delegation actions, {0} is not the delegation owner" + , line!())] NotDelegate(String), - #[error("Total vesting amount is inprobably low -> {0}, this is likely an error")] + #[error("VESTING ({}): Total vesting amount is inprobably low -> {0}, this is likely an error", line!())] ImprobableVestingAmount(u128), - #[error("Address {0} has already bonded a node")] + #[error("VESTING ({}): Address {0} has already bonded a node", line!())] AlreadyBonded(String), - #[error("Received empty funds vector")] + #[error("VESTING ({}): Received empty funds vector", line!())] EmptyFunds, - #[error("Received wrong denom: {0}, expected {1}")] + #[error("VESTING ({}): Received wrong denom: {0}, expected {1}", line!())] WrongDenom(String, String), - #[error("Received multiple denoms, expected 1")] + #[error("VESTING ({}): Received multiple denoms, expected 1", line!())] MultipleDenoms, - #[error("No delegations found for account {0}, mix_identity {1}")] + #[error("VESTING ({}): No delegations found for account {0}, mix_identity {1}", line!())] NoSuchDelegation(Addr, IdentityKey), - #[error("Only mixnet contract can perform this operation, got {0}")] + #[error("VESTING ({}): Only mixnet contract can perform this operation, got {0}", line!())] NotMixnetContract(Addr), - #[error("Calculation underflowed")] + #[error("VESTING ({}): Calculation underflowed", line!())] Underflow, - #[error("No bond found for account {0}")] + #[error("VESTING ({}): No bond found for account {0}", line!())] NoBondFound(String), - #[error("Action can only be executed by account owner -> {0}")] + #[error("VESTING ({}): Action can only be executed by account owner -> {0}", line!())] NotOwner(String), - #[error("Invalid address: {0}")] + #[error("VESTING ({}): Invalid address: {0}", line!())] InvalidAddress(String), } diff --git a/contracts/vesting/src/lib.rs b/contracts/vesting/src/lib.rs index 40e1d3a682..4e57232fe5 100644 --- a/contracts/vesting/src/lib.rs +++ b/contracts/vesting/src/lib.rs @@ -1,6 +1,5 @@ pub mod contract; mod errors; -pub mod messages; mod storage; mod support; mod traits; diff --git a/contracts/vesting/src/support/tests.rs b/contracts/vesting/src/support/tests.rs index ce0708bfde..facefb00f8 100644 --- a/contracts/vesting/src/support/tests.rs +++ b/contracts/vesting/src/support/tests.rs @@ -1,11 +1,11 @@ #[cfg(test)] pub mod helpers { use crate::contract::instantiate; - use crate::messages::{InitMsg, VestingSpecification}; use crate::vesting::{populate_vesting_periods, Account}; use config::defaults::DENOM; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; use cosmwasm_std::{Addr, Coin, Empty, Env, MemoryStorage, OwnedDeps, Storage, Uint128}; + use vesting_contract_common::messages::{InitMsg, VestingSpecification}; pub fn init_contract() -> OwnedDeps> { let mut deps = mock_dependencies(); diff --git a/contracts/vesting/src/vesting/account/delegating_account.rs b/contracts/vesting/src/vesting/account/delegating_account.rs index ce61f5598d..00aeeaaf7d 100644 --- a/contracts/vesting/src/vesting/account/delegating_account.rs +++ b/contracts/vesting/src/vesting/account/delegating_account.rs @@ -2,13 +2,13 @@ use crate::errors::ContractError; use crate::storage::save_delegation; use crate::storage::MIXNET_CONTRACT_ADDRESS; use crate::traits::DelegatingAccount; -use config::defaults::DENOM; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; use mixnet_contract_common::ExecuteMsg as MixnetExecuteMsg; use mixnet_contract_common::IdentityKey; use vesting_contract_common::events::{ new_vesting_delegation_event, new_vesting_undelegation_event, }; +use vesting_contract_common::one_unym; use super::Account; @@ -70,10 +70,7 @@ impl DelegatingAccount for Account { let undelegate_from_mixnode = wasm_execute( MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, - vec![Coin { - amount: Uint128::new(0), - denom: DENOM.to_string(), - }], + vec![one_unym()], )?; Ok(Response::new() diff --git a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs index 6e2d7c5f36..3500e55b57 100644 --- a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs @@ -7,6 +7,7 @@ use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway}; use vesting_contract_common::events::{ new_vesting_gateway_bonding_event, new_vesting_gateway_unbonding_event, }; +use vesting_contract_common::one_unym; use super::Account; @@ -64,7 +65,11 @@ impl GatewayBondingAccount for Account { }; if let Some(_bond) = self.load_gateway_pledge(storage)? { - let unbond_msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?; + let unbond_msg = wasm_execute( + MIXNET_CONTRACT_ADDRESS.load(storage)?, + &msg, + vec![one_unym()], + )?; Ok(Response::new() .add_message(unbond_msg) diff --git a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs index 24a7c8dc9b..1c4f148a27 100644 --- a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs @@ -7,6 +7,7 @@ use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, MixNode}; use vesting_contract_common::events::{ new_vesting_mixnode_bonding_event, new_vesting_mixnode_unbonding_event, }; +use vesting_contract_common::one_unym; use super::Account; @@ -64,7 +65,11 @@ impl MixnodeBondingAccount for Account { }; if self.load_mixnode_pledge(storage)?.is_some() { - let unbond_msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?; + let unbond_msg = wasm_execute( + MIXNET_CONTRACT_ADDRESS.load(storage)?, + &msg, + vec![one_unym()], + )?; Ok(Response::new() .add_message(unbond_msg) diff --git a/contracts/vesting/src/vesting/mod.rs b/contracts/vesting/src/vesting/mod.rs index 205bba3c1e..3d084afa01 100644 --- a/contracts/vesting/src/vesting/mod.rs +++ b/contracts/vesting/src/vesting/mod.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; mod account; pub use account::*; -use crate::messages::VestingSpecification; +use vesting_contract_common::messages::VestingSpecification; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct VestingPeriod { @@ -43,7 +43,6 @@ pub fn populate_vesting_periods( #[cfg(test)] mod tests { use crate::contract::execute; - use crate::messages::ExecuteMsg; use crate::storage::load_account; use crate::support::tests::helpers::{init_contract, vesting_account_fixture}; use crate::traits::DelegatingAccount; @@ -53,6 +52,7 @@ mod tests { use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{coins, Addr, Coin, Timestamp, Uint128}; use mixnet_contract_common::{Gateway, MixNode}; + use vesting_contract_common::messages::ExecuteMsg; #[test] fn test_account_creation() { diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 25fafed2e9..5d6b0c3e31 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -5338,6 +5338,7 @@ dependencies = [ "url", "validator-api-requests", "vesting-contract", + "vesting-contract-common", ] [[package]] @@ -5382,7 +5383,12 @@ dependencies = [ name = "vesting-contract-common" version = "0.1.0" dependencies = [ + "config", "cosmwasm-std", + "cw-storage-plus", + "mixnet-contract-common", + "schemars", + "serde", ] [[package]] diff --git a/nym-wallet/src/types/rust/operation.ts b/nym-wallet/src/types/rust/operation.ts index ddb0cfb82f..cd79c2c3ea 100644 --- a/nym-wallet/src/types/rust/operation.ts +++ b/nym-wallet/src/types/rust/operation.ts @@ -27,4 +27,5 @@ export type Operation = | "CreatePeriodicVestingAccount" | "AdvanceCurrentInterval" | "WriteRewardedSet" - | "ClearRewardedSet"; \ No newline at end of file + | "ClearRewardedSet" + | "UpdateMixnetAddress"; \ No newline at end of file