From d83cab757a2f051674c41360df0210dad8bf398f Mon Sep 17 00:00:00 2001 From: Dzung Do Date: Thu, 11 Jul 2024 22:09:52 +0700 Subject: [PATCH 01/17] add unstake custom message --- Cargo.lock | 1 + .../provider/native-staking-proxy/Cargo.toml | 1 + .../native-staking-proxy/src/contract.rs | 3 ++- packages/bindings/src/msg.rs | 17 +++++++++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index b2f0dab3..9fe473b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -676,6 +676,7 @@ dependencies = [ "cw2", "derivative", "mesh-apis", + "mesh-bindings", "mesh-burn", "mesh-native-staking", "mesh-vault", diff --git a/contracts/provider/native-staking-proxy/Cargo.toml b/contracts/provider/native-staking-proxy/Cargo.toml index a8210310..6328e09e 100644 --- a/contracts/provider/native-staking-proxy/Cargo.toml +++ b/contracts/provider/native-staking-proxy/Cargo.toml @@ -21,6 +21,7 @@ mt = ["library", "sylvia/mt"] [dependencies] mesh-apis = { workspace = true } mesh-burn = { workspace = true } +mesh-bindings = { workspace = true } sylvia = { workspace = true } cosmwasm-schema = { workspace = true } diff --git a/contracts/provider/native-staking-proxy/src/contract.rs b/contracts/provider/native-staking-proxy/src/contract.rs index 89aa6240..decff2c8 100644 --- a/contracts/provider/native-staking-proxy/src/contract.rs +++ b/contracts/provider/native-staking-proxy/src/contract.rs @@ -7,6 +7,7 @@ use cw2::set_contract_version; use cw_storage_plus::Item; use cw_utils::{must_pay, nonpayable}; +use mesh_bindings::ProviderMsg; use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx}; use sylvia::{contract, schemars}; @@ -288,7 +289,7 @@ impl NativeStakingProxyContract<'_> { ContractError::InvalidDenom(amount.denom) ); - let msg = StakingMsg::Undelegate { validator, amount }; + let msg = ProviderMsg::Unstake { validator, amount }; Ok(Response::new().add_message(msg)) } diff --git a/packages/bindings/src/msg.rs b/packages/bindings/src/msg.rs index 70e94ffe..fde80955 100644 --- a/packages/bindings/src/msg.rs +++ b/packages/bindings/src/msg.rs @@ -87,6 +87,12 @@ pub enum ProviderMsg { /// If these conditions are met, it will instantly unbond /// amount.amount tokens from the vault contract. Unbond { delegator: String, amount: Coin }, + /// Unstake ensures that amount.denom is the native staking denom and + /// the calling contract is the native staking proxy contract. + /// + /// If these conditions are met, it will instantly unstake + /// amount.amount tokens from the native staking proxy contract. + Unstake { validator: String, amount: Coin }, } impl ProviderMsg { @@ -111,6 +117,17 @@ impl ProviderMsg { amount: coin, } } + + pub fn unstake(denom: &str, validator: &str, amount: impl Into) -> ProviderMsg { + let coin = Coin { + amount: amount.into(), + denom: denom.into(), + }; + ProviderMsg::Unstake { + validator: validator.to_string(), + amount: coin, + } + } } impl From for CosmosMsg { From e7b020c1ad45d52e923c55ca527d0b8546d220fa Mon Sep 17 00:00:00 2001 From: Dzung Do Date: Fri, 12 Jul 2024 16:50:09 +0700 Subject: [PATCH 02/17] add native staking proxy mock --- .../external-staking/src/multitest.rs | 2 +- .../native-staking-proxy/src/contract.rs | 21 +- .../provider/native-staking-proxy/src/lib.rs | 1 + .../provider/native-staking-proxy/src/mock.rs | 503 ++++++++++++++++++ .../native-staking-proxy/src/multitest.rs | 23 +- .../provider/native-staking/src/multitest.rs | 8 +- contracts/provider/vault/src/multitest.rs | 8 +- 7 files changed, 535 insertions(+), 31 deletions(-) create mode 100644 contracts/provider/native-staking-proxy/src/mock.rs diff --git a/contracts/provider/external-staking/src/multitest.rs b/contracts/provider/external-staking/src/multitest.rs index ed8298a2..84092f45 100644 --- a/contracts/provider/external-staking/src/multitest.rs +++ b/contracts/provider/external-staking/src/multitest.rs @@ -6,7 +6,7 @@ use cosmwasm_std::{coin, coins, to_json_binary, Decimal, Uint128}; use cw_multi_test::App as MtApp; use mesh_native_staking::contract::sv::mt::CodeId as NativeStakingCodeId; use mesh_native_staking::contract::sv::InstantiateMsg as NativeStakingInstantiateMsg; -use mesh_native_staking_proxy::contract::sv::mt::CodeId as NativeStakingProxyCodeId; +use mesh_native_staking_proxy::mock::sv::mt::CodeId as NativeStakingProxyCodeId; use mesh_vault::mock::sv::mt::{CodeId as VaultCodeId, VaultMockProxy}; use mesh_vault::mock::VaultMock; use mesh_vault::msg::{LocalStakingInfo, StakingInitInfo}; diff --git a/contracts/provider/native-staking-proxy/src/contract.rs b/contracts/provider/native-staking-proxy/src/contract.rs index decff2c8..9b01af2f 100644 --- a/contracts/provider/native-staking-proxy/src/contract.rs +++ b/contracts/provider/native-staking-proxy/src/contract.rs @@ -7,7 +7,7 @@ use cw2::set_contract_version; use cw_storage_plus::Item; use cw_utils::{must_pay, nonpayable}; -use mesh_bindings::ProviderMsg; +use mesh_bindings::{ProviderCustomMsg, ProviderMsg}; use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx}; use sylvia::{contract, schemars}; @@ -27,6 +27,7 @@ pub struct NativeStakingProxyContract<'a> { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] #[sv::error(ContractError)] +#[sv::custom(msg=ProviderCustomMsg)] impl NativeStakingProxyContract<'_> { pub const fn new() -> Self { Self { @@ -44,7 +45,7 @@ impl NativeStakingProxyContract<'_> { denom: String, owner: String, validator: String, - ) -> Result { + ) -> Result, ContractError> { let config = Config { denom, parent: ctx.info.sender.clone(), @@ -77,7 +78,7 @@ impl NativeStakingProxyContract<'_> { /// Stakes the tokens from `info.funds` to the given validator. /// Can only be called by the parent contract #[sv::msg(exec)] - fn stake(&self, ctx: ExecCtx, validator: String) -> Result { + fn stake(&self, ctx: ExecCtx, validator: String) -> Result, ContractError> { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.parent, ctx.info.sender, ContractError::Unauthorized {}); @@ -98,7 +99,7 @@ impl NativeStakingProxyContract<'_> { ctx: ExecCtx, validator: Option, amount: Coin, - ) -> Result { + ) -> Result, ContractError> { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.parent, ctx.info.sender, ContractError::Unauthorized {}); @@ -187,7 +188,7 @@ impl NativeStakingProxyContract<'_> { src_validator: String, dst_validator: String, amount: Coin, - ) -> Result { + ) -> Result, ContractError> { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); @@ -214,7 +215,7 @@ impl NativeStakingProxyContract<'_> { ctx: ExecCtx, proposal_id: u64, vote: VoteOption, - ) -> Result { + ) -> Result, ContractError> { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); @@ -231,7 +232,7 @@ impl NativeStakingProxyContract<'_> { ctx: ExecCtx, proposal_id: u64, vote: Vec, - ) -> Result { + ) -> Result, ContractError> { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); @@ -248,7 +249,7 @@ impl NativeStakingProxyContract<'_> { /// send the tokens to the caller. /// NOTE: must make sure not to release unbonded tokens #[sv::msg(exec)] - fn withdraw_rewards(&self, ctx: ExecCtx) -> Result { + fn withdraw_rewards(&self, ctx: ExecCtx) -> Result, ContractError> { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); @@ -277,7 +278,7 @@ impl NativeStakingProxyContract<'_> { ctx: ExecCtx, validator: String, amount: Coin, - ) -> Result { + ) -> Result, ContractError> { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); @@ -297,7 +298,7 @@ impl NativeStakingProxyContract<'_> { /// This will go back to the parent via `release_proxy_stake`. /// Errors if the proxy doesn't have any liquid tokens #[sv::msg(exec)] - fn release_unbonded(&self, ctx: ExecCtx) -> Result { + fn release_unbonded(&self, ctx: ExecCtx) -> Result, ContractError> { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); diff --git a/contracts/provider/native-staking-proxy/src/lib.rs b/contracts/provider/native-staking-proxy/src/lib.rs index 684e3b37..3431e367 100644 --- a/contracts/provider/native-staking-proxy/src/lib.rs +++ b/contracts/provider/native-staking-proxy/src/lib.rs @@ -3,5 +3,6 @@ pub mod error; pub mod msg; #[cfg(test)] mod multitest; +pub mod mock; pub mod native_staking_callback; mod state; diff --git a/contracts/provider/native-staking-proxy/src/mock.rs b/contracts/provider/native-staking-proxy/src/mock.rs new file mode 100644 index 00000000..19cd50c1 --- /dev/null +++ b/contracts/provider/native-staking-proxy/src/mock.rs @@ -0,0 +1,503 @@ +use cosmwasm_std::WasmMsg::Execute; +use cosmwasm_std::{ + coin, ensure_eq, to_json_binary, Coin, DistributionMsg, GovMsg, Response, StakingMsg, + VoteOption, WeightedVoteOption, +}; +use cw2::set_contract_version; +use cw_storage_plus::Item; + +use cw_utils::{must_pay, nonpayable}; +use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx}; +use sylvia::{contract, schemars}; + +use crate::error::ContractError; +use crate::msg::{ConfigResponse, OwnerMsg}; +use crate::native_staking_callback; +use crate::state::Config; + +pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +pub struct NativeStakingProxyMock<'a> { + config: Item<'a, Config>, + burned: Item<'a, u128>, +} + +#[cfg_attr(not(feature = "library"), sylvia::entry_points)] +#[contract] +#[sv::error(ContractError)] +impl NativeStakingProxyMock<'_> { + pub const fn new() -> Self { + Self { + config: Item::new("config"), + burned: Item::new("burned"), + } + } + + /// The caller of the instantiation will be the native-staking contract. + /// We stake `funds.info` on the given validator + #[sv::msg(instantiate)] + pub fn instantiate( + &self, + ctx: InstantiateCtx, + denom: String, + owner: String, + validator: String, + ) -> Result { + let config = Config { + denom, + parent: ctx.info.sender.clone(), + owner: ctx.deps.api.addr_validate(&owner)?, + }; + self.config.save(ctx.deps.storage, &config)?; + set_contract_version(ctx.deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + // Set burned stake to zero + self.burned.save(ctx.deps.storage, &0)?; + + // Stake info.funds on validator + let exec_ctx = ExecCtx { + deps: ctx.deps, + env: ctx.env, + info: ctx.info, + }; + let res = self.stake(exec_ctx, validator)?; + + // Set owner as recipient of future withdrawals + let set_withdrawal = DistributionMsg::SetWithdrawAddress { + address: config.owner.into_string(), + }; + + // Pass owner to caller's reply handler + let owner_msg = to_json_binary(&OwnerMsg { owner })?; + Ok(res.add_message(set_withdrawal).set_data(owner_msg)) + } + + /// Stakes the tokens from `info.funds` to the given validator. + /// Can only be called by the parent contract + #[sv::msg(exec)] + fn stake(&self, ctx: ExecCtx, validator: String) -> Result { + let cfg = self.config.load(ctx.deps.storage)?; + ensure_eq!(cfg.parent, ctx.info.sender, ContractError::Unauthorized {}); + + let amount = must_pay(&ctx.info, &cfg.denom)?; + + let amount = coin(amount.u128(), cfg.denom); + let msg = StakingMsg::Delegate { validator, amount }; + + Ok(Response::new().add_message(msg)) + } + + /// Burn `amount` tokens from the given validator, if set. + /// If `validator` is not set, undelegate evenly from all validators the user has stake. + /// Can only be called by the parent contract + #[sv::msg(exec)] + fn burn( + &self, + ctx: ExecCtx, + validator: Option, + amount: Coin, + ) -> Result { + let cfg = self.config.load(ctx.deps.storage)?; + ensure_eq!(cfg.parent, ctx.info.sender, ContractError::Unauthorized {}); + + nonpayable(&ctx.info)?; + + // Check denom + ensure_eq!( + amount.denom, + cfg.denom, + ContractError::InvalidDenom(amount.denom) + ); + + let delegations = match validator { + Some(validator) => { + match ctx + .deps + .querier + .query_delegation(ctx.env.contract.address.clone(), validator)? + .map(|full_delegation| { + ( + full_delegation.validator, + full_delegation.amount.amount.u128(), + ) + }) { + Some(delegation) => vec![delegation], + None => vec![], + } + } + None => ctx + .deps + .querier + .query_all_delegations(ctx.env.contract.address.clone())? + .iter() + .map(|delegation| { + ( + delegation.validator.clone(), + delegation.amount.amount.u128(), + ) + }) + .collect::>(), + }; + + // Error if no validators + if delegations.is_empty() { + return Err(ContractError::InsufficientDelegations( + ctx.env.contract.address.to_string(), + amount.amount, + )); + } + + let (burned, burns) = mesh_burn::distribute_burn(&delegations, amount.amount.u128()); + + // Bail if we don't have enough delegations + if burned < amount.amount.u128() { + return Err(ContractError::InsufficientDelegations( + ctx.env.contract.address.to_string(), + amount.amount, + )); + } + + // Build undelegate messages + // FIXME: Use an "immediate unbonding" message for undelegation + let mut undelegate_msgs = vec![]; + for (validator, burn_amount) in burns { + let undelegate_msg = StakingMsg::Undelegate { + validator: validator.to_string(), + amount: coin(burn_amount, &cfg.denom), + }; + undelegate_msgs.push(undelegate_msg); + } + + // Accounting trick to avoid burning stake + self.burned.update(ctx.deps.storage, |old| { + Ok::<_, ContractError>(old + amount.amount.u128()) + })?; + + Ok(Response::new().add_messages(undelegate_msgs)) + } + + /// Re-stakes the given amount from the one validator to another on behalf of the calling user. + /// Returns an error if the user doesn't have such stake + #[sv::msg(exec)] + fn restake( + &self, + ctx: ExecCtx, + src_validator: String, + dst_validator: String, + amount: Coin, + ) -> Result { + let cfg = self.config.load(ctx.deps.storage)?; + ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); + + nonpayable(&ctx.info)?; + + ensure_eq!( + amount.denom, + cfg.denom, + ContractError::InvalidDenom(amount.denom) + ); + + let msg = StakingMsg::Redelegate { + src_validator, + dst_validator, + amount, + }; + Ok(Response::new().add_message(msg)) + } + + /// Vote with the user's stake (over all delegations) + #[sv::msg(exec)] + fn vote( + &self, + ctx: ExecCtx, + proposal_id: u64, + vote: VoteOption, + ) -> Result { + let cfg = self.config.load(ctx.deps.storage)?; + ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); + + nonpayable(&ctx.info)?; + + let msg = GovMsg::Vote { proposal_id, vote }; + Ok(Response::new().add_message(msg)) + } + + /// Vote with the user's stake (over all delegations) + #[sv::msg(exec)] + fn vote_weighted( + &self, + ctx: ExecCtx, + proposal_id: u64, + vote: Vec, + ) -> Result { + let cfg = self.config.load(ctx.deps.storage)?; + ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); + + nonpayable(&ctx.info)?; + + let msg = GovMsg::VoteWeighted { + proposal_id, + options: vote, + }; + Ok(Response::new().add_message(msg)) + } + + /// If the caller has any delegations, withdraw all rewards from those delegations and + /// send the tokens to the caller. + /// NOTE: must make sure not to release unbonded tokens + #[sv::msg(exec)] + fn withdraw_rewards(&self, ctx: ExecCtx) -> Result { + let cfg = self.config.load(ctx.deps.storage)?; + ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); + + nonpayable(&ctx.info)?; + + // Withdraw all delegations to the owner (already set as withdrawal address in instantiate) + let msgs: Vec<_> = ctx + .deps + .querier + .query_all_delegations(ctx.env.contract.address)? + .into_iter() + .map(|delegation| DistributionMsg::WithdrawDelegatorReward { + validator: delegation.validator, + }) + .collect(); + let res = Response::new().add_messages(msgs); + Ok(res) + } + + /// Unstakes the given amount from the given validator on behalf of the calling user. + /// Returns an error if the user doesn't have such stake. + /// After the unbonding period, it will allow the user to claim the tokens (returning to vault) + #[sv::msg(exec)] + fn unstake( + &self, + ctx: ExecCtx, + validator: String, + amount: Coin, + ) -> Result { + let cfg = self.config.load(ctx.deps.storage)?; + ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); + + nonpayable(&ctx.info)?; + + ensure_eq!( + amount.denom, + cfg.denom, + ContractError::InvalidDenom(amount.denom) + ); + + let msg = StakingMsg::Undelegate { validator, amount }; + Ok(Response::new().add_message(msg)) + } + + /// Releases any tokens that have fully unbonded from a previous unstake. + /// This will go back to the parent via `release_proxy_stake`. + /// Errors if the proxy doesn't have any liquid tokens + #[sv::msg(exec)] + fn release_unbonded(&self, ctx: ExecCtx) -> Result { + let cfg = self.config.load(ctx.deps.storage)?; + ensure_eq!(cfg.owner, ctx.info.sender, ContractError::Unauthorized {}); + + nonpayable(&ctx.info)?; + + // Simply assumes all of our liquid assets are from unbondings + let balance = ctx + .deps + .querier + .query_balance(ctx.env.contract.address, cfg.denom)?; + // But discount burned stake + // FIXME: this is not accurate, as it doesn't take into account the unbonding period + let burned = self.burned.load(ctx.deps.storage)?; + let balance = coin(balance.amount.u128().saturating_sub(burned), &balance.denom); + + // Short circuit if there are no funds to send + if balance.amount.is_zero() { + return Ok(Response::new()); + } + + // Send them to the parent contract via `release_proxy_stake` + let msg = to_json_binary(&native_staking_callback::sv::ExecMsg::ReleaseProxyStake {})?; + + let wasm_msg = Execute { + contract_addr: cfg.parent.to_string(), + msg, + funds: vec![balance], + }; + Ok(Response::new().add_message(wasm_msg)) + } + + #[sv::msg(query)] + fn config(&self, ctx: QueryCtx) -> Result { + Ok(self.config.load(ctx.deps.storage)?) + } +} + +// Some unit tests, due to mt limitations / unsupported msgs +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::DistributionMsg::SetWithdrawAddress; + use cosmwasm_std::GovMsg::{Vote, VoteWeighted}; + use cosmwasm_std::{CosmosMsg, Decimal, DepsMut}; + + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::VoteOption::Yes; + use cw_utils::PaymentError; + + static OSMO: &str = "uosmo"; + static CREATOR: &str = "staking"; // The creator of the proxy contract(s) is the staking contract + static OWNER: &str = "user"; + static VALIDATOR: &str = "validator"; + + fn do_instantiate(deps: DepsMut) -> (ExecCtx, NativeStakingProxyMock) { + let contract = NativeStakingProxyMock::new(); + let mut ctx = InstantiateCtx { + deps, + env: mock_env(), + info: mock_info(CREATOR, &[coin(100, OSMO)]), + }; + contract + .instantiate( + ctx.branch(), + OSMO.to_owned(), + OWNER.to_owned(), + VALIDATOR.to_owned(), + ) + .unwrap(); + let exec_ctx = ExecCtx { + deps: ctx.deps, + info: mock_info(OWNER, &[]), + env: ctx.env, + }; + (exec_ctx, contract) + } + + // Extra checks of instantiate returned messages and data + #[test] + fn instantiating() { + let mut deps = mock_dependencies(); + let contract = NativeStakingProxyMock::new(); + let mut ctx = InstantiateCtx { + deps: deps.as_mut(), + env: mock_env(), + info: mock_info(CREATOR, &[coin(100, OSMO)]), + }; + let res = contract + .instantiate( + ctx.branch(), + OSMO.to_owned(), + OWNER.to_owned(), + VALIDATOR.to_owned(), + ) + .unwrap(); + + // Assert returned messages + assert_eq!( + res.messages[0].msg, + CosmosMsg::Staking(StakingMsg::Delegate { + validator: VALIDATOR.to_owned(), + amount: coin(100, OSMO) + }) + ); + assert_eq!( + res.messages[1].msg, + CosmosMsg::Distribution(SetWithdrawAddress { + address: OWNER.to_owned(), + }) + ); + + // Assert data payload + assert_eq!( + res.data.unwrap(), + to_json_binary(&OwnerMsg { + owner: OWNER.to_owned(), + }) + .unwrap() + ); + } + + #[test] + fn voting() { + let mut deps = mock_dependencies(); + let (mut ctx, contract) = do_instantiate(deps.as_mut()); + + // The owner can vote + let proposal_id = 1; + let vote = Yes; + let res = contract + .vote(ctx.branch(), proposal_id, vote.clone()) + .unwrap(); + assert_eq!(1, res.messages.len()); + // assert it's a governance vote + assert_eq!( + res.messages[0].msg, + cosmwasm_std::CosmosMsg::Gov(Vote { + proposal_id, + vote: vote.clone() + }) + ); + + // But not send funds + ctx.info = mock_info(OWNER, &[coin(1, OSMO)]); + let res = contract.vote(ctx.branch(), proposal_id, vote.clone()); + assert!(matches!( + res.unwrap_err(), + ContractError::Payment(PaymentError::NonPayable {}) + )); + + // Nobody else can vote + ctx.info = mock_info("somebody", &[]); + let res = contract.vote(ctx.branch(), proposal_id, vote.clone()); + assert!(matches!(res.unwrap_err(), ContractError::Unauthorized {})); + + // Not even the creator + ctx.info = mock_info(CREATOR, &[]); + let res = contract.vote(ctx, proposal_id, vote); + assert!(matches!(res.unwrap_err(), ContractError::Unauthorized {})); + } + + #[test] + fn weighted_voting() { + let mut deps = mock_dependencies(); + let (mut ctx, contract) = do_instantiate(deps.as_mut()); + + // The owner can weighted vote + let proposal_id = 2; + let vote = vec![WeightedVoteOption { + option: Yes, + weight: Decimal::percent(50), + }]; + let res = contract + .vote_weighted(ctx.branch(), proposal_id, vote.clone()) + .unwrap(); + assert_eq!(1, res.messages.len()); + // Assert it's a weighted governance vote + assert_eq!( + res.messages[0].msg, + cosmwasm_std::CosmosMsg::Gov(VoteWeighted { + proposal_id, + options: vote.clone() + }) + ); + + // But not send funds + ctx.info = mock_info(OWNER, &[coin(1, OSMO)]); + let res = contract.vote_weighted(ctx.branch(), proposal_id, vote.clone()); + assert!(matches!( + res.unwrap_err(), + ContractError::Payment(PaymentError::NonPayable {}) + )); + + // Nobody else can vote + ctx.info = mock_info("somebody", &[]); + let res = contract.vote_weighted(ctx.branch(), proposal_id, vote.clone()); + assert!(matches!(res.unwrap_err(), ContractError::Unauthorized {})); + + // Not even the creator + ctx.info = mock_info(CREATOR, &[]); + let res = contract.vote_weighted(ctx, proposal_id, vote); + assert!(matches!(res.unwrap_err(), ContractError::Unauthorized {})); + } +} \ No newline at end of file diff --git a/contracts/provider/native-staking-proxy/src/multitest.rs b/contracts/provider/native-staking-proxy/src/multitest.rs index 90baa916..b0e684f4 100644 --- a/contracts/provider/native-staking-proxy/src/multitest.rs +++ b/contracts/provider/native-staking-proxy/src/multitest.rs @@ -10,9 +10,8 @@ use mesh_vault::mock::sv::mt::VaultMockProxy; use mesh_vault::mock::VaultMock; use mesh_vault::msg::LocalStakingInfo; -use crate::contract; -use crate::contract::sv::mt::NativeStakingProxyContractProxy; -use crate::contract::NativeStakingProxyContract; +use crate::mock::sv::mt::NativeStakingProxyMockProxy; +use crate::mock::NativeStakingProxyMock; use crate::msg::ConfigResponse; const OSMO: &str = "uosmo"; @@ -62,7 +61,7 @@ fn setup<'app>( ) -> AnyResult>> { let vault_code = mesh_vault::mock::sv::mt::CodeId::store_code(app); let staking_code = mesh_native_staking::contract::sv::mt::CodeId::store_code(app); - let staking_proxy_code = contract::sv::mt::CodeId::store_code(app); + let staking_proxy_code = crate::mock::sv::mt::CodeId::store_code(app); // Instantiate vault msg let staking_init_info = mesh_vault::msg::StakingInitInfo { @@ -126,7 +125,7 @@ fn instantiation() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyMock<'_>> = Proxy::new(Addr::unchecked(proxy_addr), &app); // Check config @@ -170,7 +169,7 @@ fn staking() { let vault = setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyMock<'_>> = Proxy::new(Addr::unchecked(proxy_addr), &app); // Stake some more @@ -216,7 +215,7 @@ fn restaking() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyMock<'_>> = Proxy::new(Addr::unchecked(proxy_addr), &app); // Restake 30% to a different validator @@ -255,7 +254,7 @@ fn unstaking() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyMock<'_>> = Proxy::new(Addr::unchecked(proxy_addr), &app); // Unstake 50% @@ -310,7 +309,7 @@ fn burning() { setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyMock<'_>> = Proxy::new(Addr::unchecked(proxy_addr), &app); // Burn 10%, from validator @@ -377,7 +376,7 @@ fn burning_multiple_delegations() { setup(&app, owner, user, &validators).unwrap(); // Access staking proxy instance - let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyMock<'_>> = Proxy::new(Addr::unchecked(proxy_addr), &app); // Burn 15%, no validator specified @@ -458,7 +457,7 @@ fn releasing_unbonded() { let vault = setup(&app, owner, user, &[validator]).unwrap(); // Access staking proxy instance - let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyMock<'_>> = Proxy::new(Addr::unchecked(proxy_addr), &app); // Unstake 100% @@ -514,7 +513,7 @@ fn withdrawing_rewards() { let original_user_funds = app.app().wrap().query_balance(user, OSMO).unwrap(); // Access staking proxy instance - let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyMock<'_>> = Proxy::new(Addr::unchecked(proxy_addr), &app); // Advance time enough for rewards to accrue diff --git a/contracts/provider/native-staking/src/multitest.rs b/contracts/provider/native-staking/src/multitest.rs index fde0c43a..1463a9e8 100644 --- a/contracts/provider/native-staking/src/multitest.rs +++ b/contracts/provider/native-staking/src/multitest.rs @@ -6,10 +6,10 @@ use cw_multi_test::{App as MtApp, StakingInfo}; use sylvia::multitest::{App, Proxy}; use mesh_apis::local_staking_api::sv::mt::LocalStakingApiProxy; -use mesh_native_staking_proxy::contract::sv::mt::{ - CodeId as NativeStakingProxyCodeId, NativeStakingProxyContractProxy, +use mesh_native_staking_proxy::mock::sv::mt::{ + CodeId as NativeStakingProxyCodeId, NativeStakingProxyMockProxy, }; -use mesh_native_staking_proxy::contract::NativeStakingProxyContract; +use mesh_native_staking_proxy::mock::NativeStakingProxyMock; use mesh_sync::ValueRange; use mesh_vault::mock::sv::mt::VaultMockProxy; use mesh_vault::msg::LocalStakingInfo; @@ -291,7 +291,7 @@ fn releasing_proxy_stake() { ); // Access staking instance - let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyContract<'_>> = + let staking_proxy: Proxy<'_, MtApp, NativeStakingProxyMock<'_>> = Proxy::new(Addr::unchecked(proxy_addr), &app); // User bonds some funds to the vault diff --git a/contracts/provider/vault/src/multitest.rs b/contracts/provider/vault/src/multitest.rs index 9e8b897a..fc2f9485 100644 --- a/contracts/provider/vault/src/multitest.rs +++ b/contracts/provider/vault/src/multitest.rs @@ -8,8 +8,8 @@ use mesh_external_staking::state::SlashRatio; use mesh_external_staking::state::Stake; use mesh_native_staking::contract::sv::mt::NativeStakingContractProxy; use mesh_native_staking::contract::NativeStakingContract; -use mesh_native_staking_proxy::contract::sv::mt::NativeStakingProxyContractProxy; -use mesh_native_staking_proxy::contract::NativeStakingProxyContract; +use mesh_native_staking_proxy::mock::sv::mt::NativeStakingProxyMockProxy; +use mesh_native_staking_proxy::mock::NativeStakingProxyMock; use mesh_sync::Tx::InFlightStaking; use mesh_sync::{Tx, ValueRange}; use sylvia::multitest::{App, Proxy}; @@ -126,7 +126,7 @@ fn setup_inner<'app>( let staking_init_info = if local_staking { let native_staking_code = mesh_native_staking::contract::sv::mt::CodeId::store_code(app); let native_staking_proxy_code = - mesh_native_staking_proxy::contract::sv::mt::CodeId::store_code(app); + mesh_native_staking_proxy::mock::sv::mt::CodeId::store_code(app); let native_staking_inst_msg = mesh_native_staking::contract::sv::InstantiateMsg { denom: OSMO.to_string(), @@ -262,7 +262,7 @@ fn proxy_for_user<'a>( local_staking: &Proxy<'_, MtApp, NativeStakingContract<'_>>, user: &str, app: &'a App, -) -> Proxy<'a, MtApp, NativeStakingProxyContract<'a>> { +) -> Proxy<'a, MtApp, NativeStakingProxyMock<'a>> { let proxy_addr = local_staking .proxy_by_owner(user.to_string()) .unwrap() From a773d32e87bb45f846ff3a38d870d989cf498b7c Mon Sep 17 00:00:00 2001 From: Dzung Do Date: Mon, 15 Jul 2024 12:13:07 +0700 Subject: [PATCH 03/17] fix build error --- contracts/provider/native-staking-proxy/src/mock.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/provider/native-staking-proxy/src/mock.rs b/contracts/provider/native-staking-proxy/src/mock.rs index 19cd50c1..20fa153e 100644 --- a/contracts/provider/native-staking-proxy/src/mock.rs +++ b/contracts/provider/native-staking-proxy/src/mock.rs @@ -23,7 +23,6 @@ pub struct NativeStakingProxyMock<'a> { burned: Item<'a, u128>, } -#[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] #[sv::error(ContractError)] impl NativeStakingProxyMock<'_> { From 10142f0bceb3d7bebde49a11ec0ea97163fb7df8 Mon Sep 17 00:00:00 2001 From: Dzung Do Date: Mon, 29 Jul 2024 03:42:50 +0700 Subject: [PATCH 04/17] lint the code --- .../external-staking/src/multitest/utils.rs | 3 +- .../native-staking-proxy/src/contract.rs | 6 ++- .../provider/native-staking-proxy/src/lib.rs | 2 +- .../provider/native-staking-proxy/src/mock.rs | 2 +- .../src/native_staking_callback.rs | 2 +- contracts/provider/vault/src/contract.rs | 43 +++++++++++++++---- contracts/provider/vault/src/lib.rs | 2 +- contracts/provider/vault/src/mock.rs | 3 +- packages/apis/src/vault_api.rs | 2 +- packages/bindings/src/lib.rs | 2 +- packages/bindings/src/msg.rs | 2 +- 11 files changed, 50 insertions(+), 19 deletions(-) diff --git a/contracts/provider/external-staking/src/multitest/utils.rs b/contracts/provider/external-staking/src/multitest/utils.rs index 1b7d52a4..7d9a8e3b 100644 --- a/contracts/provider/external-staking/src/multitest/utils.rs +++ b/contracts/provider/external-staking/src/multitest/utils.rs @@ -47,8 +47,7 @@ pub(crate) trait AppExt { impl AppExt for App { #[track_caller] fn new_with_balances(balances: &[(&str, &[Coin])]) -> Self { - - let app =MtApp::new(|router, _api, storage| { + let app = MtApp::new(|router, _api, storage| { for (addr, coins) in balances { router .bank diff --git a/contracts/provider/native-staking-proxy/src/contract.rs b/contracts/provider/native-staking-proxy/src/contract.rs index 9b01af2f..8cbc9f9b 100644 --- a/contracts/provider/native-staking-proxy/src/contract.rs +++ b/contracts/provider/native-staking-proxy/src/contract.rs @@ -78,7 +78,11 @@ impl NativeStakingProxyContract<'_> { /// Stakes the tokens from `info.funds` to the given validator. /// Can only be called by the parent contract #[sv::msg(exec)] - fn stake(&self, ctx: ExecCtx, validator: String) -> Result, ContractError> { + fn stake( + &self, + ctx: ExecCtx, + validator: String, + ) -> Result, ContractError> { let cfg = self.config.load(ctx.deps.storage)?; ensure_eq!(cfg.parent, ctx.info.sender, ContractError::Unauthorized {}); diff --git a/contracts/provider/native-staking-proxy/src/lib.rs b/contracts/provider/native-staking-proxy/src/lib.rs index 3431e367..54c54224 100644 --- a/contracts/provider/native-staking-proxy/src/lib.rs +++ b/contracts/provider/native-staking-proxy/src/lib.rs @@ -1,8 +1,8 @@ pub mod contract; pub mod error; +pub mod mock; pub mod msg; #[cfg(test)] mod multitest; -pub mod mock; pub mod native_staking_callback; mod state; diff --git a/contracts/provider/native-staking-proxy/src/mock.rs b/contracts/provider/native-staking-proxy/src/mock.rs index 20fa153e..9fc5b3d6 100644 --- a/contracts/provider/native-staking-proxy/src/mock.rs +++ b/contracts/provider/native-staking-proxy/src/mock.rs @@ -499,4 +499,4 @@ mod tests { let res = contract.vote_weighted(ctx, proposal_id, vote); assert!(matches!(res.unwrap_err(), ContractError::Unauthorized {})); } -} \ No newline at end of file +} diff --git a/contracts/provider/native-staking/src/native_staking_callback.rs b/contracts/provider/native-staking/src/native_staking_callback.rs index fbfe4a35..4996296f 100644 --- a/contracts/provider/native-staking/src/native_staking_callback.rs +++ b/contracts/provider/native-staking/src/native_staking_callback.rs @@ -33,4 +33,4 @@ impl NativeStakingCallback for NativeStakingContract<'_> { Ok(Response::new().add_message(msg)) } -} \ No newline at end of file +} diff --git a/contracts/provider/vault/src/contract.rs b/contracts/provider/vault/src/contract.rs index 3138e37a..cf415057 100644 --- a/contracts/provider/vault/src/contract.rs +++ b/contracts/provider/vault/src/contract.rs @@ -1,5 +1,6 @@ use cosmwasm_std::{ - coin, ensure, Addr, Binary, Coin, Decimal, DepsMut, Fraction, Order, Reply, Response, StdResult, Storage, SubMsg, SubMsgResponse, Uint128, WasmMsg + coin, ensure, Addr, Binary, Coin, Decimal, DepsMut, Fraction, Order, Reply, Response, + StdResult, Storage, SubMsg, SubMsgResponse, Uint128, WasmMsg, }; use cw2::set_contract_version; use cw_storage_plus::{Bounder, Item, Map}; @@ -143,7 +144,11 @@ impl VaultContract<'_> { } #[sv::msg(exec)] - fn bond(&self, ctx: ExecCtx, amount: Coin) -> Result, ContractError> { + fn bond( + &self, + ctx: ExecCtx, + amount: Coin, + ) -> Result, ContractError> { nonpayable(&ctx.info)?; let denom = self.config.load(ctx.deps.storage)?.denom; @@ -156,7 +161,10 @@ impl VaultContract<'_> { user.collateral += amount.amount; self.users.save(ctx.deps.storage, &ctx.info.sender, &user)?; let amt = amount.amount; - let msg = ProviderMsg::Bond { delegator: ctx.info.sender.clone().into_string(), amount}; + let msg = ProviderMsg::Bond { + delegator: ctx.info.sender.clone().into_string(), + amount, + }; let resp = Response::new() .add_message(msg) .add_attribute("action", "unbond") @@ -167,7 +175,11 @@ impl VaultContract<'_> { } #[sv::msg(exec)] - fn unbond(&self, ctx: ExecCtx, amount: Coin) -> Result, ContractError> { + fn unbond( + &self, + ctx: ExecCtx, + amount: Coin, + ) -> Result, ContractError> { nonpayable(&ctx.info)?; let denom = self.config.load(ctx.deps.storage)?.denom; @@ -187,7 +199,10 @@ impl VaultContract<'_> { user.collateral -= amount.amount; self.users.save(ctx.deps.storage, &ctx.info.sender, &user)?; let amt = amount.amount; - let msg = ProviderMsg::Unbond { delegator: ctx.info.sender.clone().into_string(), amount}; + let msg = ProviderMsg::Unbond { + delegator: ctx.info.sender.clone().into_string(), + amount, + }; let resp = Response::new() .add_message(msg) .add_attribute("action", "unbond") @@ -492,7 +507,11 @@ impl VaultContract<'_> { } #[sv::msg(reply)] - fn reply(&self, ctx: ReplyCtx, reply: Reply) -> Result, ContractError> { + fn reply( + &self, + ctx: ReplyCtx, + reply: Reply, + ) -> Result, ContractError> { match reply.id { REPLY_ID_INSTANTIATE => self.reply_init_callback(ctx.deps, reply.result.unwrap()), _ => Err(ContractError::InvalidReplyId(reply.id)), @@ -1085,7 +1104,11 @@ impl VaultApi for VaultContract<'_> { Ok(resp) } - fn commit_tx(&self, mut ctx: ExecCtx, tx_id: u64) -> Result, ContractError> { + fn commit_tx( + &self, + mut ctx: ExecCtx, + tx_id: u64, + ) -> Result, ContractError> { self.commit_stake(&mut ctx, tx_id)?; let resp = Response::new() @@ -1096,7 +1119,11 @@ impl VaultApi for VaultContract<'_> { Ok(resp) } - fn rollback_tx(&self, mut ctx: ExecCtx, tx_id: u64) -> Result, ContractError> { + fn rollback_tx( + &self, + mut ctx: ExecCtx, + tx_id: u64, + ) -> Result, ContractError> { self.rollback_stake(&mut ctx, tx_id)?; let resp = Response::new() diff --git a/contracts/provider/vault/src/lib.rs b/contracts/provider/vault/src/lib.rs index 1876926d..e9948f04 100644 --- a/contracts/provider/vault/src/lib.rs +++ b/contracts/provider/vault/src/lib.rs @@ -1,8 +1,8 @@ pub mod contract; pub mod error; +pub mod mock; pub mod msg; #[cfg(test)] pub mod multitest; -pub mod mock; mod state; pub mod txs; diff --git a/contracts/provider/vault/src/mock.rs b/contracts/provider/vault/src/mock.rs index 82235b7b..e1ea1642 100644 --- a/contracts/provider/vault/src/mock.rs +++ b/contracts/provider/vault/src/mock.rs @@ -1,5 +1,6 @@ use cosmwasm_std::{ - coin, ensure, Addr, BankMsg, Binary, Coin, Decimal, DepsMut, Empty, Fraction, Order, Reply, Response, StdResult, Storage, SubMsg, SubMsgResponse, Uint128, WasmMsg + coin, ensure, Addr, BankMsg, Binary, Coin, Decimal, DepsMut, Empty, Fraction, Order, Reply, + Response, StdResult, Storage, SubMsg, SubMsgResponse, Uint128, WasmMsg, }; use cw2::set_contract_version; use cw_storage_plus::{Bounder, Item, Map}; diff --git a/packages/apis/src/vault_api.rs b/packages/apis/src/vault_api.rs index 8e2a4d08..b2c60207 100644 --- a/packages/apis/src/vault_api.rs +++ b/packages/apis/src/vault_api.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{to_json_binary, Addr, Coin, Response, StdError, Uint128, CustomMsg, WasmMsg}; +use cosmwasm_std::{to_json_binary, Addr, Coin, CustomMsg, Response, StdError, Uint128, WasmMsg}; use sylvia::types::ExecCtx; use sylvia::{interface, schemars}; diff --git a/packages/bindings/src/lib.rs b/packages/bindings/src/lib.rs index 880e1b16..45c3ba81 100644 --- a/packages/bindings/src/lib.rs +++ b/packages/bindings/src/lib.rs @@ -1,7 +1,7 @@ mod msg; mod query; -pub use msg::{VirtualStakeCustomMsg, VirtualStakeMsg, ProviderCustomMsg, ProviderMsg}; +pub use msg::{ProviderCustomMsg, ProviderMsg, VirtualStakeCustomMsg, VirtualStakeMsg}; pub use query::{ BondStatusResponse, SlashRatioResponse, TokenQuerier, VirtualStakeCustomQuery, VirtualStakeQuery, diff --git a/packages/bindings/src/msg.rs b/packages/bindings/src/msg.rs index fde80955..2b33bb3d 100644 --- a/packages/bindings/src/msg.rs +++ b/packages/bindings/src/msg.rs @@ -117,7 +117,7 @@ impl ProviderMsg { amount: coin, } } - + pub fn unstake(denom: &str, validator: &str, amount: impl Into) -> ProviderMsg { let coin = Coin { amount: amount.into(), From e985ca5a080641a9cac591a9f64f4e373561a526 Mon Sep 17 00:00:00 2001 From: Trinity Date: Wed, 14 Aug 2024 12:03:26 +0700 Subject: [PATCH 05/17] Remove virtual staking mock --- Cargo.lock | 266 +++++++++-------- contracts/consumer/converter/Cargo.toml | 1 + contracts/consumer/converter/src/multitest.rs | 40 +-- .../src/multitest/virtual_staking_mock.rs | 276 ------------------ .../consumer/virtual-staking/src/contract.rs | 58 +++- contracts/consumer/virtual-staking/src/msg.rs | 11 + 6 files changed, 229 insertions(+), 423 deletions(-) delete mode 100644 contracts/consumer/converter/src/multitest/virtual_staking_mock.rs diff --git a/Cargo.lock b/Cargo.lock index 0df67f14..77d629a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -15,15 +15,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "base16ct" @@ -33,9 +33,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -75,15 +75,15 @@ checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cfg-if" @@ -93,18 +93,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "num-traits", ] [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_panic" @@ -123,12 +123,11 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b4c3f9c4616d6413d4b5fc4c270a4cc32a374b9be08671e80e1a019f805d8f" +checksum = "0f862b355f7e47711e0acfe6af92cb3fd8fd5936b66a9eaa338b51edabd1e77d" dependencies = [ "digest 0.10.7", - "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -137,18 +136,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c586ced10c3b00e809ee664a895025a024f60d65d34fe4c09daed4a4db68a3f3" +checksum = "cd85de6467cd1073688c86b39833679ae6db18cf4771471edd9809f15f1679f1" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8467874827d384c131955ff6f4d47d02e72a956a08eb3c0ff24f8c903a5517b4" +checksum = "5b4cd28147a66eba73720b47636a58097a979ad8c8bfdb4ed437ebcbfe362576" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -159,9 +158,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6db85d98ac80922aef465e564d5b21fa9cfac5058cb62df7f116c3682337393" +checksum = "9acd45c63d41bc9b16bc6dc7f6bd604a8c2ad29ce96c8f3c96d7fc8ef384392e" dependencies = [ "proc-macro2", "quote", @@ -170,9 +169,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712fe58f39d55c812f7b2c84e097cdede3a39d520f89b6dc3153837e31741927" +checksum = "2685c2182624b2e9e17f7596192de49a3f86b7a0c9a5f6b25c1df5e24592e836" dependencies = [ "base64", "bech32", @@ -192,18 +191,18 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] [[package]] name = "crypto-bigint" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -247,7 +246,7 @@ dependencies = [ "cw-utils", "derivative", "itertools 0.12.1", - "prost 0.12.4", + "prost 0.12.6", "schemars", "serde", "sha2 0.10.8", @@ -297,9 +296,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", @@ -339,15 +338,15 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.13" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest 0.10.7", @@ -374,15 +373,15 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" -version = "0.13.6" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", @@ -432,9 +431,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -463,9 +462,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hex" @@ -484,12 +483,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.5", ] [[package]] @@ -512,15 +511,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "k256" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa", @@ -532,9 +531,9 @@ dependencies = [ [[package]] name = "konst" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030400e39b2dff8beaa55986a17e0014ad657f569ca92426aafcb5e8e71faee7" +checksum = "50a0ba6de5f7af397afff922f22c149ff605c766cd3269cf6c1cd5e466dbe3b9" dependencies = [ "const_panic", "konst_kernel", @@ -544,9 +543,9 @@ dependencies = [ [[package]] name = "konst_kernel" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3376133edc39f027d551eb77b077c2865a0ef252b2e7d0dd6b6dc303db95d8b5" +checksum = "be0a455a1719220fd6adf756088e1c69a85bf14b6a9e24537a5cc04f503edb2b" dependencies = [ "typewit", ] @@ -559,15 +558,15 @@ checksum = "4e28ab1dc35e09d60c2b8c90d12a9a8d9666c876c10a3739a3196db0103b6043" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "memchr" -version = "2.6.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76fc44e2588d5b436dbc3c6cf62aef290f90dab6235744a93dfe1cc18f451e2c" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mesh-apis" @@ -610,6 +609,7 @@ dependencies = [ "mesh-bindings", "mesh-burn", "mesh-simple-price-feed", + "mesh-virtual-staking", "schemars", "serde", "sylvia", @@ -822,24 +822,24 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "osmosis-std" @@ -916,9 +916,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -935,12 +935,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", - "prost-derive 0.12.4", + "prost-derive 0.12.6", ] [[package]] @@ -958,15 +958,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] @@ -1014,15 +1014,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schemars" -version = "0.8.17" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "schemars_derive", @@ -1032,14 +1032,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.17" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] @@ -1058,15 +1058,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" dependencies = [ "serde_derive", ] @@ -1100,33 +1100,34 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] name = "serde_derive_internals" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1157,9 +1158,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core 0.6.4", @@ -1167,9 +1168,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -1183,9 +1184,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sylvia" @@ -1218,7 +1219,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] @@ -1234,9 +1235,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", @@ -1261,7 +1262,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] @@ -1272,41 +1273,41 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", "test-case-core", ] [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", @@ -1315,33 +1316,42 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typewit" -version = "1.5.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5cee357cc77d1e02f10a3e6c4e13b8462fafab05998b62d331b7d9485589ff" +checksum = "c6fb9ae6a3cafaf0a5d14c2302ca525f9ae8e07a0f0e6949de88d882c37a6e24" +dependencies = [ + "typewit_proc_macros", +] + +[[package]] +name = "typewit_proc_macros" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1351,15 +1361,15 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] name = "zeroize" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/contracts/consumer/converter/Cargo.toml b/contracts/consumer/converter/Cargo.toml index a152fbeb..c71087f0 100644 --- a/contracts/consumer/converter/Cargo.toml +++ b/contracts/consumer/converter/Cargo.toml @@ -38,6 +38,7 @@ thiserror = { workspace = true } [dev-dependencies] mesh-burn = { workspace = true } mesh-simple-price-feed = { workspace = true, features = ["mt"] } +mesh-virtual-staking = { workspace = true, features = ["mt"] } cw-multi-test = { workspace = true } test-case = { workspace = true } diff --git a/contracts/consumer/converter/src/multitest.rs b/contracts/consumer/converter/src/multitest.rs index 22169d99..551150c2 100644 --- a/contracts/consumer/converter/src/multitest.rs +++ b/contracts/consumer/converter/src/multitest.rs @@ -1,21 +1,20 @@ -mod virtual_staking_mock; - use cosmwasm_std::{coin, coins, Addr, Decimal, StdError, Uint128, Validator}; use cw_multi_test::{no_init, AppBuilder}; use mesh_apis::converter_api::sv::mt::ConverterApiProxy; use mesh_apis::converter_api::RewardInfo; use mesh_simple_price_feed::contract::sv::mt::CodeId as PriceFeedCodeId; use mesh_simple_price_feed::contract::SimplePriceFeedContract; +use mesh_virtual_staking::contract::sv::mt::{ + CodeId as VirtualStakingCodeId, VirtualStakingContractProxy, +}; +use mesh_virtual_staking::contract::VirtualStakingContract; use sylvia::multitest::{App, Proxy}; -use virtual_staking_mock::sv::mt::CodeId as VirtualStakingCodeId; -use virtual_staking_mock::VirtualStakingMock; use crate::contract::sv::mt::CodeId as ConverterCodeId; use crate::contract::sv::mt::ConverterContractProxy; use crate::contract::{custom, ConverterContract}; use crate::error::ContractError; use crate::error::ContractError::Unauthorized; -use crate::multitest::virtual_staking_mock::sv::mt::VirtualStakingMockProxy; const JUNO: &str = "ujuno"; @@ -31,7 +30,7 @@ struct SetupArgs<'a> { struct SetupResponse<'a> { price_feed: Proxy<'a, MtApp, SimplePriceFeedContract<'a>>, converter: Proxy<'a, MtApp, ConverterContract<'a>>, - virtual_staking: Proxy<'a, MtApp, VirtualStakingMock<'a>>, + virtual_staking: Proxy<'a, MtApp, VirtualStakingContract<'a>>, } fn new_app() -> App { @@ -152,10 +151,10 @@ fn ibc_stake_and_unstake() { // no one is staked let val1 = "Val Kilmer"; let val2 = "Valley Girl"; - assert!(virtual_staking.all_stake().unwrap().stakes.is_empty()); + assert!(virtual_staking.get_all_stake().unwrap().stakes.is_empty()); assert_eq!( virtual_staking - .stake(val1.to_string()) + .get_stake(val1.to_string()) .unwrap() .stake .u128(), @@ -163,7 +162,7 @@ fn ibc_stake_and_unstake() { ); assert_eq!( virtual_staking - .stake(val2.to_string()) + .get_stake(val2.to_string()) .unwrap() .stake .u128(), @@ -186,10 +185,13 @@ fn ibc_stake_and_unstake() { .call(owner) .unwrap(); + // new epoch to update all stake values + virtual_staking.test_handle_epoch().call(owner).unwrap(); + // and check the stakes (1000 * 0.6 * 0.5 = 300) (2000 * 0.6 * 0.5 = 600) assert_eq!( virtual_staking - .stake(val1.to_string()) + .get_stake(val1.to_string()) .unwrap() .stake .u128(), @@ -197,14 +199,14 @@ fn ibc_stake_and_unstake() { ); assert_eq!( virtual_staking - .stake(val2.to_string()) + .get_stake(val2.to_string()) .unwrap() .stake .u128(), 600 ); assert_eq!( - virtual_staking.all_stake().unwrap().stakes, + virtual_staking.get_all_stake().unwrap().stakes, vec![ (val1.to_string(), Uint128::new(300)), (val2.to_string(), Uint128::new(600)), @@ -238,10 +240,10 @@ fn ibc_stake_and_burn() { // no one is staked let val1 = "Val Kilmer"; let val2 = "Valley Girl"; - assert!(virtual_staking.all_stake().unwrap().stakes.is_empty()); + assert!(virtual_staking.get_all_stake().unwrap().stakes.is_empty()); assert_eq!( virtual_staking - .stake(val1.to_string()) + .get_stake(val1.to_string()) .unwrap() .stake .u128(), @@ -249,7 +251,7 @@ fn ibc_stake_and_burn() { ); assert_eq!( virtual_staking - .stake(val2.to_string()) + .get_stake(val2.to_string()) .unwrap() .stake .u128(), @@ -272,10 +274,12 @@ fn ibc_stake_and_burn() { .call(owner) .unwrap(); + // new epoch to update all stake values + virtual_staking.test_handle_epoch().call(owner).unwrap(); // and check the stakes (1000 * 0.6 * 0.5 = 300) (2000 * 0.6 * 0.5 = 600) assert_eq!( virtual_staking - .stake(val1.to_string()) + .get_stake(val1.to_string()) .unwrap() .stake .u128(), @@ -283,14 +287,14 @@ fn ibc_stake_and_burn() { ); assert_eq!( virtual_staking - .stake(val2.to_string()) + .get_stake(val2.to_string()) .unwrap() .stake .u128(), 600 ); assert_eq!( - virtual_staking.all_stake().unwrap().stakes, + virtual_staking.get_all_stake().unwrap().stakes, vec![ (val1.to_string(), Uint128::new(300)), (val2.to_string(), Uint128::new(600)), diff --git a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs deleted file mode 100644 index 3faaedf8..00000000 --- a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs +++ /dev/null @@ -1,276 +0,0 @@ -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{ensure_eq, Addr, Coin, Response, StdError, StdResult, Uint128, Validator}; - -use cw_storage_plus::{Item, Map}; -use cw_utils::{nonpayable, PaymentError}; -use mesh_apis::virtual_staking_api::{self, ValidatorSlash, VirtualStakingApi}; -use sylvia::contract; -use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, SudoCtx}; - -use crate::contract::custom; - -#[cw_serde] -pub struct Config { - /// The denom we accept for staking - pub denom: String, - /// The address of the converter contract (that is authorized to bond/unbond and will receive rewards) - pub converter: Addr, -} - -#[derive(thiserror::Error, Debug)] -pub enum ContractError { - #[error("{0}")] - Std(#[from] StdError), - - #[error("{0}")] - Payment(#[from] PaymentError), - - #[error("Unauthorized")] - Unauthorized, - - #[error("Wrong denom. Cannot stake {0}")] - WrongDenom(String), - - #[error("Virtual staking {0} has not enough delegated funds: {1}")] - InsufficientDelegations(String, Uint128), -} - -/// This is a stub implementation of the virtual staking contract, for test purposes only. -/// When proper virtual staking contract is available, this should be replaced in multitests -pub struct VirtualStakingMock<'a> { - config: Item<'a, Config>, - stake: Map<'a, &'a str, Uint128>, -} - -#[contract] -#[sv::error(ContractError)] -#[sv::messages(virtual_staking_api as VirtualStakingApi)] -#[sv::custom(query=custom::ConverterQuery, msg=custom::ConverterMsg)] -impl VirtualStakingMock<'_> { - pub const fn new() -> Self { - Self { - config: Item::new("config"), - stake: Map::new("stake"), - } - } - - #[sv::msg(instantiate)] - pub fn instantiate( - &self, - ctx: InstantiateCtx, - ) -> Result { - nonpayable(&ctx.info)?; - let denom = ctx.deps.querier.query_bonded_denom()?; - let config = Config { - denom, - converter: ctx.info.sender, - }; - self.config.save(ctx.deps.storage, &config)?; - Ok(Response::new()) - } - - #[sv::msg(query)] - fn config( - &self, - ctx: QueryCtx, - ) -> Result { - let cfg = self.config.load(ctx.deps.storage)?; - let denom = cfg.denom; - let converter = cfg.converter.into_string(); - Ok(ConfigResponse { denom, converter }) - } - - #[sv::msg(query)] - fn stake( - &self, - ctx: QueryCtx, - validator: String, - ) -> Result { - let stake = self - .stake - .may_load(ctx.deps.storage, &validator)? - .unwrap_or_default(); - Ok(StakeResponse { stake }) - } - - #[sv::msg(query)] - fn all_stake( - &self, - ctx: QueryCtx, - ) -> Result { - let stakes = self - .stake - .range(ctx.deps.storage, None, None, cosmwasm_std::Order::Ascending) - .collect::>()?; - Ok(AllStakeResponse { stakes }) - } -} - -#[cw_serde] -pub struct StakeResponse { - pub stake: Uint128, -} - -#[cw_serde] -pub struct AllStakeResponse { - pub stakes: Vec<(String, Uint128)>, -} - -#[cw_serde] -pub struct ConfigResponse { - pub denom: String, - pub converter: String, -} - -impl VirtualStakingApi for VirtualStakingMock<'_> { - type Error = ContractError; - type ExecC = custom::ConverterMsg; - type QueryC = custom::ConverterQuery; - - /// Requests to bond tokens to a validator. This will be actually handled at the next epoch. - /// If the virtual staking module is over the max cap, it will trigger a rebalance. - /// If the max cap is 0, then this will immediately return an error. - fn bond( - &self, - ctx: ExecCtx, - validator: String, - amount: Coin, - ) -> Result, Self::Error> { - nonpayable(&ctx.info)?; - let cfg = self.config.load(ctx.deps.storage)?; - ensure_eq!(ctx.info.sender, cfg.converter, ContractError::Unauthorized); // only the converter can call this - ensure_eq!( - amount.denom, - cfg.denom, - ContractError::WrongDenom(cfg.denom) - ); - - // Update the amount requested - self.stake - .update::<_, ContractError>(ctx.deps.storage, &validator, |old| { - Ok(old.unwrap_or_default() + amount.amount) - })?; - - Ok(Response::new()) - } - - /// Requests to unbond tokens from a validator. This will be actually handled at the next epoch. - /// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond. - /// If the virtual staking contract doesn't have at least amount tokens staked to the given validator, this will return an error. - fn unbond( - &self, - ctx: ExecCtx, - validator: String, - amount: Coin, - ) -> Result, Self::Error> { - nonpayable(&ctx.info)?; - let cfg = self.config.load(ctx.deps.storage)?; - ensure_eq!(ctx.info.sender, cfg.converter, ContractError::Unauthorized); // only the converter can call this - ensure_eq!( - amount.denom, - cfg.denom, - ContractError::WrongDenom(cfg.denom) - ); - - // Update the amount requested - self.stake - .update::<_, ContractError>(ctx.deps.storage, &validator, |old| { - Ok(old.unwrap_or_default() - amount.amount) - })?; - - Ok(Response::new()) - } - - /// Requests to unbond and burn tokens from a lists of validators (one or more). This will be actually handled at the next epoch. - /// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond. - /// If the virtual staking contract doesn't have at least amount tokens staked over the given validators, this will return an error. - fn burn( - &self, - ctx: ExecCtx, - validators: Vec, - amount: Coin, - ) -> Result, Self::Error> { - nonpayable(&ctx.info)?; - let cfg = self.config.load(ctx.deps.storage)?; - // only the converter can call this - ensure_eq!(ctx.info.sender, cfg.converter, ContractError::Unauthorized); - ensure_eq!( - amount.denom, - cfg.denom, - ContractError::WrongDenom(cfg.denom) - ); - - let mut stakes = vec![]; - for validator in validators { - let stake = self - .stake - .may_load(ctx.deps.storage, &validator)? - .unwrap_or_default() - .u128(); - if stake != 0 { - stakes.push((validator, stake)); - } - } - - // Error if no delegations - if stakes.is_empty() { - return Err(ContractError::InsufficientDelegations( - ctx.env.contract.address.to_string(), - amount.amount, - )); - } - - let (burned, burns) = mesh_burn::distribute_burn(stakes.as_slice(), amount.amount.u128()); - - // Bail if we still don't have enough stake - if burned < amount.amount.u128() { - return Err(ContractError::InsufficientDelegations( - ctx.env.contract.address.to_string(), - amount.amount, - )); - } - - // Update stake - for (validator, burn_amount) in burns { - self.stake - .update::<_, ContractError>(ctx.deps.storage, validator, |old| { - Ok(old.unwrap_or_default() - Uint128::new(burn_amount)) - })?; - } - - Ok(Response::new()) - } - - /// SudoMsg::HandleEpoch{} should be called once per epoch by the sdk (in EndBlock). - /// It allows the virtual staking contract to bond or unbond any pending requests, as well - /// as to perform a rebalance if needed (over the max cap). - /// - /// It should also withdraw all pending rewards here, and send them to the converter contract. - fn handle_epoch( - &self, - _ctx: SudoCtx, - ) -> Result, Self::Error> { - unimplemented!() - } - - /// SudoMsg::ValsetUpdate{} should be called every time there's a validator set update: - /// - Addition of a new validator to the active validator set. - /// - Temporary removal of a validator from the active set. (i.e. `unbonded` state). - /// - Update of validator data. - /// - Temporary removal of a validator from the active set due to jailing. Implies slashing. - /// - Addition of an existing validator to the active validator set. - /// - Permanent removal (i.e. tombstoning) of a validator from the active set. Implies slashing - fn handle_valset_update( - &self, - _ctx: SudoCtx, - _additions: Option>, - _removals: Option>, - _updated: Option>, - _jailed: Option>, - _unjailed: Option>, - _tombstoned: Option>, - _slashed: Option>, - ) -> Result, Self::Error> { - unimplemented!() - } -} diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index 52af7d8b..a963e7c2 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -18,7 +18,7 @@ use sylvia::{contract, schemars}; use mesh_apis::virtual_staking_api::{self, ValidatorSlash, VirtualStakingApi}; use crate::error::ContractError; -use crate::msg::ConfigResponse; +use crate::msg::{AllStakeResponse, ConfigResponse, StakeResponse}; use crate::state::Config; pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); @@ -221,6 +221,62 @@ impl VirtualStakingContract<'_> { Ok(Response::new()) } } + + /// This is only used for tests. + /// Ideally we want conditional compilation of these whole methods and the enum variants + #[sv::msg(exec)] + pub fn test_handle_epoch( + &self, + ctx: ExecCtx, + ) -> Result, ContractError> { + #[cfg(any(test, feature = "mt"))] + { + let ExecCtx { mut deps, .. } = ctx; + let requests: Vec<(String, Uint128)> = self + .bond_requests + .range( + deps.as_ref().storage, + None, + None, + cosmwasm_std::Order::Ascending, + ) + .collect::>()?; + + // Save the future values + self.bonded.save(deps.branch().storage, &requests)?; + Ok(Response::new()) + } + #[cfg(not(any(test, feature = "mt")))] + { + Err(ContractError::Unauthorized) + } + } + + #[sv::msg(query)] + fn get_stake( + &self, + ctx: QueryCtx, + validator: String, + ) -> Result { + let bonded = self.bonded.load(ctx.deps.storage)?; + + if let Some(stake) = bonded.iter().find(|&x| x.0 == validator) { + Ok(StakeResponse { stake: stake.1 }) + } else { + Ok(StakeResponse { + stake: Uint128::zero(), + }) + } + } + + #[sv::msg(query)] + fn get_all_stake( + &self, + ctx: QueryCtx, + ) -> Result { + let stakes = self.bonded.load(ctx.deps.storage)?; + Ok(AllStakeResponse { stakes }) + } } /// Returns a tuple containing the reward target and a boolean value diff --git a/contracts/consumer/virtual-staking/src/msg.rs b/contracts/consumer/virtual-staking/src/msg.rs index 6833b801..eeb70c50 100644 --- a/contracts/consumer/virtual-staking/src/msg.rs +++ b/contracts/consumer/virtual-staking/src/msg.rs @@ -1,4 +1,5 @@ use cosmwasm_schema::cw_serde; +use cosmwasm_std::Uint128; use crate::state::Config; @@ -11,6 +12,16 @@ pub struct ConfigResponse { pub converter: String, } +#[cw_serde] +pub struct StakeResponse { + pub stake: Uint128, +} + +#[cw_serde] +pub struct AllStakeResponse { + pub stakes: Vec<(String, Uint128)>, +} + impl From for ConfigResponse { fn from(config: Config) -> Self { Self { From 0844fed210ae013d08e95d33fc1f3e12ef742610 Mon Sep 17 00:00:00 2001 From: Trinity Date: Wed, 14 Aug 2024 12:14:11 +0700 Subject: [PATCH 06/17] handle unused variable --- contracts/consumer/virtual-staking/src/contract.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index a963e7c2..be6a9014 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -248,6 +248,7 @@ impl VirtualStakingContract<'_> { } #[cfg(not(any(test, feature = "mt")))] { + let _ = ctx; Err(ContractError::Unauthorized) } } From 38a9da8c6a62593d140f7a6d5729c0b2fefc2d0f Mon Sep 17 00:00:00 2001 From: Trinity Date: Fri, 16 Aug 2024 17:02:41 +0700 Subject: [PATCH 07/17] Update logic for auto unbond tombstoned validator --- Cargo.lock | 266 +++++++++--------- contracts/consumer/converter/Cargo.toml | 1 + contracts/consumer/converter/src/contract.rs | 4 +- contracts/consumer/converter/src/multitest.rs | 1 + .../consumer/virtual-staking/src/contract.rs | 68 +++-- .../consumer/virtual-staking/src/multitest.rs | 1 + .../consumer/virtual-staking/src/state.rs | 3 + packages/apis/src/virtual_staking_api.rs | 2 + 8 files changed, 198 insertions(+), 148 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0df67f14..77d629a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -15,15 +15,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "base16ct" @@ -33,9 +33,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -75,15 +75,15 @@ checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cfg-if" @@ -93,18 +93,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "num-traits", ] [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_panic" @@ -123,12 +123,11 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b4c3f9c4616d6413d4b5fc4c270a4cc32a374b9be08671e80e1a019f805d8f" +checksum = "0f862b355f7e47711e0acfe6af92cb3fd8fd5936b66a9eaa338b51edabd1e77d" dependencies = [ "digest 0.10.7", - "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -137,18 +136,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c586ced10c3b00e809ee664a895025a024f60d65d34fe4c09daed4a4db68a3f3" +checksum = "cd85de6467cd1073688c86b39833679ae6db18cf4771471edd9809f15f1679f1" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8467874827d384c131955ff6f4d47d02e72a956a08eb3c0ff24f8c903a5517b4" +checksum = "5b4cd28147a66eba73720b47636a58097a979ad8c8bfdb4ed437ebcbfe362576" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -159,9 +158,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6db85d98ac80922aef465e564d5b21fa9cfac5058cb62df7f116c3682337393" +checksum = "9acd45c63d41bc9b16bc6dc7f6bd604a8c2ad29ce96c8f3c96d7fc8ef384392e" dependencies = [ "proc-macro2", "quote", @@ -170,9 +169,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712fe58f39d55c812f7b2c84e097cdede3a39d520f89b6dc3153837e31741927" +checksum = "2685c2182624b2e9e17f7596192de49a3f86b7a0c9a5f6b25c1df5e24592e836" dependencies = [ "base64", "bech32", @@ -192,18 +191,18 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] [[package]] name = "crypto-bigint" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -247,7 +246,7 @@ dependencies = [ "cw-utils", "derivative", "itertools 0.12.1", - "prost 0.12.4", + "prost 0.12.6", "schemars", "serde", "sha2 0.10.8", @@ -297,9 +296,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", @@ -339,15 +338,15 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.13" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest 0.10.7", @@ -374,15 +373,15 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" -version = "0.13.6" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", @@ -432,9 +431,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -463,9 +462,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hex" @@ -484,12 +483,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.5", ] [[package]] @@ -512,15 +511,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "k256" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa", @@ -532,9 +531,9 @@ dependencies = [ [[package]] name = "konst" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030400e39b2dff8beaa55986a17e0014ad657f569ca92426aafcb5e8e71faee7" +checksum = "50a0ba6de5f7af397afff922f22c149ff605c766cd3269cf6c1cd5e466dbe3b9" dependencies = [ "const_panic", "konst_kernel", @@ -544,9 +543,9 @@ dependencies = [ [[package]] name = "konst_kernel" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3376133edc39f027d551eb77b077c2865a0ef252b2e7d0dd6b6dc303db95d8b5" +checksum = "be0a455a1719220fd6adf756088e1c69a85bf14b6a9e24537a5cc04f503edb2b" dependencies = [ "typewit", ] @@ -559,15 +558,15 @@ checksum = "4e28ab1dc35e09d60c2b8c90d12a9a8d9666c876c10a3739a3196db0103b6043" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "memchr" -version = "2.6.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76fc44e2588d5b436dbc3c6cf62aef290f90dab6235744a93dfe1cc18f451e2c" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mesh-apis" @@ -610,6 +609,7 @@ dependencies = [ "mesh-bindings", "mesh-burn", "mesh-simple-price-feed", + "mesh-virtual-staking", "schemars", "serde", "sylvia", @@ -822,24 +822,24 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "osmosis-std" @@ -916,9 +916,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -935,12 +935,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", - "prost-derive 0.12.4", + "prost-derive 0.12.6", ] [[package]] @@ -958,15 +958,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] @@ -1014,15 +1014,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schemars" -version = "0.8.17" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "schemars_derive", @@ -1032,14 +1032,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.17" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] @@ -1058,15 +1058,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" dependencies = [ "serde_derive", ] @@ -1100,33 +1100,34 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] name = "serde_derive_internals" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1157,9 +1158,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core 0.6.4", @@ -1167,9 +1168,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -1183,9 +1184,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sylvia" @@ -1218,7 +1219,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] @@ -1234,9 +1235,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", @@ -1261,7 +1262,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] @@ -1272,41 +1273,41 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", "test-case-core", ] [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.74", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", @@ -1315,33 +1316,42 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typewit" -version = "1.5.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5cee357cc77d1e02f10a3e6c4e13b8462fafab05998b62d331b7d9485589ff" +checksum = "c6fb9ae6a3cafaf0a5d14c2302ca525f9ae8e07a0f0e6949de88d882c37a6e24" +dependencies = [ + "typewit_proc_macros", +] + +[[package]] +name = "typewit_proc_macros" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1351,15 +1361,15 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] name = "zeroize" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/contracts/consumer/converter/Cargo.toml b/contracts/consumer/converter/Cargo.toml index a152fbeb..4a940c64 100644 --- a/contracts/consumer/converter/Cargo.toml +++ b/contracts/consumer/converter/Cargo.toml @@ -23,6 +23,7 @@ fake-custom = [ "mesh-simple-price-feed/fake-custom" ] [dependencies] mesh-apis = { workspace = true } mesh-bindings = { workspace = true } +mesh-virtual-staking = { workspace = true, features = ["mt"]} sylvia = { workspace = true } cosmwasm-schema = { workspace = true } diff --git a/contracts/consumer/converter/src/contract.rs b/contracts/consumer/converter/src/contract.rs index caedb92d..1063e118 100644 --- a/contracts/consumer/converter/src/contract.rs +++ b/contracts/consumer/converter/src/contract.rs @@ -71,6 +71,7 @@ impl ConverterContract<'_> { discount: Decimal, remote_denom: String, virtual_staking_code_id: u64, + tombstoned_unbond_enable: bool, admin: Option, ) -> Result { nonpayable(&ctx.info)?; @@ -95,11 +96,12 @@ impl ConverterContract<'_> { ctx.deps.api.addr_validate(admin)?; } + let msg = to_json_binary(&mesh_virtual_staking::contract::sv::InstantiateMsg { tombstoned_unbond_enable })?; // Instantiate virtual staking contract let init_msg = WasmMsg::Instantiate { admin, code_id: virtual_staking_code_id, - msg: b"{}".into(), + msg, funds: vec![], label: format!("Virtual Staking: {}", &config.remote_denom), }; diff --git a/contracts/consumer/converter/src/multitest.rs b/contracts/consumer/converter/src/multitest.rs index 22169d99..afc410bb 100644 --- a/contracts/consumer/converter/src/multitest.rs +++ b/contracts/consumer/converter/src/multitest.rs @@ -62,6 +62,7 @@ fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { discount, JUNO.to_owned(), virtual_staking_code.code_id(), + true, Some(admin.to_owned()), ) .with_label("Juno Converter") diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index 52af7d8b..deac10ce 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -43,7 +43,7 @@ pub struct VirtualStakingContract<'a> { /// This is what validators are inactive because of tombstoning, jailing or removal (unbonded). // `inactive` could be a Map like `bond_requests`, but the only time we use it is to read / write the entire list in bulk (in handle_epoch), // never accessing one element. Reading 100 elements in an Item is much cheaper than ranging over a Map with 100 entries. - pub inactive: Item<'a, Vec>, + pub inactive: Item<'a, Vec<(String, bool)>>, /// Amount of tokens that have been burned from a validator. /// This is just for accounting / tracking reasons, as token "burning" is being implemented as unbonding, /// and there's no real need to discount the burned amount in this contract. @@ -74,12 +74,14 @@ impl VirtualStakingContract<'_> { pub fn instantiate( &self, ctx: InstantiateCtx, + tombstoned_unbond_enable: bool, ) -> Result, ContractError> { nonpayable(&ctx.info)?; let denom = ctx.deps.querier.query_bonded_denom()?; let config = Config { denom, converter: ctx.info.sender, + tombstoned_unbond_enable, }; self.config.save(ctx.deps.storage, &config)?; // initialize these to no one, so no issue when reading for the first time @@ -235,6 +237,7 @@ fn pop_target(deps: DepsMut) -> StdResult<(String, bool fn calculate_rebalance( current: Vec<(String, Uint128)>, desired: Vec<(String, Uint128)>, + tombstoned_list: HashSet, denom: &str, ) -> Vec> { let mut desired: BTreeMap<_, _> = desired.into_iter().collect(); @@ -243,6 +246,11 @@ fn calculate_rebalance( let mut msgs = vec![]; for (validator, prev) in current { let next = desired.remove(&validator).unwrap_or_else(Uint128::zero); + if tombstoned_list.contains(&validator) && !next.is_zero(){ + let amount = coin(next.u128(), denom); + msgs.push(VirtualStakeMsg::Unbond { validator, amount }.into()); + continue; + } match next.cmp(&prev) { Ordering::Less => { let unbond = prev - next; @@ -499,8 +507,8 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { // withdraw rewards let bonded = self.bonded.load(deps.storage)?; - let inactive = self.inactive.load(deps.storage)?; - let withdraw = withdraw_reward_msgs(deps.branch(), &bonded, &inactive); + let inactive_list = self.inactive.load(deps.storage)?; + let withdraw = withdraw_reward_msgs(deps.branch(), &bonded, &inactive_list.iter().map(| (i, _) | i.to_string()).collect::>()); let mut resp = Response::new().add_submessages(withdraw); let bond = @@ -523,7 +531,7 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { self.adjust_slashings(deps.branch(), &mut current, &slash)?; // Update inactive list. Defensive, as it should already been updated in handle_valset_update, due to removals self.inactive.update(deps.branch().storage, |mut old| { - old.extend_from_slice(&slash.iter().map(|v| v.address.clone()).collect::>()); + old.extend_from_slice(&slash.iter().map(|v| (v.address.clone(), v.is_tombstoned)).collect::>()); old.dedup(); Ok::<_, ContractError>(old) })?; @@ -548,11 +556,25 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { } } + // Force the tombstoned validator to auto unbond + let tombstoned_list = inactive_list + .iter() + .filter_map(| (val, is_tombstoned) | if *is_tombstoned { Some(val.to_string()) } else { None }) + .collect::>(); + let mut request_with_tombstoned = requests.clone(); + for (val, amount) in request_with_tombstoned.iter_mut() { + if tombstoned_list.contains(val) { + *amount = Uint128::zero(); + // Update new value for the bond requests + self.bond_requests.save(deps.storage, val, amount)?; + } + } + // Save the future values - self.bonded.save(deps.branch().storage, &requests)?; + self.bonded.save(deps.branch().storage, &request_with_tombstoned)?; // Compare these two to make bond/unbond calls as needed - let rebalance = calculate_rebalance(current, requests, &config.denom); + let rebalance = calculate_rebalance(current, requests, tombstoned_list, &config.denom); resp = resp.add_messages(rebalance); Ok(resp) @@ -598,9 +620,11 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { if !removals.is_empty() || !additions.is_empty() { self.inactive.update(deps.storage, |mut old| { // Add removals - old.extend_from_slice(removals); + old.extend_from_slice(&tombstoned.iter().map(| t | (t.to_string(), true)).collect::>()); + let removals = removals.iter().filter(| r | !tombstoned.contains(r)).collect::>(); + old.extend_from_slice(&removals.iter().map(| r | (r.to_string(), false)).collect::>()); // Filter additions - old.retain(|v| !additions.iter().any(|a| a.address == *v)); + old.retain(|v| !additions.iter().any(|a| a.address == *v.0)); old.dedup(); Ok::<_, ContractError>(old) })?; @@ -1033,8 +1057,8 @@ mod tests { contract .hit_epoch(deps.as_mut()) .assert_bond(&[]) // No bond msgs after tombstoning - .assert_unbond(&[]) // No unbond msgs after tombstoning - .assert_rewards(&["val1", "val2"]); // Last rewards msgs after tombstoning + .assert_unbond(&[("val1", (15u128, &denom))]) // No unbond msgs after tombstoning + .assert_rewards(&["val2"]); // Last rewards msgs after tombstoning // Check that the bonded amounts of val1 have been slashed for double sign (25%) // Val2 is unaffected. @@ -1043,7 +1067,7 @@ mod tests { assert_eq!( bonded, [ - ("val1".to_string(), Uint128::new(15)), + ("val1".to_string(), Uint128::new(0)), ("val2".to_string(), Uint128::new(20)) ] ); @@ -1075,21 +1099,25 @@ mod tests { contract .hit_epoch(deps.as_mut()) - .assert_bond(&[("val1", (20, &denom))]) // FIXME?: Tombstoned validators can still bond - .assert_unbond(&[]) - .assert_rewards(&["val1"]); // Rewards are still being gathered + .assert_bond(&[]) // Tombstoned validators will be auto unbond + .assert_unbond(&[("val1", (28u128, &denom))]) + .assert_rewards(&[]); // Rewards are still being gathered // Check that the previously bonded amounts of val1 have been slashed for double sign (25%) let bonded = contract.bonded.load(deps.as_ref().storage).unwrap(); assert_eq!( bonded, [ - ("val1".to_string(), Uint128::new(8 + 20)), // Due to rounding up + ("val1".to_string(), Uint128::new(0)), // Due to rounding up ] ); // Subsequent rewards msgs are removed after validator is tombstoned - contract.hit_epoch(deps.as_mut()).assert_rewards(&[]); + contract + .hit_epoch(deps.as_mut()) + .assert_bond(&[]) // Tombstoned validators can still bond + .assert_unbond(&[]) + .assert_rewards(&[]); } #[test] @@ -1117,7 +1145,7 @@ mod tests { .hit_epoch(deps.as_mut()) .assert_bond(&[]) // No bond msgs after jailing .assert_unbond(&[("val1", (8u128, &denom))]) // Unbond adjusted for double sign slashing - .assert_rewards(&["val1"]); // Rewards are still being gathered + .assert_rewards(&[]); // Rewards are still being gathered // Check that bonded accounting has been adjusted let bonded = contract.bonded.load(deps.as_ref().storage).unwrap(); @@ -1325,7 +1353,7 @@ mod tests { deps, env: mock_env(), info: mock_info("me", &[]), - }) + }, true) .unwrap(); } @@ -1444,6 +1472,7 @@ mod tests { power: 0, slash_amount, slash_ratio: nominal_slash_ratio.to_string(), + is_tombstoned: false, }]), ) .unwrap(); @@ -1482,7 +1511,7 @@ mod tests { self.handle_valset_update( deps, None, - None, + Some(vec![val.to_string()]), None, None, None, @@ -1496,6 +1525,7 @@ mod tests { power: 0, slash_amount, slash_ratio: nominal_slash_ratio.to_string(), + is_tombstoned: true, }]), ) .unwrap(); diff --git a/contracts/consumer/virtual-staking/src/multitest.rs b/contracts/consumer/virtual-staking/src/multitest.rs index 54be3760..27bca83b 100644 --- a/contracts/consumer/virtual-staking/src/multitest.rs +++ b/contracts/consumer/virtual-staking/src/multitest.rs @@ -59,6 +59,7 @@ fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { discount, JUNO.to_owned(), virtual_staking_code.code_id(), + true, Some(admin.to_owned()), ) .with_label("Juno Converter") diff --git a/contracts/consumer/virtual-staking/src/state.rs b/contracts/consumer/virtual-staking/src/state.rs index 705e61a7..63e58802 100644 --- a/contracts/consumer/virtual-staking/src/state.rs +++ b/contracts/consumer/virtual-staking/src/state.rs @@ -8,4 +8,7 @@ pub struct Config { /// The address of the converter contract (that is authorized to bond/unbond and will receive rewards) pub converter: Addr, + + /// If it enable, tombstoned validators will be unbond automatically + pub tombstoned_unbond_enable: bool, } diff --git a/packages/apis/src/virtual_staking_api.rs b/packages/apis/src/virtual_staking_api.rs index 982855ce..39023732 100644 --- a/packages/apis/src/virtual_staking_api.rs +++ b/packages/apis/src/virtual_staking_api.rs @@ -102,4 +102,6 @@ pub struct ValidatorSlash { /// The (nominal) slash ratio for the validator. /// Useful in case we don't know if it's a double sign or downtime slash. pub slash_ratio: String, + /// Validator is tombstoned or not. + pub is_tombstoned: bool, } From 9a2edc5cfa3f230bbf9f855f76b03bb2b075ec31 Mon Sep 17 00:00:00 2001 From: Trinity Date: Fri, 16 Aug 2024 17:04:38 +0700 Subject: [PATCH 08/17] lint --- contracts/consumer/converter/src/contract.rs | 4 +- .../consumer/virtual-staking/src/contract.rs | 61 +++++++++++++++---- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/contracts/consumer/converter/src/contract.rs b/contracts/consumer/converter/src/contract.rs index 1063e118..1c2fe4c6 100644 --- a/contracts/consumer/converter/src/contract.rs +++ b/contracts/consumer/converter/src/contract.rs @@ -96,7 +96,9 @@ impl ConverterContract<'_> { ctx.deps.api.addr_validate(admin)?; } - let msg = to_json_binary(&mesh_virtual_staking::contract::sv::InstantiateMsg { tombstoned_unbond_enable })?; + let msg = to_json_binary(&mesh_virtual_staking::contract::sv::InstantiateMsg { + tombstoned_unbond_enable, + })?; // Instantiate virtual staking contract let init_msg = WasmMsg::Instantiate { admin, diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index deac10ce..0cf237c5 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -246,7 +246,7 @@ fn calculate_rebalance( let mut msgs = vec![]; for (validator, prev) in current { let next = desired.remove(&validator).unwrap_or_else(Uint128::zero); - if tombstoned_list.contains(&validator) && !next.is_zero(){ + if tombstoned_list.contains(&validator) && !next.is_zero() { let amount = coin(next.u128(), denom); msgs.push(VirtualStakeMsg::Unbond { validator, amount }.into()); continue; @@ -508,7 +508,14 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { // withdraw rewards let bonded = self.bonded.load(deps.storage)?; let inactive_list = self.inactive.load(deps.storage)?; - let withdraw = withdraw_reward_msgs(deps.branch(), &bonded, &inactive_list.iter().map(| (i, _) | i.to_string()).collect::>()); + let withdraw = withdraw_reward_msgs( + deps.branch(), + &bonded, + &inactive_list + .iter() + .map(|(i, _)| i.to_string()) + .collect::>(), + ); let mut resp = Response::new().add_submessages(withdraw); let bond = @@ -531,7 +538,12 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { self.adjust_slashings(deps.branch(), &mut current, &slash)?; // Update inactive list. Defensive, as it should already been updated in handle_valset_update, due to removals self.inactive.update(deps.branch().storage, |mut old| { - old.extend_from_slice(&slash.iter().map(|v| (v.address.clone(), v.is_tombstoned)).collect::>()); + old.extend_from_slice( + &slash + .iter() + .map(|v| (v.address.clone(), v.is_tombstoned)) + .collect::>(), + ); old.dedup(); Ok::<_, ContractError>(old) })?; @@ -559,7 +571,13 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { // Force the tombstoned validator to auto unbond let tombstoned_list = inactive_list .iter() - .filter_map(| (val, is_tombstoned) | if *is_tombstoned { Some(val.to_string()) } else { None }) + .filter_map(|(val, is_tombstoned)| { + if *is_tombstoned { + Some(val.to_string()) + } else { + None + } + }) .collect::>(); let mut request_with_tombstoned = requests.clone(); for (val, amount) in request_with_tombstoned.iter_mut() { @@ -571,7 +589,8 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { } // Save the future values - self.bonded.save(deps.branch().storage, &request_with_tombstoned)?; + self.bonded + .save(deps.branch().storage, &request_with_tombstoned)?; // Compare these two to make bond/unbond calls as needed let rebalance = calculate_rebalance(current, requests, tombstoned_list, &config.denom); @@ -620,9 +639,22 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { if !removals.is_empty() || !additions.is_empty() { self.inactive.update(deps.storage, |mut old| { // Add removals - old.extend_from_slice(&tombstoned.iter().map(| t | (t.to_string(), true)).collect::>()); - let removals = removals.iter().filter(| r | !tombstoned.contains(r)).collect::>(); - old.extend_from_slice(&removals.iter().map(| r | (r.to_string(), false)).collect::>()); + old.extend_from_slice( + &tombstoned + .iter() + .map(|t| (t.to_string(), true)) + .collect::>(), + ); + let removals = removals + .iter() + .filter(|r| !tombstoned.contains(r)) + .collect::>(); + old.extend_from_slice( + &removals + .iter() + .map(|r| (r.to_string(), false)) + .collect::>(), + ); // Filter additions old.retain(|v| !additions.iter().any(|a| a.address == *v.0)); old.dedup(); @@ -1349,11 +1381,14 @@ mod tests { impl VirtualStakingExt for VirtualStakingContract<'_> { fn quick_inst(&self, deps: DepsMut) { - self.instantiate(InstantiateCtx { - deps, - env: mock_env(), - info: mock_info("me", &[]), - }, true) + self.instantiate( + InstantiateCtx { + deps, + env: mock_env(), + info: mock_info("me", &[]), + }, + true, + ) .unwrap(); } From e8d2571a5e581d53921cb12be56a8d29b53a5c7c Mon Sep 17 00:00:00 2001 From: Trinity Date: Fri, 16 Aug 2024 17:18:27 +0700 Subject: [PATCH 09/17] correct version in cargo --- contracts/consumer/converter/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/consumer/converter/Cargo.toml b/contracts/consumer/converter/Cargo.toml index 4a940c64..f28e95fe 100644 --- a/contracts/consumer/converter/Cargo.toml +++ b/contracts/consumer/converter/Cargo.toml @@ -23,7 +23,7 @@ fake-custom = [ "mesh-simple-price-feed/fake-custom" ] [dependencies] mesh-apis = { workspace = true } mesh-bindings = { workspace = true } -mesh-virtual-staking = { workspace = true, features = ["mt"]} +mesh-virtual-staking = { workspace = true } sylvia = { workspace = true } cosmwasm-schema = { workspace = true } @@ -39,6 +39,7 @@ thiserror = { workspace = true } [dev-dependencies] mesh-burn = { workspace = true } mesh-simple-price-feed = { workspace = true, features = ["mt"] } +mesh-virtual-staking = { workspace = true, features = ["mt"] } cw-multi-test = { workspace = true } test-case = { workspace = true } From 5d9d7f9811d83dad26080dc10d42de3315826b7e Mon Sep 17 00:00:00 2001 From: Trinity Date: Tue, 27 Aug 2024 12:49:14 +0700 Subject: [PATCH 10/17] Fix logic for immediate unbonding --- contracts/provider/native-staking-proxy/src/contract.rs | 2 +- packages/bindings/src/msg.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/contracts/provider/native-staking-proxy/src/contract.rs b/contracts/provider/native-staking-proxy/src/contract.rs index 8cbc9f9b..e362ee8c 100644 --- a/contracts/provider/native-staking-proxy/src/contract.rs +++ b/contracts/provider/native-staking-proxy/src/contract.rs @@ -294,7 +294,7 @@ impl NativeStakingProxyContract<'_> { ContractError::InvalidDenom(amount.denom) ); - let msg = ProviderMsg::Unstake { validator, amount }; + let msg = ProviderMsg::Unstake { delegator: ctx.info.sender.to_string(), validator, amount }; Ok(Response::new().add_message(msg)) } diff --git a/packages/bindings/src/msg.rs b/packages/bindings/src/msg.rs index 2b33bb3d..01381610 100644 --- a/packages/bindings/src/msg.rs +++ b/packages/bindings/src/msg.rs @@ -92,7 +92,7 @@ pub enum ProviderMsg { /// /// If these conditions are met, it will instantly unstake /// amount.amount tokens from the native staking proxy contract. - Unstake { validator: String, amount: Coin }, + Unstake { delegator: String, validator: String, amount: Coin }, } impl ProviderMsg { @@ -118,12 +118,13 @@ impl ProviderMsg { } } - pub fn unstake(denom: &str, validator: &str, amount: impl Into) -> ProviderMsg { + pub fn unstake(denom: &str, delegator:&str, validator: &str, amount: impl Into) -> ProviderMsg { let coin = Coin { amount: amount.into(), denom: denom.into(), }; ProviderMsg::Unstake { + delegator: delegator.to_string(), validator: validator.to_string(), amount: coin, } From fa2b8532eaea2fdd116c277d3ba0e2db39b88b55 Mon Sep 17 00:00:00 2001 From: Trinity Date: Thu, 29 Aug 2024 12:48:44 +0700 Subject: [PATCH 11/17] Complete logic for auto unbond tombstoned validator --- .../consumer/virtual-staking/src/contract.rs | 62 ++++++++++++------- .../provider/external-staking/src/contract.rs | 9 +++ packages/bindings/src/lib.rs | 2 +- packages/bindings/src/query.rs | 14 +++++ packages/virtual-staking-mock/src/lib.rs | 3 + 5 files changed, 66 insertions(+), 24 deletions(-) diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index 0cf237c5..31a1a86e 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -2,8 +2,7 @@ use std::cmp::Ordering; use std::collections::{BTreeMap, HashMap, HashSet}; use cosmwasm_std::{ - coin, ensure_eq, to_json_binary, Coin, CosmosMsg, CustomQuery, DepsMut, DistributionMsg, Env, - Event, Reply, Response, StdResult, Storage, SubMsg, Uint128, Validator, WasmMsg, + coin, ensure_eq, to_json_binary, Coin, CosmosMsg, CustomQuery, DepsMut, DistributionMsg, Env, Event, Reply, Response, StdResult, Storage, SubMsg, Uint128, Validator, WasmMsg }; use cw2::set_contract_version; use cw_storage_plus::{Item, Map}; @@ -237,7 +236,7 @@ fn pop_target(deps: DepsMut) -> StdResult<(String, bool fn calculate_rebalance( current: Vec<(String, Uint128)>, desired: Vec<(String, Uint128)>, - tombstoned_list: HashSet, + tombstoned_list: HashMap, denom: &str, ) -> Vec> { let mut desired: BTreeMap<_, _> = desired.into_iter().collect(); @@ -246,8 +245,8 @@ fn calculate_rebalance( let mut msgs = vec![]; for (validator, prev) in current { let next = desired.remove(&validator).unwrap_or_else(Uint128::zero); - if tombstoned_list.contains(&validator) && !next.is_zero() { - let amount = coin(next.u128(), denom); + if tombstoned_list.contains_key(&validator) && !next.is_zero() { + let amount = tombstoned_list.get(&validator).unwrap().clone(); msgs.push(VirtualStakeMsg::Unbond { validator, amount }.into()); continue; } @@ -569,19 +568,17 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { } // Force the tombstoned validator to auto unbond - let tombstoned_list = inactive_list - .iter() - .filter_map(|(val, is_tombstoned)| { - if *is_tombstoned { - Some(val.to_string()) - } else { - None - } - }) - .collect::>(); + let mut tombstoned_list: HashMap = HashMap::new(); + for (val, is_tombstoned) in inactive_list.iter() { + if *is_tombstoned { + let resp = TokenQuerier::new(&deps.querier).total_delegations(env.contract.address.to_string(), val.to_string())?; + tombstoned_list.insert(val.to_string(), resp.delegation); + } + } + let mut request_with_tombstoned = requests.clone(); for (val, amount) in request_with_tombstoned.iter_mut() { - if tombstoned_list.contains(val) { + if tombstoned_list.contains_key(val) { *amount = Uint128::zero(); // Update new value for the bond requests self.bond_requests.save(deps.storage, val, amount)?; @@ -636,7 +633,7 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { // Update inactive list. // We ignore `unjailed` as it's not clear they make the validator active again or not. - if !removals.is_empty() || !additions.is_empty() { + if !removals.is_empty() || !additions.is_empty() || !tombstoned.is_empty() { self.inactive.update(deps.storage, |mut old| { // Add removals old.extend_from_slice( @@ -645,10 +642,6 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { .map(|t| (t.to_string(), true)) .collect::>(), ); - let removals = removals - .iter() - .filter(|r| !tombstoned.contains(r)) - .collect::>(); old.extend_from_slice( &removals .iter() @@ -705,7 +698,7 @@ mod tests { testing::{mock_env, mock_info, MockApi, MockQuerier, MockStorage}, Decimal, }; - use mesh_bindings::{BondStatusResponse, SlashRatioResponse}; + use mesh_bindings::{BondStatusResponse, SlashRatioResponse, TotalDelegationResponse}; use super::*; @@ -1273,6 +1266,9 @@ mod tests { slash_fraction_downtime: "0.1".to_string(), slash_fraction_double_sign: "0.25".to_string(), }); + let total_delegation = MockTotalDelegation::new(TotalDelegationResponse { + delegation: coin(0, "DOES NOT MATTER"), + }); let handler = { let bs_copy = bond_status.clone(); @@ -1289,6 +1285,11 @@ mod tests { to_json_binary(&*slash_ratio.borrow()).unwrap(), )) } + mesh_bindings::VirtualStakeQuery::TotalDelegation { .. } => { + cosmwasm_std::SystemResult::Ok(cosmwasm_std::ContractResult::Ok( + to_json_binary(&*total_delegation.borrow()).unwrap(), + )) + } } } }; @@ -1339,6 +1340,21 @@ mod tests { } } + + #[derive(Clone)] + struct MockTotalDelegation(Rc>); + + impl MockTotalDelegation { + fn new(res: TotalDelegationResponse) -> Self { + Self(Rc::new(RefCell::new(res))) + } + + fn borrow(&self) -> Ref<'_, TotalDelegationResponse> { + self.0.borrow() + } + } + + fn set_reward_targets(storage: &mut dyn Storage, targets: &[&str]) { REWARD_TARGETS .save( @@ -1546,7 +1562,7 @@ mod tests { self.handle_valset_update( deps, None, - Some(vec![val.to_string()]), + None, None, None, None, diff --git a/contracts/provider/external-staking/src/contract.rs b/contracts/provider/external-staking/src/contract.rs index 25293bc3..92408c42 100644 --- a/contracts/provider/external-staking/src/contract.rs +++ b/contracts/provider/external-staking/src/contract.rs @@ -2562,4 +2562,13 @@ mod tests { ] ); } + + #[test] + fn convert_str_decimal() { + let slash_ratio = match String::from("0.100000000000000000").parse::(){ + Ok(ratio) => ratio, + Err(err) => panic!("err: {}", err) + }; + println!("slash_ratio: {:#?}", slash_ratio); + } } diff --git a/packages/bindings/src/lib.rs b/packages/bindings/src/lib.rs index 45c3ba81..2c8e83a9 100644 --- a/packages/bindings/src/lib.rs +++ b/packages/bindings/src/lib.rs @@ -3,7 +3,7 @@ mod query; pub use msg::{ProviderCustomMsg, ProviderMsg, VirtualStakeCustomMsg, VirtualStakeMsg}; pub use query::{ - BondStatusResponse, SlashRatioResponse, TokenQuerier, VirtualStakeCustomQuery, + BondStatusResponse, SlashRatioResponse, TotalDelegationResponse, TokenQuerier, VirtualStakeCustomQuery, VirtualStakeQuery, }; diff --git a/packages/bindings/src/query.rs b/packages/bindings/src/query.rs index 07af5ac5..47766064 100644 --- a/packages/bindings/src/query.rs +++ b/packages/bindings/src/query.rs @@ -21,6 +21,10 @@ pub enum VirtualStakeQuery { /// Returns the blockchain's slashing ratios. #[returns(SlashRatioResponse)] SlashRatio {}, + + /// Returns total delegations of the give validator + #[returns(TotalDelegationResponse)] + TotalDelegation { contract: String, validator: String }, } /// Bookkeeping info in the virtual staking sdk module @@ -42,6 +46,11 @@ pub struct SlashRatioResponse { pub slash_fraction_double_sign: String, } +#[cw_serde] +pub struct TotalDelegationResponse { + pub delegation: Coin +} + impl CustomQuery for VirtualStakeCustomQuery {} impl From for QueryRequest { @@ -69,4 +78,9 @@ impl<'a> TokenQuerier<'a> { let slash_ratio_query = VirtualStakeQuery::SlashRatio {}; self.querier.query(&slash_ratio_query.into()) } + + pub fn total_delegations(&self, contract: String, validator: String) -> StdResult { + let total_delegations_query = VirtualStakeQuery::TotalDelegation { contract, validator }; + self.querier.query(&total_delegations_query.into()) + } } diff --git a/packages/virtual-staking-mock/src/lib.rs b/packages/virtual-staking-mock/src/lib.rs index 09abfeb6..9ac144f2 100644 --- a/packages/virtual-staking-mock/src/lib.rs +++ b/packages/virtual-staking-mock/src/lib.rs @@ -181,6 +181,9 @@ impl Module for VirtualStakingModule { mesh_bindings::VirtualStakeQuery::SlashRatio {} => { to_json_binary(&self.slash_ratio.load(storage)?)? } + mesh_bindings::VirtualStakeQuery::TotalDelegation { contract, validator } => { + to_json_binary(&self.bonds.load(storage, (Addr::unchecked(&contract), Addr::unchecked(&validator)))?)? + } }; Ok(to_json_binary(&result)?) From 38dbdba574f3141c9410d9b893b90c109bede348 Mon Sep 17 00:00:00 2001 From: Trinity Date: Thu, 29 Aug 2024 12:49:01 +0700 Subject: [PATCH 12/17] lint --- .../consumer/virtual-staking/src/contract.rs | 8 ++++---- .../provider/external-staking/src/contract.rs | 4 ++-- packages/bindings/src/lib.rs | 4 ++-- packages/bindings/src/query.rs | 15 +++++++++++---- packages/virtual-staking-mock/src/lib.rs | 10 +++++++--- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index 31a1a86e..cfae0a82 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -2,7 +2,8 @@ use std::cmp::Ordering; use std::collections::{BTreeMap, HashMap, HashSet}; use cosmwasm_std::{ - coin, ensure_eq, to_json_binary, Coin, CosmosMsg, CustomQuery, DepsMut, DistributionMsg, Env, Event, Reply, Response, StdResult, Storage, SubMsg, Uint128, Validator, WasmMsg + coin, ensure_eq, to_json_binary, Coin, CosmosMsg, CustomQuery, DepsMut, DistributionMsg, Env, + Event, Reply, Response, StdResult, Storage, SubMsg, Uint128, Validator, WasmMsg, }; use cw2::set_contract_version; use cw_storage_plus::{Item, Map}; @@ -571,7 +572,8 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { let mut tombstoned_list: HashMap = HashMap::new(); for (val, is_tombstoned) in inactive_list.iter() { if *is_tombstoned { - let resp = TokenQuerier::new(&deps.querier).total_delegations(env.contract.address.to_string(), val.to_string())?; + let resp = TokenQuerier::new(&deps.querier) + .total_delegations(env.contract.address.to_string(), val.to_string())?; tombstoned_list.insert(val.to_string(), resp.delegation); } } @@ -1340,7 +1342,6 @@ mod tests { } } - #[derive(Clone)] struct MockTotalDelegation(Rc>); @@ -1354,7 +1355,6 @@ mod tests { } } - fn set_reward_targets(storage: &mut dyn Storage, targets: &[&str]) { REWARD_TARGETS .save( diff --git a/contracts/provider/external-staking/src/contract.rs b/contracts/provider/external-staking/src/contract.rs index 92408c42..b953c151 100644 --- a/contracts/provider/external-staking/src/contract.rs +++ b/contracts/provider/external-staking/src/contract.rs @@ -2565,9 +2565,9 @@ mod tests { #[test] fn convert_str_decimal() { - let slash_ratio = match String::from("0.100000000000000000").parse::(){ + let slash_ratio = match String::from("0.100000000000000000").parse::() { Ok(ratio) => ratio, - Err(err) => panic!("err: {}", err) + Err(err) => panic!("err: {}", err), }; println!("slash_ratio: {:#?}", slash_ratio); } diff --git a/packages/bindings/src/lib.rs b/packages/bindings/src/lib.rs index 2c8e83a9..4600323e 100644 --- a/packages/bindings/src/lib.rs +++ b/packages/bindings/src/lib.rs @@ -3,8 +3,8 @@ mod query; pub use msg::{ProviderCustomMsg, ProviderMsg, VirtualStakeCustomMsg, VirtualStakeMsg}; pub use query::{ - BondStatusResponse, SlashRatioResponse, TotalDelegationResponse, TokenQuerier, VirtualStakeCustomQuery, - VirtualStakeQuery, + BondStatusResponse, SlashRatioResponse, TokenQuerier, TotalDelegationResponse, + VirtualStakeCustomQuery, VirtualStakeQuery, }; // This is a signal, such that any contract that imports these helpers diff --git a/packages/bindings/src/query.rs b/packages/bindings/src/query.rs index 47766064..2db2bdb7 100644 --- a/packages/bindings/src/query.rs +++ b/packages/bindings/src/query.rs @@ -48,7 +48,7 @@ pub struct SlashRatioResponse { #[cw_serde] pub struct TotalDelegationResponse { - pub delegation: Coin + pub delegation: Coin, } impl CustomQuery for VirtualStakeCustomQuery {} @@ -79,8 +79,15 @@ impl<'a> TokenQuerier<'a> { self.querier.query(&slash_ratio_query.into()) } - pub fn total_delegations(&self, contract: String, validator: String) -> StdResult { - let total_delegations_query = VirtualStakeQuery::TotalDelegation { contract, validator }; + pub fn total_delegations( + &self, + contract: String, + validator: String, + ) -> StdResult { + let total_delegations_query = VirtualStakeQuery::TotalDelegation { + contract, + validator, + }; self.querier.query(&total_delegations_query.into()) - } + } } diff --git a/packages/virtual-staking-mock/src/lib.rs b/packages/virtual-staking-mock/src/lib.rs index 9ac144f2..ba0eb0e8 100644 --- a/packages/virtual-staking-mock/src/lib.rs +++ b/packages/virtual-staking-mock/src/lib.rs @@ -181,9 +181,13 @@ impl Module for VirtualStakingModule { mesh_bindings::VirtualStakeQuery::SlashRatio {} => { to_json_binary(&self.slash_ratio.load(storage)?)? } - mesh_bindings::VirtualStakeQuery::TotalDelegation { contract, validator } => { - to_json_binary(&self.bonds.load(storage, (Addr::unchecked(&contract), Addr::unchecked(&validator)))?)? - } + mesh_bindings::VirtualStakeQuery::TotalDelegation { + contract, + validator, + } => to_json_binary(&self.bonds.load( + storage, + (Addr::unchecked(&contract), Addr::unchecked(&validator)), + )?)?, }; Ok(to_json_binary(&result)?) From b8347379756ea183562965b843d345ba5465c34d Mon Sep 17 00:00:00 2001 From: Trinity Date: Fri, 30 Aug 2024 09:20:45 +0700 Subject: [PATCH 13/17] Check amount is zero --- contracts/consumer/virtual-staking/src/contract.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index cfae0a82..9d6bf2dc 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -248,7 +248,9 @@ fn calculate_rebalance( let next = desired.remove(&validator).unwrap_or_else(Uint128::zero); if tombstoned_list.contains_key(&validator) && !next.is_zero() { let amount = tombstoned_list.get(&validator).unwrap().clone(); - msgs.push(VirtualStakeMsg::Unbond { validator, amount }.into()); + if !amount.amount.is_zero() { + msgs.push(VirtualStakeMsg::Unbond { validator, amount }.into()); + } continue; } match next.cmp(&prev) { From c77d510eb7a71572e49c0ff39f60fc63927fb0a5 Mon Sep 17 00:00:00 2001 From: Trinity Date: Fri, 30 Aug 2024 13:40:46 +0700 Subject: [PATCH 14/17] lint --- .../provider/native-staking-proxy/src/contract.rs | 6 +++++- packages/bindings/src/msg.rs | 13 +++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/contracts/provider/native-staking-proxy/src/contract.rs b/contracts/provider/native-staking-proxy/src/contract.rs index e362ee8c..fcbc52c8 100644 --- a/contracts/provider/native-staking-proxy/src/contract.rs +++ b/contracts/provider/native-staking-proxy/src/contract.rs @@ -294,7 +294,11 @@ impl NativeStakingProxyContract<'_> { ContractError::InvalidDenom(amount.denom) ); - let msg = ProviderMsg::Unstake { delegator: ctx.info.sender.to_string(), validator, amount }; + let msg = ProviderMsg::Unstake { + delegator: ctx.info.sender.to_string(), + validator, + amount, + }; Ok(Response::new().add_message(msg)) } diff --git a/packages/bindings/src/msg.rs b/packages/bindings/src/msg.rs index 01381610..cedb0767 100644 --- a/packages/bindings/src/msg.rs +++ b/packages/bindings/src/msg.rs @@ -92,7 +92,11 @@ pub enum ProviderMsg { /// /// If these conditions are met, it will instantly unstake /// amount.amount tokens from the native staking proxy contract. - Unstake { delegator: String, validator: String, amount: Coin }, + Unstake { + delegator: String, + validator: String, + amount: Coin, + }, } impl ProviderMsg { @@ -118,7 +122,12 @@ impl ProviderMsg { } } - pub fn unstake(denom: &str, delegator:&str, validator: &str, amount: impl Into) -> ProviderMsg { + pub fn unstake( + denom: &str, + delegator: &str, + validator: &str, + amount: impl Into, + ) -> ProviderMsg { let coin = Coin { amount: amount.into(), denom: denom.into(), From 097f1d98e274216fd1a1a9674f4461442dfba771 Mon Sep 17 00:00:00 2001 From: Trinity Date: Thu, 5 Sep 2024 13:39:36 +0700 Subject: [PATCH 15/17] Fix tests --- .../consumer/virtual-staking/src/contract.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index 9d6bf2dc..6587541e 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -1083,6 +1083,7 @@ mod tests { // Val1 is being tombstoned contract.tombstone(deps.as_mut(), "val1", Decimal::percent(25), Uint128::new(5)); + knobs.total_delegation.update_total_delegation(15u128, &denom); contract .hit_epoch(deps.as_mut()) .assert_bond(&[]) // No bond msgs after tombstoning @@ -1125,6 +1126,7 @@ mod tests { // And it's being tombstoned at the same time contract.tombstone(deps.as_mut(), "val1", Decimal::percent(25), Uint128::new(2)); + knobs.total_delegation.update_total_delegation(28u128, &denom); contract .hit_epoch(deps.as_mut()) @@ -1276,6 +1278,7 @@ mod tests { let handler = { let bs_copy = bond_status.clone(); + let td_copy = total_delegation.clone(); move |msg: &_| { let VirtualStakeCustomQuery::VirtualStake(msg) = msg; match msg { @@ -1291,7 +1294,7 @@ mod tests { } mesh_bindings::VirtualStakeQuery::TotalDelegation { .. } => { cosmwasm_std::SystemResult::Ok(cosmwasm_std::ContractResult::Ok( - to_json_binary(&*total_delegation.borrow()).unwrap(), + to_json_binary(&*td_copy.borrow()).unwrap(), )) } } @@ -1305,12 +1308,13 @@ mod tests { querier: MockQuerier::new(&[]).with_custom_handler(handler), custom_query_type: PhantomData, }, - StakingKnobs { bond_status }, + StakingKnobs { bond_status, total_delegation }, ) } struct StakingKnobs { bond_status: MockBondStatus, + total_delegation: MockTotalDelegation, } #[derive(Clone)] @@ -1355,6 +1359,14 @@ mod tests { fn borrow(&self) -> Ref<'_, TotalDelegationResponse> { self.0.borrow() } + + fn update_total_delegation(&self, amount: impl Into, denom: impl Into) { + let mut mut_obj = self.0.borrow_mut(); + mut_obj.delegation = Coin{ + amount: amount.into(), + denom: denom.into(), + }; + } } fn set_reward_targets(storage: &mut dyn Storage, targets: &[&str]) { From 0af62579c32516d16dd11fc0f28ec1f9df839310 Mon Sep 17 00:00:00 2001 From: Trinity Date: Thu, 5 Sep 2024 13:39:46 +0700 Subject: [PATCH 16/17] lint --- .../consumer/virtual-staking/src/contract.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index 6587541e..3e124a5d 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -1083,7 +1083,9 @@ mod tests { // Val1 is being tombstoned contract.tombstone(deps.as_mut(), "val1", Decimal::percent(25), Uint128::new(5)); - knobs.total_delegation.update_total_delegation(15u128, &denom); + knobs + .total_delegation + .update_total_delegation(15u128, &denom); contract .hit_epoch(deps.as_mut()) .assert_bond(&[]) // No bond msgs after tombstoning @@ -1126,7 +1128,9 @@ mod tests { // And it's being tombstoned at the same time contract.tombstone(deps.as_mut(), "val1", Decimal::percent(25), Uint128::new(2)); - knobs.total_delegation.update_total_delegation(28u128, &denom); + knobs + .total_delegation + .update_total_delegation(28u128, &denom); contract .hit_epoch(deps.as_mut()) @@ -1308,7 +1312,10 @@ mod tests { querier: MockQuerier::new(&[]).with_custom_handler(handler), custom_query_type: PhantomData, }, - StakingKnobs { bond_status, total_delegation }, + StakingKnobs { + bond_status, + total_delegation, + }, ) } @@ -1362,7 +1369,7 @@ mod tests { fn update_total_delegation(&self, amount: impl Into, denom: impl Into) { let mut mut_obj = self.0.borrow_mut(); - mut_obj.delegation = Coin{ + mut_obj.delegation = Coin { amount: amount.into(), denom: denom.into(), }; From 0ffa7794f3dc95a3f3687dd0e64b2dda0b03d578 Mon Sep 17 00:00:00 2001 From: Trinity Date: Thu, 5 Sep 2024 13:49:38 +0700 Subject: [PATCH 17/17] lint --- contracts/consumer/converter/src/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/consumer/converter/src/contract.rs b/contracts/consumer/converter/src/contract.rs index 47ab3b6d..20e1ea8e 100644 --- a/contracts/consumer/converter/src/contract.rs +++ b/contracts/consumer/converter/src/contract.rs @@ -103,7 +103,7 @@ impl ConverterContract<'_> { max_retrieve, tombstoned_unbond_enable, })?; - + // Instantiate virtual staking contract let init_msg = WasmMsg::Instantiate { admin,