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

Slash amounts support #179

Merged
merged 15 commits into from
Dec 11, 2023
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 .github/workflows/basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,4 @@ jobs:
with:
toolchain: 1.70.0
command: clippy
args: --all-targets -- -D warnings
args: --all-targets -- -D warnings -A clippy::too-many-arguments
93 changes: 90 additions & 3 deletions contracts/consumer/converter/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use cosmwasm_std::{
ensure_eq, to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, Deps, DepsMut, Event,
ensure_eq, to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, Deps, DepsMut, Event, Fraction,
MessageInfo, Reply, Response, SubMsg, SubMsgResponse, Uint128, Validator, WasmMsg,
};
use cw2::set_contract_version;
Expand All @@ -9,7 +9,7 @@ use mesh_apis::ibc::ConsumerPacket;
use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, ReplyCtx};
use sylvia::{contract, schemars};

use mesh_apis::converter_api::{self, ConverterApi, RewardInfo};
use mesh_apis::converter_api::{self, ConverterApi, RewardInfo, ValidatorSlashInfo};
use mesh_apis::price_feed_api;
use mesh_apis::virtual_staking_api;

Expand Down Expand Up @@ -59,7 +59,7 @@ impl ConverterContract<'_> {
) -> Result<Response, ContractError> {
nonpayable(&ctx.info)?;
// validate args
if discount > Decimal::one() {
if discount >= Decimal::one() {
return Err(ContractError::InvalidDiscount);
}
if remote_denom.is_empty() {
Expand Down Expand Up @@ -285,6 +285,34 @@ impl ConverterContract<'_> {
})
}

fn invert_price(&self, deps: Deps, amount: Coin) -> Result<Coin, ContractError> {
let config = self.config.load(deps.storage)?;
ensure_eq!(
config.local_denom,
amount.denom,
ContractError::WrongDenom {
sent: amount.denom,
expected: config.local_denom
}
);

// get the price value (usage is a bit clunky, need to use trait and cannot chain Remote::new() with .querier())
// also see https://github.com/CosmWasm/sylvia/issues/181 to just store Remote in state
use price_feed_api::Querier;
let remote = price_feed_api::Remote::new(config.price_feed);
let price = remote.querier(&deps.querier).price()?.native_per_foreign;
let converted = (amount.amount * price.inv().ok_or(ContractError::InvalidPrice {})?)
* config
.price_adjustment
.inv()
.ok_or(ContractError::InvalidDiscount {})?;

Ok(Coin {
denom: config.remote_denom,
amount: converted,
})
}

