Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: on_close_channel logic #203

Merged
merged 8 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/consumer/converter/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl ConverterContract<'_> {
virtual_staking_code_id: u64,
tombstoned_unbond_enable: bool,
admin: Option<String>,
max_retrieve: u16,
max_retrieve: u32,
) -> Result<custom::Response, ContractError> {
nonpayable(&ctx.info)?;
// validate args
Expand Down
6 changes: 6 additions & 0 deletions contracts/consumer/converter/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ pub enum ContractError {
#[error("You must start the channel handshake on this side, it doesn't support OpenTry")]
IbcOpenTryDisallowed,

#[error("IBC channels not match")]
IbcChannelNotMatch,

#[error("You must start the channel close on provider side")]
IbcChannelCloseInitDisallowed,

#[error("Sent wrong denom over IBC: {sent}, expected {expected}")]
WrongDenom { sent: String, expected: String },

Expand Down
24 changes: 19 additions & 5 deletions contracts/consumer/converter/src/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,18 @@
/// On closed channel, we take all tokens from reflect contract to this contract.
/// We also delete the channel entry from accounts.
pub fn ibc_channel_close(
_deps: DepsMut,
deps: DepsMut,
_env: Env,
_msg: IbcChannelCloseMsg,
) -> Result<IbcBasicResponse, ContractError> {
todo!();
let contract = ConverterContract::new();
let msg = virtual_staking_api::sv::ExecMsg::HandleCloseChannel {};
let msg = WasmMsg::Execute {
contract_addr: contract.virtual_stake.load(deps.storage)?.into(),
msg: to_json_binary(&msg)?,
funds: vec![],
};
Ok(IbcBasicResponse::new().add_message(msg))
}

#[cfg_attr(not(feature = "library"), entry_point)]
Expand Down Expand Up @@ -228,7 +235,7 @@
tx_id: _,
} => {
let response = contract.unstake(deps, delegator, validator, unstake)?;
let ack = ack_success(&UnstakeAck {})?;
let ack: cosmwasm_std::Binary = ack_success(&UnstakeAck {})?;
IbcReceiveResponse::new()
.set_ack(ack)
.add_submessages(response.messages)
Expand All @@ -247,9 +254,16 @@
ProviderPacket::TransferRewards {
rewards, recipient, ..
} => {
let msg = contract.transfer_rewards(deps.as_ref(), recipient, rewards)?;
let msg =
contract.transfer_rewards(deps.as_ref(), recipient.clone(), rewards.clone())?;
let event = Event::new("mesh-transfer-rewards")
.add_attribute("recipient", &recipient)
.add_attribute("rewards", &rewards.amount.to_string());

Check failure on line 261 in contracts/consumer/converter/src/ibc.rs

View workflow job for this annotation

GitHub Actions / Lints

the borrowed expression implements the required traits
let ack = ack_success(&TransferRewardsAck {})?;
IbcReceiveResponse::new().set_ack(ack).add_message(msg)
IbcReceiveResponse::new()
.set_ack(ack)
.add_message(msg)
.add_event(event)
}
};
Ok(res)
Expand Down
49 changes: 48 additions & 1 deletion contracts/consumer/virtual-staking/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::u32;

use cosmwasm_std::{
coin, ensure_eq, to_json_binary, Coin, CosmosMsg, CustomQuery, DepsMut, DistributionMsg, Env,
Expand Down Expand Up @@ -74,7 +75,7 @@ impl VirtualStakingContract<'_> {
pub fn instantiate(
&self,
ctx: InstantiateCtx<VirtualStakeCustomQuery>,
max_retrieve: u16,
max_retrieve: u32,
tombstoned_unbond_enable: bool,
) -> Result<Response<VirtualStakeCustomMsg>, ContractError> {
nonpayable(&ctx.info)?;
Expand Down Expand Up @@ -622,6 +623,52 @@ impl VirtualStakingApi for VirtualStakingContract<'_> {
Ok(Response::new().add_messages(msgs))
}

fn handle_close_channel(
&self,
ctx: ExecCtx<Self::QueryC>,
) -> Result<Response<VirtualStakeCustomMsg>, Self::Error> {
nonpayable(&ctx.info)?;
let ExecCtx { deps, env, info } = ctx;
let config = self.config.load(deps.storage)?;
ensure_eq!(info.sender, config.converter, ContractError::Unauthorized); // only the converter can call this

let all_delegations = TokenQuerier::new(&deps.querier)
.all_delegations(env.contract.address.to_string(), u32::MAX)?;

let mut msgs = vec![VirtualStakeMsg::DeleteAllScheduledTasks {}];
for delegation in all_delegations.delegations.iter() {
let amount = Coin {
denom: config.denom.clone(),
amount: delegation.amount,
};
msgs.push(VirtualStakeMsg::UpdateDelegation {
amount: amount.clone(),
is_deduct: true,
delegator: delegation.delegator.clone(),
validator: delegation.validator.clone(),
});
msgs.push(VirtualStakeMsg::Unbond {
amount,
validator: delegation.validator.clone(),
});
self.bond_requests
.save(deps.storage, &delegation.validator, &Uint128::zero())?;
}

let requests: Vec<(String, Uint128)> = self
.bond_requests
.range(
deps.as_ref().storage,
None,
None,
cosmwasm_std::Order::Ascending,
)
.collect::<Result<_, _>>()?;
self.bonded.save(deps.storage, &requests)?;

Ok(Response::new().add_messages(msgs))
}

// FIXME: need to handle custom message types and queries
/**
* This is called once per epoch to withdraw all rewards and rebalance the bonded tokens.
Expand Down
6 changes: 3 additions & 3 deletions contracts/consumer/virtual-staking/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ pub struct Config {
/// The address of the converter contract (that is authorized to bond/unbond and will receive rewards)
pub converter: Addr,

/// Maximum delegations per query
pub max_retrieve: u32,

/// If it enable, tombstoned validators will be unbond automatically
pub tombstoned_unbond_enable: bool,

/// Maximum delegations per query
pub max_retrieve: u16,
}
53 changes: 51 additions & 2 deletions contracts/provider/external-staking/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use cosmwasm_std::{
coin, ensure, ensure_eq, to_json_binary, Coin, Decimal, DepsMut, Env, Event, IbcMsg, Order,
Response, StdResult, Storage, Uint128, Uint256, WasmMsg,
coin, ensure, ensure_eq, to_json_binary, Addr, Coin, Decimal, DepsMut, Env, Event, IbcMsg,
Order, Response, StdResult, Storage, Uint128, Uint256, WasmMsg,
};
use cw2::set_contract_version;
use cw_storage_plus::{Bounder, Item, Map};
Expand Down Expand Up @@ -497,6 +497,55 @@
Ok(event)
}

pub(crate) fn handle_close_channel(
&self,
deps: DepsMut,
env: Env,
) -> Result<(), ContractError> {
let stakes: Vec<((Addr, String), Stake)> = self
.stakes
.stake
.range(
deps.as_ref().storage,
None,
None,
cosmwasm_std::Order::Ascending,
)
.collect::<Result<_, _>>()?;

for ((user, validator), stake) in stakes.iter() {
let mut new_stake = stake.clone();
let amount = new_stake.stake.low().clone();

Check failure on line 518 in contracts/provider/external-staking/src/contract.rs

View workflow job for this annotation

GitHub Actions / Lints

using `clone` on type `Uint128` which implements the `Copy` trait
let unbond = PendingUnbond {
amount,
release_at: env.block.time,
};
new_stake.pending_unbonds.push(unbond);
new_stake.stake = ValueRange::new_val(Uint128::zero());

let mut distribution = self
.distribution
.may_load(deps.storage, &validator)?

Check failure on line 528 in contracts/provider/external-staking/src/contract.rs

View workflow job for this annotation

GitHub Actions / Lints

this expression creates a reference which is immediately dereferenced by the compiler
.unwrap_or_default();
new_stake
.points_alignment
.stake_decreased(amount, distribution.points_per_stake);

distribution.total_stake -= amount;

// Save stake
self.stakes
.stake
.save(deps.storage, (user, validator), &new_stake)?;

// Save distribution
self.distribution
.save(deps.storage, validator, &distribution)?;
}

Ok(())
}

/// In non-test code, this is called from `ibc_packet_ack`
#[allow(clippy::too_many_arguments)]
pub(crate) fn valset_update(
Expand Down
6 changes: 6 additions & 0 deletions contracts/provider/external-staking/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ pub enum ContractError {
#[error("You must start the channel handshake on the other side, it doesn't support OpenInit")]
IbcOpenInitDisallowed,

#[error("IBC channels not match")]
IbcChannelNotMatch,

#[error("You must start the channel close on this side")]
IbcChannelCloseConfirmDisallowed,

#[error("Invalid authorized endpoint: {0}")]
InvalidEndpoint(String),

Expand Down
22 changes: 18 additions & 4 deletions contracts/provider/external-staking/src/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,25 @@ pub fn ibc_channel_connect(

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn ibc_channel_close(
_deps: DepsMut,
_env: Env,
_msg: IbcChannelCloseMsg,
deps: DepsMut,
env: Env,
msg: IbcChannelCloseMsg,
) -> Result<IbcBasicResponse, ContractError> {
todo!();
match msg {
IbcChannelCloseMsg::CloseInit { channel } => {
if channel.ne(&IBC_CHANNEL.load(deps.storage)?) {
return Err(ContractError::IbcChannelNotMatch);
}
}
IbcChannelCloseMsg::CloseConfirm { .. } => {
return Err(ContractError::IbcChannelCloseConfirmDisallowed)
}
};

let contract = ExternalStakingContract::new();
contract.handle_close_channel(deps, env)?;

Ok(IbcBasicResponse::new())
}

#[cfg_attr(not(feature = "library"), entry_point)]
Expand Down
8 changes: 8 additions & 0 deletions packages/apis/src/virtual_staking_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ pub trait VirtualStakingApi {
amount: Coin,
) -> Result<Response<Self::ExecC>, Self::Error>;

/// Handle the close channel process.
/// Unbond all tokens from contract and delete scheduled tasks.
#[sv::msg(exec)]
fn handle_close_channel(
&self,
ctx: ExecCtx<Self::QueryC>,
) -> Result<Response<Self::ExecC>, Self::Error>;

/// 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).
Expand Down
4 changes: 2 additions & 2 deletions packages/bindings/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub enum VirtualStakeQuery {

/// Returns a max retrieve amount of delegations for the given contract
#[returns(AllDelegationsResponse)]
AllDelegations { contract: String, max_retrieve: u16 },
AllDelegations { contract: String, max_retrieve: u32 },
}

/// Bookkeeping info in the virtual staking sdk module
Expand Down Expand Up @@ -108,7 +108,7 @@ impl<'a> TokenQuerier<'a> {
pub fn all_delegations(
&self,
contract: String,
max_retrieve: u16,
max_retrieve: u32,
) -> StdResult<AllDelegationsResponse> {
let all_delegations_query = VirtualStakeQuery::AllDelegations {
contract,
Expand Down
Loading