diff --git a/contracts/consumer/virtual-staking/src/contract.rs b/contracts/consumer/virtual-staking/src/contract.rs index 037e28d4..582e8841 100644 --- a/contracts/consumer/virtual-staking/src/contract.rs +++ b/contracts/consumer/virtual-staking/src/contract.rs @@ -578,7 +578,7 @@ impl VirtualStakingApi for VirtualStakingContract<'_> { if max_cap.is_zero() { let all_delegations = TokenQuerier::new(&deps.querier) .all_delegations(env.contract.address.to_string(), config.max_retrieve)?; - if all_delegations.delegations.len() == 0 { + if all_delegations.delegations.is_empty() { return Ok(resp.add_message(VirtualStakeMsg::DeleteAllScheduledTasks {})); } let mut msgs = vec![]; diff --git a/contracts/provider/vault/Cargo.toml b/contracts/provider/vault/Cargo.toml index 9969f529..668c2910 100644 --- a/contracts/provider/vault/Cargo.toml +++ b/contracts/provider/vault/Cargo.toml @@ -19,9 +19,10 @@ library = [] mt = ["library", "sylvia/mt"] [dependencies] -mesh-apis = { workspace = true } -mesh-sync = { workspace = true } -mesh-bindings = { workspace = true } +mesh-apis = { workspace = true } +mesh-sync = { workspace = true } +mesh-bindings = { workspace = true } +mesh-native-staking = { workspace = true } sylvia = { workspace = true } cosmwasm-schema = { workspace = true } @@ -41,7 +42,6 @@ test-case = { workspace = true } derivative = { workspace = true } anyhow = { workspace = true } mesh-external-staking = { workspace = true, features = ["mt"] } -mesh-native-staking = { workspace = true, features = ["mt"] } mesh-native-staking-proxy = { workspace = true, features = ["mt"] } [[bin]] diff --git a/contracts/provider/vault/src/contract.rs b/contracts/provider/vault/src/contract.rs index cf415057..a3d8ff4c 100644 --- a/contracts/provider/vault/src/contract.rs +++ b/contracts/provider/vault/src/contract.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ - coin, ensure, Addr, Binary, Coin, Decimal, DepsMut, Fraction, Order, Reply, Response, - StdResult, Storage, SubMsg, SubMsgResponse, Uint128, WasmMsg, + coin, ensure, to_json_binary, 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}; @@ -212,6 +212,61 @@ impl VaultContract<'_> { Ok(resp) } + #[sv::msg(exec)] + fn restake( + &self, + mut ctx: ExecCtx, + amount: Coin, + validator: String, + ) -> Result, ContractError> { + nonpayable(&ctx.info)?; + + let denom = self.config.load(ctx.deps.storage)?.denom; + ensure!(denom == amount.denom, ContractError::UnexpectedDenom(denom)); + + let mut user = self + .users + .may_load(ctx.deps.storage, &ctx.info.sender)? + .unwrap_or_default(); + user.collateral += amount.amount; + self.users.save(ctx.deps.storage, &ctx.info.sender, &user)?; + + let amt = amount.amount; + let mut resp = Response::new() + .add_attribute("action", "restake") + .add_attribute("sender", ctx.info.sender.clone().into_string()) + .add_attribute("amount", amt.to_string()); + let restake_msg = ProviderMsg::Restake { + delegator: ctx.info.sender.clone().into_string(), + validator: validator.clone(), + amount: amount.clone(), + }; + resp = resp.add_message(restake_msg); + + let config = self.config.load(ctx.deps.storage)?; + if let Some(local_staking) = self.local_staking.load(ctx.deps.storage)? { + self.stake( + &mut ctx, + &config, + &local_staking.contract.0, + local_staking.max_slash, + amount.clone(), + false, + )?; + + let stake_msg = local_staking.contract.receive_stake( + ctx.info.sender.to_string(), + to_json_binary(&mesh_native_staking::msg::StakeMsg { validator }).unwrap(), + vec![amount], + )?; + + resp = resp.add_message(stake_msg); + Ok(resp) + } else { + Err(ContractError::NoLocalStaking) + } + } + /// This assigns a claim of amount tokens to the remote contract, which can take some action with it #[sv::msg(exec)] fn stake_remote( diff --git a/packages/bindings/src/msg.rs b/packages/bindings/src/msg.rs index 0e74a3d7..0fdc67f8 100644 --- a/packages/bindings/src/msg.rs +++ b/packages/bindings/src/msg.rs @@ -130,6 +130,16 @@ pub enum ProviderMsg { validator: String, amount: Coin, }, + /// Restake 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 restake + /// amount.amount tokens from staking module to local staking contract. + Restake { + delegator: String, + validator: String, + amount: Coin, + }, } impl ProviderMsg { @@ -171,6 +181,23 @@ impl ProviderMsg { amount: coin, } } + + pub fn restake( + denom: &str, + delegator: &str, + validator: &str, + amount: impl Into, + ) -> ProviderMsg { + let coin = Coin { + amount: amount.into(), + denom: denom.into(), + }; + ProviderMsg::Restake { + delegator: delegator.to_string(), + validator: validator.to_string(), + amount: coin, + } + } } impl From for CosmosMsg {