diff --git a/contracts/consumer/converter/Cargo.toml b/contracts/consumer/converter/Cargo.toml index c60f0af3..a152fbeb 100644 --- a/contracts/consumer/converter/Cargo.toml +++ b/contracts/consumer/converter/Cargo.toml @@ -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 } diff --git a/contracts/consumer/converter/src/contract.rs b/contracts/consumer/converter/src/contract.rs index 20203484..a45887f2 100644 --- a/contracts/consumer/converter/src/contract.rs +++ b/contracts/consumer/converter/src/contract.rs @@ -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; +} +#[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; +} pub struct ConverterContract<'a> { pub config: Item<'a, Config>, @@ -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 { @@ -63,13 +66,13 @@ impl ConverterContract<'_> { #[sv::msg(instantiate)] pub fn instantiate( &self, - ctx: InstantiateCtx, + ctx: InstantiateCtx, price_feed: String, discount: Decimal, remote_denom: String, virtual_staking_code_id: u64, admin: Option, - ) -> Result { + ) -> Result { nonpayable(&ctx.info)?; // validate args if discount >= Decimal::one() { @@ -108,9 +111,9 @@ impl ConverterContract<'_> { #[sv::msg(reply)] fn reply( &self, - ctx: ReplyCtx, + ctx: ReplyCtx, reply: Reply, - ) -> Result { + ) -> Result { match reply.id { REPLY_ID_INSTANTIATE => self.reply_init_callback(ctx.deps, reply.result.unwrap()), _ => Err(ContractError::InvalidReplyId(reply.id)), @@ -120,9 +123,9 @@ impl ConverterContract<'_> { /// Store virtual staking address fn reply_init_callback( &self, - deps: DepsMut, + deps: DepsMut, reply: SubMsgResponse, - ) -> Result { + ) -> Result { 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)?; @@ -134,10 +137,10 @@ impl ConverterContract<'_> { #[sv::msg(exec)] fn test_stake( &self, - ctx: ExecCtx, + ctx: ExecCtx, validator: String, stake: Coin, - ) -> Result { + ) -> Result { #[cfg(any(test, feature = "mt"))] { // This can only ever be called in tests @@ -155,10 +158,10 @@ impl ConverterContract<'_> { #[sv::msg(exec)] fn test_unstake( &self, - ctx: ExecCtx, + ctx: ExecCtx, validator: String, unstake: Coin, - ) -> Result { + ) -> Result { #[cfg(any(test, feature = "mt"))] { // This can only ever be called in tests @@ -176,10 +179,10 @@ impl ConverterContract<'_> { #[sv::msg(exec)] fn test_burn( &self, - ctx: ExecCtx, + ctx: ExecCtx, validators: Vec, burn: Coin, - ) -> Result { + ) -> Result { #[cfg(any(test, feature = "mt"))] { // This can only ever be called in tests @@ -193,7 +196,10 @@ impl ConverterContract<'_> { } #[sv::msg(query)] - fn config(&self, ctx: QueryCtx) -> Result { + fn config( + &self, + ctx: QueryCtx, + ) -> Result { let config = self.config.load(ctx.deps.storage)?; let virtual_staking = self.virtual_stake.load(ctx.deps.storage)?.into_string(); Ok(ConfigResponse { @@ -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, + deps: DepsMut, validator: String, stake: Coin, - ) -> Result { + ) -> Result { let amount = self.normalize_price(deps.as_ref(), stake)?; let event = Event::new("mesh-bond") @@ -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, + deps: DepsMut, validator: String, unstake: Coin, - ) -> Result { + ) -> Result { let amount = self.normalize_price(deps.as_ref(), unstake)?; let event = Event::new("mesh-unbond") @@ -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, + deps: DepsMut, validators: &[String], burn: Coin, - ) -> Result { + ) -> Result { let amount = self.normalize_price(deps.as_ref(), burn)?; let event = Event::new("mesh-burn") @@ -280,7 +286,7 @@ impl ConverterContract<'_> { fn normalize_price( &self, - deps: Deps, + deps: Deps, amount: Coin, ) -> Result { let config = self.config.load(deps.storage)?; @@ -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>::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; @@ -313,7 +322,7 @@ impl ConverterContract<'_> { fn invert_price( &self, - deps: Deps, + deps: Deps, amount: Coin, ) -> Result { let config = self.config.load(deps.storage)?; @@ -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>::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 @@ -349,10 +362,10 @@ impl ConverterContract<'_> { pub(crate) fn transfer_rewards( &self, - deps: Deps, + deps: Deps, recipient: String, rewards: Coin, - ) -> Result { + ) -> Result, ContractError> { // ensure the address is proper let recipient = deps.api.addr_validate(&recipient)?; @@ -377,7 +390,7 @@ impl ConverterContract<'_> { fn ensure_authorized( &self, - deps: &DepsMut, + deps: &DepsMut, info: &MessageInfo, ) -> Result<(), ContractError> { let virtual_stake = self.virtual_stake.load(deps.storage)?; @@ -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, + mut ctx: ExecCtx, validator: String, - ) -> Result { + ) -> Result { self.ensure_authorized(&ctx.deps, &ctx.info)?; let config = self.config.load(ctx.deps.storage)?; @@ -419,9 +434,9 @@ impl ConverterApi for ConverterContract<'_> { /// in the native staking denom. fn distribute_rewards( &self, - mut ctx: ExecCtx, + mut ctx: ExecCtx, payments: Vec, - ) -> Result { + ) -> Result { self.ensure_authorized(&ctx.deps, &ctx.info)?; let config = self.config.load(ctx.deps.storage)?; @@ -459,7 +474,7 @@ impl ConverterApi for ConverterContract<'_> { #[allow(clippy::too_many_arguments)] fn valset_update( &self, - ctx: ExecCtx, + ctx: ExecCtx, additions: Vec, removals: Vec, updated: Vec, @@ -467,7 +482,7 @@ impl ConverterApi for ConverterContract<'_> { unjailed: Vec, tombstoned: Vec, mut slashed: Vec, - ) -> Result { + ) -> Result { self.ensure_authorized(&ctx.deps, &ctx.info)?; // Send over IBC to the Consumer diff --git a/contracts/consumer/converter/src/ibc.rs b/contracts/consumer/converter/src/ibc.rs index d711a2a5..c54a19dd 100644 --- a/contracts/consumer/converter/src/ibc.rs +++ b/contracts/consumer/converter/src/ibc.rs @@ -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"; @@ -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, _env: Env, msg: IbcPacketReceiveMsg, -) -> Result { +) -> Result, ContractError> { let packet: ProviderPacket = from_json(msg.packet.data)?; let contract = ConverterContract::new(); let res = match packet { @@ -280,7 +283,7 @@ pub fn ibc_packet_timeout( } pub(crate) fn make_ibc_packet( - ctx: &mut ExecCtx, + ctx: &mut ExecCtx, packet: ConsumerPacket, ) -> Result { let channel = IBC_CHANNEL.load(ctx.deps.storage)?; diff --git a/contracts/consumer/converter/src/multitest.rs b/contracts/consumer/converter/src/multitest.rs index 883b38c9..8f20b7ad 100644 --- a/contracts/consumer/converter/src/multitest.rs +++ b/contracts/consumer/converter/src/multitest.rs @@ -1,7 +1,8 @@ mod virtual_staking_mock; use cosmwasm_std::{coin, coins, Addr, Decimal, StdError, Uint128, Validator}; -use cw_multi_test::App as MtApp; +use cw_multi_test::no_init; +use cw_multi_test::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; @@ -12,13 +13,15 @@ use virtual_staking_mock::VirtualStakingMock; use crate::contract::sv::mt::CodeId as ConverterCodeId; use crate::contract::sv::mt::ConverterContractProxy; -use crate::contract::ConverterContract; +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"; +pub type MtApp = cw_multi_test::BasicApp; + struct SetupArgs<'a> { owner: &'a str, admin: &'a str, @@ -32,6 +35,10 @@ struct SetupResponse<'a> { virtual_staking: Proxy<'a, MtApp, VirtualStakingMock<'a>>, } +fn new_app() -> App { + App::new(AppBuilder::new_custom().build(no_init)) +} + fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { let SetupArgs { owner, @@ -80,7 +87,7 @@ fn setup<'a>(app: &'a App, args: SetupArgs<'a>) -> SetupResponse<'a> { #[test] fn instantiation() { - let app = App::default(); + let app = new_app(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) let admin = "theman"; @@ -122,7 +129,7 @@ fn instantiation() { #[test] fn ibc_stake_and_unstake() { - let app = App::default(); + let app = new_app(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) let admin = "theman"; @@ -208,7 +215,7 @@ fn ibc_stake_and_unstake() { #[test] fn ibc_stake_and_burn() { - let app = App::default(); + let app = new_app(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) let admin = "theman"; @@ -294,7 +301,7 @@ fn ibc_stake_and_burn() { #[test] fn valset_update_works() { - let app = App::default(); + let app = new_app(); let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract) let admin = "theman"; @@ -365,7 +372,7 @@ fn valset_update_works() { #[test] fn unauthorized() { - let app = App::default(); + let app = new_app(); let SetupResponse { converter, .. } = setup( &app, @@ -415,7 +422,7 @@ fn distribute_rewards_invalid_amount_is_rejected() { let discount = Decimal::percent(10); // 1 OSMO worth of JUNO should give 0.9 OSMO of stake let native_per_foreign = Decimal::percent(40); // 1 JUNO is worth 0.4 OSMO - let app = App::default(); + let app = new_app(); let SetupResponse { price_feed: _, @@ -490,14 +497,14 @@ fn distribute_rewards_invalid_amount_is_rejected() { } #[test] -#[ignore = "unsupported by Sylvia"] +#[ignore = "IBC unsupported by Sylvia"] fn distribute_rewards_valid_amount() { let owner = "sunny"; let admin = "theman"; let discount = Decimal::percent(10); // 1 OSMO worth of JUNO should give 0.9 OSMO of stake let native_per_foreign = Decimal::percent(40); // 1 JUNO is worth 0.4 OSMO - let app = App::default(); + let app = new_app(); let SetupResponse { price_feed: _, diff --git a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs index d2555b86..3faaedf8 100644 --- a/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs +++ b/contracts/consumer/converter/src/multitest/virtual_staking_mock.rs @@ -7,6 +7,8 @@ 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 @@ -43,6 +45,7 @@ pub struct VirtualStakingMock<'a> { #[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 { @@ -52,7 +55,10 @@ impl VirtualStakingMock<'_> { } #[sv::msg(instantiate)] - pub fn instantiate(&self, ctx: InstantiateCtx) -> Result { + pub fn instantiate( + &self, + ctx: InstantiateCtx, + ) -> Result { nonpayable(&ctx.info)?; let denom = ctx.deps.querier.query_bonded_denom()?; let config = Config { @@ -64,7 +70,10 @@ impl VirtualStakingMock<'_> { } #[sv::msg(query)] - fn config(&self, ctx: QueryCtx) -> Result { + fn config( + &self, + ctx: QueryCtx, + ) -> Result { let cfg = self.config.load(ctx.deps.storage)?; let denom = cfg.denom; let converter = cfg.converter.into_string(); @@ -72,7 +81,11 @@ impl VirtualStakingMock<'_> { } #[sv::msg(query)] - fn stake(&self, ctx: QueryCtx, validator: String) -> Result { + fn stake( + &self, + ctx: QueryCtx, + validator: String, + ) -> Result { let stake = self .stake .may_load(ctx.deps.storage, &validator)? @@ -81,7 +94,10 @@ impl VirtualStakingMock<'_> { } #[sv::msg(query)] - fn all_stake(&self, ctx: QueryCtx) -> Result { + fn all_stake( + &self, + ctx: QueryCtx, + ) -> Result { let stakes = self .stake .range(ctx.deps.storage, None, None, cosmwasm_std::Order::Ascending) @@ -108,11 +124,18 @@ pub struct ConfigResponse { 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 { + 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 @@ -136,10 +159,10 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// 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, + ctx: ExecCtx, validator: String, amount: Coin, - ) -> Result { + ) -> 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 @@ -163,10 +186,10 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// 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, + ctx: ExecCtx, validators: Vec, amount: Coin, - ) -> Result { + ) -> Result, Self::Error> { nonpayable(&ctx.info)?; let cfg = self.config.load(ctx.deps.storage)?; // only the converter can call this @@ -223,7 +246,10 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// 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 { + fn handle_epoch( + &self, + _ctx: SudoCtx, + ) -> Result, Self::Error> { unimplemented!() } @@ -236,7 +262,7 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { /// - Permanent removal (i.e. tombstoning) of a validator from the active set. Implies slashing fn handle_valset_update( &self, - _ctx: SudoCtx, + _ctx: SudoCtx, _additions: Option>, _removals: Option>, _updated: Option>, @@ -244,7 +270,7 @@ impl VirtualStakingApi for VirtualStakingMock<'_> { _unjailed: Option>, _tombstoned: Option>, _slashed: Option>, - ) -> Result { + ) -> Result, Self::Error> { unimplemented!() } } diff --git a/contracts/consumer/simple-price-feed/Cargo.toml b/contracts/consumer/simple-price-feed/Cargo.toml index 3122ac0d..b32fe2e4 100644 --- a/contracts/consumer/simple-price-feed/Cargo.toml +++ b/contracts/consumer/simple-price-feed/Cargo.toml @@ -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 = [] [dependencies] mesh-apis = { workspace = true } diff --git a/contracts/consumer/simple-price-feed/src/contract.rs b/contracts/consumer/simple-price-feed/src/contract.rs index 06aedf11..b3a5e2d8 100644 --- a/contracts/consumer/simple-price-feed/src/contract.rs +++ b/contracts/consumer/simple-price-feed/src/contract.rs @@ -14,15 +14,18 @@ use crate::state::Config; pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -#[cfg(not(any(test, feature = "mt")))] -pub type PriceFeedCustomMsg = cosmwasm_std::Empty; -#[cfg(any(test, feature = "mt"))] -pub type PriceFeedCustomMsg = mesh_bindings::VirtualStakeCustomMsg; - -#[cfg(not(any(test, feature = "mt")))] -pub type PriceFeedCustomQuery = cosmwasm_std::Empty; -#[cfg(any(test, feature = "mt"))] -pub type PriceFeedCustomQuery = mesh_bindings::VirtualStakeCustomQuery; +#[cfg(not(feature = "fake-custom"))] +pub mod custom { + pub type PriceFeedMsg = cosmwasm_std::Empty; + pub type PriceFeedQuery = cosmwasm_std::Empty; + pub type Response = cosmwasm_std::Response; +} +#[cfg(feature = "fake-custom")] +pub mod custom { + pub type PriceFeedMsg = mesh_bindings::VirtualStakeCustomMsg; + pub type PriceFeedQuery = mesh_bindings::VirtualStakeCustomQuery; + pub type Response = cosmwasm_std::Response; +} pub struct SimplePriceFeedContract<'a> { pub config: Item<'a, Config>, @@ -31,11 +34,11 @@ pub struct SimplePriceFeedContract<'a> { #[cfg_attr(not(feature = "library"), sylvia::entry_points)] #[contract] #[sv::error(ContractError)] -#[sv::messages(price_feed_api as PriceFeedApi: custom(msg, query))] +#[sv::messages(price_feed_api as PriceFeedApi)] // #[cfg_attr(any(test, feature = "mt"), sv::messages(price_feed_api as PriceFeedApi: custom(msg, query)))] // #[cfg_attr(not(any(test, feature = "mt")), sv::messages(price_feed_api as PriceFeedApi))] /// Workaround for lack of support in communication `Empty` <-> `Custom` Contracts. -#[sv::custom(query=PriceFeedCustomQuery, msg=PriceFeedCustomMsg)] +#[sv::custom(query=custom::PriceFeedQuery, msg=custom::PriceFeedMsg)] impl SimplePriceFeedContract<'_> { pub const fn new() -> Self { Self { @@ -48,10 +51,10 @@ impl SimplePriceFeedContract<'_> { #[sv::msg(instantiate)] pub fn instantiate( &self, - ctx: InstantiateCtx, + ctx: InstantiateCtx, native_per_foreign: Decimal, owner: Option, - ) -> Result, ContractError> { + ) -> Result { nonpayable(&ctx.info)?; let owner = match owner { Some(owner) => ctx.deps.api.addr_validate(&owner)?, @@ -70,9 +73,9 @@ impl SimplePriceFeedContract<'_> { #[sv::msg(exec)] fn update_price( &self, - ctx: ExecCtx, + ctx: ExecCtx, native_per_foreign: Decimal, - ) -> Result, ContractError> { + ) -> Result { nonpayable(&ctx.info)?; let mut config = self.config.load(ctx.deps.storage)?; @@ -90,7 +93,10 @@ impl SimplePriceFeedContract<'_> { } #[sv::msg(query)] - fn config(&self, ctx: QueryCtx) -> Result { + fn config( + &self, + ctx: QueryCtx, + ) -> Result { let config = self.config.load(ctx.deps.storage)?; Ok(ConfigResponse { owner: config.owner.into_string(), @@ -101,10 +107,12 @@ impl SimplePriceFeedContract<'_> { impl PriceFeedApi for SimplePriceFeedContract<'_> { type Error = ContractError; + type ExecC = custom::PriceFeedMsg; + type QueryC = custom::PriceFeedQuery; /// Return the price of the foreign token. That is, how many native tokens /// are needed to buy one foreign token. - fn price(&self, ctx: QueryCtx) -> Result { + fn price(&self, ctx: QueryCtx) -> Result { let config = self.config.load(ctx.deps.storage)?; Ok(PriceResponse { native_per_foreign: config.native_per_foreign, @@ -112,7 +120,10 @@ impl PriceFeedApi for SimplePriceFeedContract<'_> { } /// Nothing needs to be done on the epoch - fn handle_epoch(&self, _ctx: SudoCtx) -> Result { + fn handle_epoch( + &self, + _ctx: SudoCtx, + ) -> Result, Self::Error> { Ok(Response::new()) } } diff --git a/packages/apis/src/converter_api.rs b/packages/apis/src/converter_api.rs index 2a26474d..22fb1dfe 100644 --- a/packages/apis/src/converter_api.rs +++ b/packages/apis/src/converter_api.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Coin, Response, StdError, Uint128, Validator}; +use cosmwasm_std::{Coin, CustomMsg, CustomQuery, Response, StdError, Uint128, Validator}; use sylvia::types::ExecCtx; use sylvia::{interface, schemars}; @@ -9,11 +9,17 @@ use sylvia::{interface, schemars}; #[interface] pub trait ConverterApi { type Error: From; + type ExecC: CustomMsg; + type QueryC: CustomQuery; /// Rewards tokens (in native staking denom) are sent alongside the message, and should be distributed to all /// stakers who staked on this validator. #[sv::msg(exec)] - fn distribute_reward(&self, ctx: ExecCtx, validator: String) -> Result; + fn distribute_reward( + &self, + ctx: ExecCtx, + validator: String, + ) -> Result, Self::Error>; /// This is a batch for of distribute_reward, including the payment for multiple validators. /// This is more efficient than calling distribute_reward multiple times, but also more complex. @@ -23,18 +29,18 @@ pub trait ConverterApi { #[sv::msg(exec)] fn distribute_rewards( &self, - ctx: ExecCtx, + ctx: ExecCtx, payments: Vec, - ) -> Result; + ) -> Result, Self::Error>; /// Valset updates. /// /// TODO: pubkeys need to be part of the Validator struct (requires CosmWasm support). - #[allow(clippy::too_many_arguments)] #[sv::msg(exec)] + #[allow(clippy::too_many_arguments)] fn valset_update( &self, - ctx: ExecCtx, + ctx: ExecCtx, additions: Vec, removals: Vec, updated: Vec, @@ -42,7 +48,7 @@ pub trait ConverterApi { unjailed: Vec, tombstoned: Vec, slashed: Vec, - ) -> Result; + ) -> Result, Self::Error>; } #[cw_serde] diff --git a/packages/apis/src/price_feed_api.rs b/packages/apis/src/price_feed_api.rs index 49723bf1..92679320 100644 --- a/packages/apis/src/price_feed_api.rs +++ b/packages/apis/src/price_feed_api.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Decimal, Response, StdError}; +use cosmwasm_std::{CustomMsg, CustomQuery, Decimal, Response, StdError}; use sylvia::types::{QueryCtx, SudoCtx}; use sylvia::{interface, schemars}; @@ -12,14 +12,19 @@ use sylvia::{interface, schemars}; #[interface] pub trait PriceFeedApi { type Error: From; + type ExecC: CustomMsg; + type QueryC: CustomQuery; /// Return the price of the foreign token. That is, how many native tokens /// are needed to buy one foreign token. #[sv::msg(query)] - fn price(&self, ctx: QueryCtx) -> Result; + fn price(&self, ctx: QueryCtx) -> Result; #[sv::msg(sudo)] - fn handle_epoch(&self, ctx: SudoCtx) -> Result; + fn handle_epoch( + &self, + ctx: SudoCtx, + ) -> Result, Self::Error>; } #[cw_serde] diff --git a/packages/apis/src/virtual_staking_api.rs b/packages/apis/src/virtual_staking_api.rs index beb84742..d820a668 100644 --- a/packages/apis/src/virtual_staking_api.rs +++ b/packages/apis/src/virtual_staking_api.rs @@ -19,7 +19,12 @@ pub trait VirtualStakingApi { /// 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. #[sv::msg(exec)] - fn bond(&self, ctx: ExecCtx, validator: String, amount: Coin) -> Result, Self::Error>; + fn bond( + &self, + ctx: ExecCtx, + validator: String, + amount: Coin, + ) -> Result, Self::Error>; /// 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. @@ -49,7 +54,10 @@ pub trait VirtualStakingApi { /// /// It should also withdraw all pending rewards here, and send them to the converter contract. #[sv::msg(sudo)] - fn handle_epoch(&self, ctx: SudoCtx) -> Result, Self::Error>; + fn handle_epoch( + &self, + ctx: SudoCtx, + ) -> Result, Self::Error>; /// SudoMsg::ValsetUpdate{} should be called every time there's a validator set update: /// - Addition of a new validator to the active validator set.