pub(crate) fn transfer_rewards(
&self,
deps: Deps,
Expand Down Expand Up @@ -395,6 +423,7 @@ impl ConverterApi for ConverterContract<'_> {
/// Send validator set additions (entering the active validator set), jailings and tombstonings
/// to the external staking contract on the Consumer via IBC.
#[msg(exec)]
#[allow(clippy::too_many_arguments)]
fn valset_update(
&self,
ctx: ExecCtx,
Expand All @@ -404,6 +433,7 @@ impl ConverterApi for ConverterContract<'_> {
jailed: Vec<String>,
unjailed: Vec<String>,
tombstoned: Vec<String>,
mut slashed: Vec<ValidatorSlashInfo>,
) -> Result<Response, Self::Error> {
self.ensure_authorized(&ctx.deps, &ctx.info)?;

Expand Down Expand Up @@ -451,6 +481,62 @@ impl ConverterApi for ConverterContract<'_> {
event = event.add_attribute("tombstoned", tombstoned.join(","));
is_empty = false;
}
if !slashed.is_empty() {
event = event.add_attribute(
"slashed",
slashed
.iter()
.map(|v| v.address.clone())
.collect::<Vec<String>>()
.join(","),
);
event = event.add_attribute(
"ratios",
slashed
.iter()
.map(|v| v.slash_ratio.clone())
.collect::<Vec<String>>()
.join(","),
);
event = event.add_attribute(
"amounts",
slashed
.iter()
.map(|v| {
[
v.slash_amount.amount.to_string(),
v.slash_amount.denom.clone(),
]
.concat()
})
.collect::<Vec<String>>()
.join(","),
);
// Convert slash amounts to Provider's coin
slashed
.iter_mut()
.map(|v| {
v.slash_amount =
self.invert_price(ctx.deps.as_ref(), v.slash_amount.clone())?;
Ok(v)
})
.collect::<Result<Vec<_>, ContractError>>()?;
event = event.add_attribute(
"provider_amounts",
slashed
.iter()
.map(|v| {
[
v.slash_amount.amount.to_string(),
v.slash_amount.denom.clone(),
]
.concat()
})
.collect::<Vec<String>>()
.join(","),
);
is_empty = false;
}
let mut resp = Response::new();
if !is_empty {
let valset_msg = valset_update_msg(
Expand All @@ -462,6 +548,7 @@ impl ConverterApi for ConverterContract<'_> {
&jailed,
&unjailed,
&tombstoned,
&slashed,
)?;
resp = resp.add_message(valset_msg);
}
Expand Down
5 changes: 4 additions & 1 deletion contracts/consumer/converter/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ pub enum ContractError {
#[error("Invalid reply id: {0}")]
InvalidReplyId(u64),

#[error("Invalid discount, must be between 0.0 and 1.0")]
#[error("Invalid price, must be greater than 0.0")]
InvalidPrice,

#[error("Invalid discount, must be greater or equal than 0.0 and less than 1.0")]
InvalidDiscount,

#[error("Invalid denom: {0}")]
Expand Down
5 changes: 4 additions & 1 deletion contracts/consumer/converter/src/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use cosmwasm_std::{
};
use cw_storage_plus::Item;

use mesh_apis::converter_api::ValidatorSlashInfo;
use mesh_apis::ibc::{
ack_success, validate_channel_order, AckWrapper, AddValidator, ConsumerPacket, ProtocolVersion,
ProviderPacket, StakeAck, TransferRewardsAck, UnstakeAck, PROTOCOL_NAME,
Expand Down Expand Up @@ -115,7 +116,7 @@ pub fn ibc_channel_connect(

// Send a validator sync packet to arrive with the newly established channel
let validators = deps.querier.query_all_validators()?;
let msg = valset_update_msg(&env, &channel, &validators, &[], &[], &[], &[], &[])?;
let msg = valset_update_msg(&env, &channel, &validators, &[], &[], &[], &[], &[], &[])?;

Ok(IbcBasicResponse::new().add_message(msg))
}
Expand All @@ -130,6 +131,7 @@ pub(crate) fn valset_update_msg(
jailed: &[String],
unjailed: &[String],
tombstoned: &[String],
slashed: &[ValidatorSlashInfo],
) -> Result<IbcMsg, ContractError> {
let additions = additions
.iter()
Expand All @@ -156,6 +158,7 @@ pub(crate) fn valset_update_msg(
jailed: jailed.to_vec(),
unjailed: unjailed.to_vec(),
tombstoned: tombstoned.to_vec(),
slashed: slashed.to_vec(),
};
let msg = IbcMsg::SendPacket {
channel_id: channel.endpoint.channel_id.clone(),
Expand Down
5 changes: 3 additions & 2 deletions contracts/consumer/converter/src/multitest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ fn valset_update_works() {
// Check that only the virtual staking contract can call this handler
let res = converter
.converter_api_proxy()
.valset_update(vec![], vec![], vec![], vec![], vec![], vec![])
.valset_update(vec![], vec![], vec![], vec![], vec![], vec![], vec![])
.call(owner);
assert_eq!(res.unwrap_err(), Unauthorized {});

Expand All @@ -342,6 +342,7 @@ fn valset_update_works() {
vec![],
vec![],
vec![],
vec![],
)
.call(virtual_staking.contract_addr.as_ref());

Expand Down Expand Up @@ -397,7 +398,7 @@ fn unauthorized() {

let err = converter
.converter_api_proxy()
.valset_update(vec![], vec![], vec![], vec![], vec![], vec![])
.valset_update(vec![], vec![], vec![], vec![], vec![], vec![], vec![])
.call("mallory")
.unwrap_err();

Expand Down
Loading
Loading