Skip to content

Commit

Permalink
More generic, simple-price and converter pass tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanfrey committed May 13, 2024
1 parent d8b13f4 commit 7a04da5
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 104 deletions.
2 changes: 2 additions & 0 deletions contracts/consumer/converter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ backtraces = ["cosmwasm-std/backtraces"]
library = []
# enables generation of mt utilities
mt = ["library", "sylvia/mt"]
# enable this for multi-tests where you need custom messages for compatibility with virtual staking
fake-custom = [ "mesh-simple-price-feed/fake-custom" ]

[dependencies]
mesh-apis = { workspace = true }
Expand Down
111 changes: 63 additions & 48 deletions contracts/consumer/converter/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,18 @@ pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

const REPLY_ID_INSTANTIATE: u64 = 1;

#[cfg(not(any(test, feature = "mt")))]
pub type ConverterCustomMsg = cosmwasm_std::Empty;
#[cfg(any(test, feature = "mt"))]
pub type ConverterCustomMsg = mesh_bindings::VirtualStakeCustomMsg;

#[cfg(not(any(test, feature = "mt")))]
pub type ConverterCustomQuery = cosmwasm_std::Empty;
#[cfg(any(test, feature = "mt"))]
pub type ConverterCustomQuery = mesh_bindings::VirtualStakeCustomQuery;
#[cfg(not(feature = "fake-custom"))]
pub mod custom {
pub type ConverterMsg = cosmwasm_std::Empty;
pub type ConverterQuery = cosmwasm_std::Empty;
pub type Response = cosmwasm_std::Response<cosmwasm_std::Empty>;
}
#[cfg(feature = "fake-custom")]
pub mod custom {
pub type ConverterMsg = mesh_bindings::VirtualStakeCustomMsg;
pub type ConverterQuery = mesh_bindings::VirtualStakeCustomQuery;
pub type Response = cosmwasm_std::Response<ConverterMsg>;
}

pub struct ConverterContract<'a> {
pub config: Item<'a, Config>,
Expand All @@ -44,7 +47,7 @@ pub struct ConverterContract<'a> {
#[sv::error(ContractError)]
#[sv::messages(converter_api as ConverterApi)]
/// Workaround for lack of support in communication `Empty` <-> `Custom` Contracts.
#[sv::custom(query=ConverterCustomQuery, msg=ConverterCustomMsg)]
#[sv::custom(query=custom::ConverterQuery, msg=custom::ConverterMsg)]
impl ConverterContract<'_> {
pub const fn new() -> Self {
Self {
Expand All @@ -63,13 +66,13 @@ impl ConverterContract<'_> {
#[sv::msg(instantiate)]
pub fn instantiate(
&self,
ctx: InstantiateCtx<ConverterCustomQuery>,
ctx: InstantiateCtx<custom::ConverterQuery>,
price_feed: String,
discount: Decimal,
remote_denom: String,
virtual_staking_code_id: u64,
admin: Option<String>,
) -> Result<Response, ContractError> {
) -> Result<custom::Response, ContractError> {
nonpayable(&ctx.info)?;
// validate args
if discount >= Decimal::one() {
Expand Down Expand Up @@ -108,9 +111,9 @@ impl ConverterContract<'_> {
#[sv::msg(reply)]
fn reply(
&self,
ctx: ReplyCtx<ConverterCustomQuery>,
ctx: ReplyCtx<custom::ConverterQuery>,
reply: Reply,
) -> Result<Response, ContractError> {
) -> Result<custom::Response, ContractError> {
match reply.id {
REPLY_ID_INSTANTIATE => self.reply_init_callback(ctx.deps, reply.result.unwrap()),
_ => Err(ContractError::InvalidReplyId(reply.id)),
Expand All @@ -120,9 +123,9 @@ impl ConverterContract<'_> {
/// Store virtual staking address
fn reply_init_callback(
&self,
deps: DepsMut<ConverterCustomQuery>,
deps: DepsMut<custom::ConverterQuery>,
reply: SubMsgResponse,
) -> Result<Response, ContractError> {
) -> Result<custom::Response, ContractError> {
let init_data = parse_instantiate_response_data(&reply.data.unwrap())?;
let virtual_staking = Addr::unchecked(init_data.contract_address);
self.virtual_stake.save(deps.storage, &virtual_staking)?;
Expand All @@ -134,10 +137,10 @@ impl ConverterContract<'_> {
#[sv::msg(exec)]
fn test_stake(
&self,
ctx: ExecCtx<ConverterCustomQuery>,
ctx: ExecCtx<custom::ConverterQuery>,
validator: String,
stake: Coin,
) -> Result<Response, ContractError> {
) -> Result<custom::Response, ContractError> {
#[cfg(any(test, feature = "mt"))]
{
// This can only ever be called in tests
Expand All @@ -155,10 +158,10 @@ impl ConverterContract<'_> {
#[sv::msg(exec)]
fn test_unstake(
&self,
ctx: ExecCtx<ConverterCustomQuery>,
ctx: ExecCtx<custom::ConverterQuery>,
validator: String,
unstake: Coin,
) -> Result<Response, ContractError> {
) -> Result<custom::Response, ContractError> {
#[cfg(any(test, feature = "mt"))]
{
// This can only ever be called in tests
Expand All @@ -176,10 +179,10 @@ impl ConverterContract<'_> {
#[sv::msg(exec)]
fn test_burn(
&self,
ctx: ExecCtx<ConverterCustomQuery>,
ctx: ExecCtx<custom::ConverterQuery>,
validators: Vec<String>,
burn: Coin,
) -> Result<Response, ContractError> {
) -> Result<custom::Response, ContractError> {
#[cfg(any(test, feature = "mt"))]
{
// This can only ever be called in tests
Expand All @@ -193,7 +196,10 @@ impl ConverterContract<'_> {
}

#[sv::msg(query)]
fn config(&self, ctx: QueryCtx<ConverterCustomQuery>) -> Result<ConfigResponse, ContractError> {
fn config(
&self,
ctx: QueryCtx<custom::ConverterQuery>,
) -> Result<ConfigResponse, ContractError> {
let config = self.config.load(ctx.deps.storage)?;
let virtual_staking = self.virtual_stake.load(ctx.deps.storage)?.into_string();
Ok(ConfigResponse {
Expand All @@ -207,10 +213,10 @@ impl ConverterContract<'_> {
/// It is pulled out into a method, so it can also be called by test_stake for testing
pub(crate) fn stake(
&self,
deps: DepsMut<ConverterCustomQuery>,
deps: DepsMut<custom::ConverterQuery>,
validator: String,
stake: Coin,
) -> Result<Response, ContractError> {
) -> Result<custom::Response, ContractError> {
let amount = self.normalize_price(deps.as_ref(), stake)?;

let event = Event::new("mesh-bond")
Expand All @@ -231,10 +237,10 @@ impl ConverterContract<'_> {
/// It is pulled out into a method, so it can also be called by test_unstake for testing
pub(crate) fn unstake(
&self,
deps: DepsMut<ConverterCustomQuery>,
deps: DepsMut<custom::ConverterQuery>,
validator: String,
unstake: Coin,
) -> Result<Response, ContractError> {
) -> Result<custom::Response, ContractError> {
let amount = self.normalize_price(deps.as_ref(), unstake)?;

let event = Event::new("mesh-unbond")
Expand All @@ -255,10 +261,10 @@ impl ConverterContract<'_> {
/// It is pulled out into a method, so it can also be called by test_burn for testing
pub(crate) fn burn(
&self,
deps: DepsMut<ConverterCustomQuery>,
deps: DepsMut<custom::ConverterQuery>,
validators: &[String],
burn: Coin,
) -> Result<Response, ContractError> {
) -> Result<custom::Response, ContractError> {
let amount = self.normalize_price(deps.as_ref(), burn)?;

let event = Event::new("mesh-burn")
Expand All @@ -280,7 +286,7 @@ impl ConverterContract<'_> {

fn normalize_price(
&self,
deps: Deps<ConverterCustomQuery>,
deps: Deps<custom::ConverterQuery>,
amount: Coin,
) -> Result<Coin, ContractError> {
let config = self.config.load(deps.storage)?;
Expand All @@ -298,10 +304,13 @@ impl ConverterContract<'_> {
// also see https://github.com/CosmWasm/sylvia/issues/181 to just store Remote in state
use price_feed_api::sv::Querier;
use sylvia::types::Remote;
// NOTE: Jan, this feels hacky... I'm not sure if this is the right way to do it
// Also, I am sticking in a random error type here, not what I will get (which is unknown)
let remote =
Remote::<&dyn price_feed_api::PriceFeedApi<Error = StdError>>::new(config.price_feed);
let remote = Remote::<
dyn price_feed_api::PriceFeedApi<
Error = StdError,
ExecC = custom::ConverterMsg,
QueryC = custom::ConverterQuery,
>,
>::new(config.price_feed);
let price = remote.querier(&deps.querier).price()?.native_per_foreign;
let converted = (amount.amount * price) * config.price_adjustment;

Expand All @@ -313,7 +322,7 @@ impl ConverterContract<'_> {

fn invert_price(
&self,
deps: Deps<ConverterCustomQuery>,
deps: Deps<custom::ConverterQuery>,
amount: Coin,
) -> Result<Coin, ContractError> {
let config = self.config.load(deps.storage)?;
Expand All @@ -330,10 +339,14 @@ impl ConverterContract<'_> {
// also see https://github.com/CosmWasm/sylvia/issues/181 to just store Remote in state
use price_feed_api::sv::Querier;
use sylvia::types::Remote;
// NOTE: Jan, this feels hacky... I'm not sure if this is the right way to do it
// Also, I am sticking in a random error type here, not what I will get (which is unknown)
let remote =
Remote::<&dyn price_feed_api::PriceFeedApi<Error = StdError>>::new(config.price_feed);
// Note: it doesn't seem to matter which error type goes here...
let remote = Remote::<
dyn price_feed_api::PriceFeedApi<
Error = StdError,
ExecC = custom::ConverterMsg,
QueryC = custom::ConverterQuery,
>,
>::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
Expand All @@ -349,10 +362,10 @@ impl ConverterContract<'_> {

pub(crate) fn transfer_rewards(
&self,
deps: Deps<ConverterCustomQuery>,
deps: Deps<custom::ConverterQuery>,
recipient: String,
rewards: Coin,
) -> Result<CosmosMsg, ContractError> {
) -> Result<CosmosMsg<custom::ConverterMsg>, ContractError> {
// ensure the address is proper
let recipient = deps.api.addr_validate(&recipient)?;

Expand All @@ -377,7 +390,7 @@ impl ConverterContract<'_> {

fn ensure_authorized(
&self,
deps: &DepsMut<ConverterCustomQuery>,
deps: &DepsMut<custom::ConverterQuery>,
info: &MessageInfo,
) -> Result<(), ContractError> {
let virtual_stake = self.virtual_stake.load(deps.storage)?;
Expand All @@ -389,14 +402,16 @@ impl ConverterContract<'_> {

impl ConverterApi for ConverterContract<'_> {
type Error = ContractError;
type ExecC = custom::ConverterMsg;
type QueryC = custom::ConverterQuery;

/// Rewards tokens (in native staking denom) are sent alongside the message, and should be distributed to all
/// stakers who staked on this validator. This is tracked on the provider, so we send an IBC packet there.
fn distribute_reward(
&self,
mut ctx: ExecCtx<ConverterCustomQuery>,
mut ctx: ExecCtx<custom::ConverterQuery>,
validator: String,
) -> Result<Response, Self::Error> {
) -> Result<custom::Response, Self::Error> {
self.ensure_authorized(&ctx.deps, &ctx.info)?;

let config = self.config.load(ctx.deps.storage)?;
Expand All @@ -419,9 +434,9 @@ impl ConverterApi for ConverterContract<'_> {
/// in the native staking denom.
fn distribute_rewards(
&self,
mut ctx: ExecCtx<ConverterCustomQuery>,
mut ctx: ExecCtx<custom::ConverterQuery>,
payments: Vec<RewardInfo>,
) -> Result<Response, Self::Error> {
) -> Result<custom::Response, Self::Error> {
self.ensure_authorized(&ctx.deps, &ctx.info)?;

let config = self.config.load(ctx.deps.storage)?;
Expand Down Expand Up @@ -459,15 +474,15 @@ impl ConverterApi for ConverterContract<'_> {
#[allow(clippy::too_many_arguments)]
fn valset_update(
&self,
ctx: ExecCtx<ConverterCustomQuery>,
ctx: ExecCtx<custom::ConverterQuery>,
additions: Vec<Validator>,
removals: Vec<String>,
updated: Vec<Validator>,
jailed: Vec<String>,
unjailed: Vec<String>,
tombstoned: Vec<String>,
mut slashed: Vec<ValidatorSlashInfo>,
) -> Result<Response, Self::Error> {
) -> Result<custom::Response, Self::Error> {
self.ensure_authorized(&ctx.deps, &ctx.info)?;

// Send over IBC to the Consumer
Expand Down
11 changes: 7 additions & 4 deletions contracts/consumer/converter/src/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ use mesh_apis::ibc::{
};
use sylvia::types::ExecCtx;

use crate::{contract::ConverterContract, error::ContractError};
use crate::{
contract::{custom, ConverterContract},
error::ContractError,
};

/// This is the maximum version of the Mesh Security protocol that we support
const SUPPORTED_IBC_PROTOCOL_VERSION: &str = "0.11.0";
Expand Down Expand Up @@ -184,10 +187,10 @@ pub fn ibc_channel_close(
/// We cannot return any meaningful response value as we do not know the response value
/// of execution. We just return ok if we dispatched, error if we failed to dispatch
pub fn ibc_packet_receive(
deps: DepsMut,
deps: DepsMut<custom::ConverterQuery>,
_env: Env,
msg: IbcPacketReceiveMsg,
) -> Result<IbcReceiveResponse, ContractError> {
) -> Result<IbcReceiveResponse<custom::ConverterMsg>, ContractError> {
let packet: ProviderPacket = from_json(msg.packet.data)?;
let contract = ConverterContract::new();
let res = match packet {
Expand Down Expand Up @@ -280,7 +283,7 @@ pub fn ibc_packet_timeout(
}

pub(crate) fn make_ibc_packet(
ctx: &mut ExecCtx,
ctx: &mut ExecCtx<custom::ConverterQuery>,
packet: ConsumerPacket,
) -> Result<IbcMsg, ContractError> {
let channel = IBC_CHANNEL.load(ctx.deps.storage)?;
Expand Down
Loading

0 comments on commit 7a04da5

Please sign in to comment.