From 6efb90d049d71d40cf3de96c7c56c312d4647fdf Mon Sep 17 00:00:00 2001 From: vuong177 Date: Thu, 4 Jul 2024 14:01:47 +0700 Subject: [PATCH 01/12] init band oracle price feed --- Cargo.lock | 102 +++++++++-- contracts/consumer/band-price-feed/Cargo.toml | 31 ++++ .../consumer/band-price-feed/src/contract.rs | 161 ++++++++++++++++++ .../consumer/band-price-feed/src/error.rs | 26 +++ contracts/consumer/band-price-feed/src/lib.rs | 6 + .../consumer/band-price-feed/src/main.rs | 3 + contracts/consumer/band-price-feed/src/msg.rs | 57 +++++++ .../band-price-feed/src/price_keeper.rs | 154 +++++++++++++++++ .../consumer/band-price-feed/src/scheduler.rs | 101 +++++++++++ .../consumer/band-price-feed/src/state.rs | 78 +++++++++ 10 files changed, 703 insertions(+), 16 deletions(-) create mode 100644 contracts/consumer/band-price-feed/Cargo.toml create mode 100644 contracts/consumer/band-price-feed/src/contract.rs create mode 100644 contracts/consumer/band-price-feed/src/error.rs create mode 100644 contracts/consumer/band-price-feed/src/lib.rs create mode 100644 contracts/consumer/band-price-feed/src/main.rs create mode 100644 contracts/consumer/band-price-feed/src/msg.rs create mode 100644 contracts/consumer/band-price-feed/src/price_keeper.rs create mode 100644 contracts/consumer/band-price-feed/src/scheduler.rs create mode 100644 contracts/consumer/band-price-feed/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index 0df67f14..02594a41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,28 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "band-price-feed" +version = "0.10.0-alpha.1" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-band", + "cw-multi-test", + "cw-storage-plus", + "cw-utils", + "cw2", + "derivative", + "mesh-apis", + "obi", + "schemars", + "serde", + "sylvia", + "test-case", + "thiserror", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -164,7 +186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6db85d98ac80922aef465e564d5b21fa9cfac5058cb62df7f116c3682337393" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.36", "syn 1.0.109", ] @@ -234,6 +256,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cw-band" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a612aae92ab81a4bb064054c02f58ee125d793223c1c52e09543af06e4602c41" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "obi", +] + [[package]] name = "cw-multi-test" version = "0.20.1" @@ -312,7 +345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.36", "syn 1.0.109", ] @@ -829,6 +862,37 @@ dependencies = [ "autocfg", ] +[[package]] +name = "obi" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7f163302f3e95c2728706fc08b355a1fa34882cacef1612b212aaf34c0d49c4" +dependencies = [ + "obi-derive", +] + +[[package]] +name = "obi-derive" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ea93e903a6bf1f0e657ddaaf8e15da0af60f28bd2757a32871aca7c59df25e" +dependencies = [ + "obi-derive-internal", + "quote 0.3.15", + "syn 1.0.109", +] + +[[package]] +name = "obi-derive-internal" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00664fdffd7558a47a4c106c446104af3e789b740d7740885660c371eb0d09d" +dependencies = [ + "proc-macro2", + "quote 1.0.36", + "syn 1.0.109", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -866,7 +930,7 @@ dependencies = [ "itertools 0.10.5", "proc-macro2", "prost-types", - "quote", + "quote 1.0.36", "syn 1.0.109", ] @@ -898,7 +962,7 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", - "quote", + "quote 1.0.36", "syn 1.0.109", "version_check", ] @@ -910,7 +974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.36", "version_check", ] @@ -952,7 +1016,7 @@ dependencies = [ "anyhow", "itertools 0.10.5", "proc-macro2", - "quote", + "quote 1.0.36", "syn 1.0.109", ] @@ -965,7 +1029,7 @@ dependencies = [ "anyhow", "itertools 0.12.1", "proc-macro2", - "quote", + "quote 1.0.36", "syn 2.0.60", ] @@ -978,6 +1042,12 @@ dependencies = [ "prost 0.11.9", ] +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" + [[package]] name = "quote" version = "1.0.36" @@ -1037,7 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.36", "serde_derive_internals", "syn 2.0.60", ] @@ -1105,7 +1175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.36", "syn 2.0.60", ] @@ -1116,7 +1186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.36", "syn 2.0.60", ] @@ -1217,7 +1287,7 @@ dependencies = [ "proc-macro-crate", "proc-macro-error", "proc-macro2", - "quote", + "quote 1.0.36", "syn 2.0.60", ] @@ -1228,7 +1298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.36", "unicode-ident", ] @@ -1239,7 +1309,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.36", "unicode-ident", ] @@ -1260,7 +1330,7 @@ checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" dependencies = [ "cfg-if", "proc-macro2", - "quote", + "quote 1.0.36", "syn 2.0.60", ] @@ -1271,7 +1341,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.36", "syn 2.0.60", "test-case-core", ] @@ -1292,7 +1362,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.36", "syn 2.0.60", ] diff --git a/contracts/consumer/band-price-feed/Cargo.toml b/contracts/consumer/band-price-feed/Cargo.toml new file mode 100644 index 00000000..14c0ccff --- /dev/null +++ b/contracts/consumer/band-price-feed/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "band-price-feed" +edition.workspace = true +version.workspace = true +license.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + +[dependencies] +mesh-apis = { workspace = true } + +sylvia = { workspace = true } +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +cw-storage-plus = { workspace = true } +cw2 = { workspace = true } +cw-utils = { workspace = true } + +schemars = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } +obi = "0.0.2" +cw-band = "0.1.1" + +[dev-dependencies] +cw-multi-test = { workspace = true } +test-case = { workspace = true } +derivative = { workspace = true } +anyhow = { workspace = true } diff --git a/contracts/consumer/band-price-feed/src/contract.rs b/contracts/consumer/band-price-feed/src/contract.rs new file mode 100644 index 00000000..e6a268a4 --- /dev/null +++ b/contracts/consumer/band-price-feed/src/contract.rs @@ -0,0 +1,161 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + to_json_binary, Binary, Deps, DepsMut, Empty, Env, IbcMsg, IbcTimeout, MessageInfo, Response, IbcChannel, + StdResult, Uint256, Uint64, +}; +use cw2::set_contract_version; +use cw_storage_plus::Item; + +use crate::error::ContractError; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::{Config, Rate, ReferenceData, BAND_CONFIG, ENDPOINT, RATES}; +use obi::enc::OBIEncode; +use cw_band::{Input, OracleRequestPacketData}; +use crate::state::TradingPair; +use crate::price_keeper::PriceKeeper; +use crate::scheduler::{Action, Scheduler}; + +const E9: Uint64 = Uint64::new(1_000_000_000u64); +const E18: Uint256 = Uint256::from_u128(1_000_000_000_000_000_000u128); + +// Version info for migration +const CONTRACT_NAME: &str = "crates.io:band-ibc-price-feed"; +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +pub struct RemotePriceFeedContract { + pub channel: Item<'static, IbcChannel>, + pub trading_pair: Item<'static, TradingPair>, + pub price_keeper: PriceKeeper, + pub scheduler: Scheduler>, +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + BAND_CONFIG.save( + deps.storage, + &Config { + client_id: msg.client_id, + oracle_script_id: msg.oracle_script_id, + ask_count: msg.ask_count, + min_count: msg.min_count, + fee_limit: msg.fee_limit, + prepare_gas: msg.prepare_gas, + execute_gas: msg.execute_gas, + minimum_sources: msg.minimum_sources, + }, + )?; + + Ok(Response::new().add_attribute("method", "instantiate")) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + _info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::Request { symbols } => try_request(deps, env, symbols), + } +} + +// TODO: Possible features +// - Request fee + Bounty logic to prevent request spam and incentivize relayer +// - Whitelist who can call update price +pub fn try_request( + deps: DepsMut, + env: Env, + symbols: Vec, +) -> Result { + let endpoint = ENDPOINT.load(deps.storage)?; + let config = BAND_CONFIG.load(deps.storage)?; + + let raw_calldata = Input { + symbols, + minimum_sources: config.minimum_sources, + } + .try_to_vec() + .map(Binary) + .map_err(|err| ContractError::CustomError { + val: err.to_string(), + })?; + + let packet = OracleRequestPacketData { + client_id: config.client_id, + oracle_script_id: config.oracle_script_id, + calldata: raw_calldata, + ask_count: config.ask_count, + min_count: config.min_count, + prepare_gas: config.prepare_gas, + execute_gas: config.execute_gas, + fee_limit: config.fee_limit, + }; + + Ok(Response::new().add_message(IbcMsg::SendPacket { + channel_id: endpoint.channel_id, + data: to_json_binary(&packet)?, + timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(60)), + })) +} + +/// this is a no-op +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> StdResult { + Ok(Response::default()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::GetRate { symbol } => to_json_binary(&query_rate(deps, &symbol)?), + QueryMsg::GetReferenceData { symbol_pair } => { + to_json_binary(&query_reference_data(deps, &symbol_pair)?) + } + QueryMsg::GetReferenceDataBulk { symbol_pairs } => { + to_json_binary(&query_reference_data_bulk(deps, &symbol_pairs)?) + } + } +} + +fn query_rate(deps: Deps, symbol: &str) -> StdResult { + if symbol == "USD" { + Ok(Rate::new(E9, Uint64::MAX, Uint64::new(0))) + } else { + RATES.load(deps.storage, symbol) + } +} + +fn query_reference_data(deps: Deps, symbol_pair: &(String, String)) -> StdResult { + let base = query_rate(deps, &symbol_pair.0)?; + let quote = query_rate(deps, &symbol_pair.1)?; + + Ok(ReferenceData::new( + Uint256::from(base.rate) + .checked_mul(E18)? + .checked_div(Uint256::from(quote.rate))?, + base.resolve_time, + quote.resolve_time, + )) +} + +fn query_reference_data_bulk( + deps: Deps, + symbol_pairs: &[(String, String)], +) -> StdResult> { + symbol_pairs + .iter() + .map(|pair| query_reference_data(deps, pair)) + .collect() +} +// TODO: Writing test +#[cfg(test)] +mod tests {} \ No newline at end of file diff --git a/contracts/consumer/band-price-feed/src/error.rs b/contracts/consumer/band-price-feed/src/error.rs new file mode 100644 index 00000000..db8c61e1 --- /dev/null +++ b/contracts/consumer/band-price-feed/src/error.rs @@ -0,0 +1,26 @@ +use cosmwasm_std::StdError; +use thiserror::Error; + +/// Never is a placeholder to ensure we don't return any errors +#[derive(Error, Debug)] +pub enum Never {} + +#[derive(Error, Debug, PartialEq)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("Request didn't suceess")] + RequestNotSuccess {}, + + #[error("Only supports channel with ibc version bandchain-1, got {version}")] + InvalidIbcVersion { version: String }, + + #[error("Only supports unordered channel")] + OnlyUnorderedChannel {}, + + #[error("Custom Error val: {val:?}")] + CustomError { val: String }, + // Add any other custom errors you like here. + // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. +} diff --git a/contracts/consumer/band-price-feed/src/lib.rs b/contracts/consumer/band-price-feed/src/lib.rs new file mode 100644 index 00000000..fb31a56a --- /dev/null +++ b/contracts/consumer/band-price-feed/src/lib.rs @@ -0,0 +1,6 @@ +pub mod contract; +pub mod error; +pub mod msg; +pub mod state; +pub mod price_keeper; +pub mod scheduler; \ No newline at end of file diff --git a/contracts/consumer/band-price-feed/src/main.rs b/contracts/consumer/band-price-feed/src/main.rs new file mode 100644 index 00000000..e7a11a96 --- /dev/null +++ b/contracts/consumer/band-price-feed/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/contracts/consumer/band-price-feed/src/msg.rs b/contracts/consumer/band-price-feed/src/msg.rs new file mode 100644 index 00000000..7dc4bf31 --- /dev/null +++ b/contracts/consumer/band-price-feed/src/msg.rs @@ -0,0 +1,57 @@ +use crate::state::{Rate, ReferenceData}; +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{Coin, Uint64}; + +#[cw_serde] +pub struct InstantiateMsg { + // A unique ID for the oracle request + pub client_id: String, + // The oracle script ID to query + pub oracle_script_id: Uint64, + // The number of validators that are requested to respond + pub ask_count: Uint64, + // The minimum number of validators that need to respond + pub min_count: Uint64, + // The maximum amount of band in uband to be paid to the data source providers + // e.g. vec![Coin::new(100, "uband")] + pub fee_limit: Vec, + // Amount of gas to pay to prepare raw requests + pub prepare_gas: Uint64, + // Amount of gas reserved for execution + pub execute_gas: Uint64, + // Minimum number of sources required to return a successful response + pub minimum_sources: u8, + // The minimum amount that sender need to send to create a new oracle request + pub fee: Vec, +} + +#[cw_serde] +pub enum ExecuteMsg { + Request { symbols: Vec }, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + #[returns(Rate)] + // Returns the RefData of a given symbol + GetRate { + // Symbol to query + symbol: String, + }, + #[returns(ReferenceData)] + // Returns the ReferenceData of a given asset pairing + GetReferenceData { + // Symbol pair to query where: + // symbol_pair := (base_symbol, quote_symbol) + // e.g. BTC/USD ≡ ("BTC", "USD") + symbol_pair: (String, String), + }, + #[returns(Vec)] + // Returns the ReferenceDatas of the given asset pairings + GetReferenceDataBulk { + // Vector of Symbol pair to query + // e.g. ≡ <("BTC", "USD"), ("ETH", "USD"), ("BAND", "BTC")> + symbol_pairs: Vec<(String, String)>, + }, +} diff --git a/contracts/consumer/band-price-feed/src/price_keeper.rs b/contracts/consumer/band-price-feed/src/price_keeper.rs new file mode 100644 index 00000000..50b5d2de --- /dev/null +++ b/contracts/consumer/band-price-feed/src/price_keeper.rs @@ -0,0 +1,154 @@ +use cosmwasm_std::{Decimal, Deps, DepsMut, Env, Timestamp}; +use cw_storage_plus::Item; + +use crate::state::PriceInfo; + +/// A component that keeps track of the latest price info. +pub struct PriceKeeper { + pub price_info: Item<'static, PriceInfo>, + pub price_info_ttl_in_secs: Item<'static, u64>, +} + +impl PriceKeeper { + pub const fn new() -> Self { + Self { + price_info: Item::new("price"), + price_info_ttl_in_secs: Item::new("price_ttl"), + } + } + + pub fn init( + &self, + deps: &mut DepsMut, + price_info_ttl_in_secs: u64, + ) -> Result<(), PriceKeeperError> { + self.price_info_ttl_in_secs + .save(deps.storage, &price_info_ttl_in_secs)?; + Ok(()) + } + + pub fn update( + &self, + deps: DepsMut, + time: Timestamp, + twap: Decimal, + ) -> Result<(), PriceKeeperError> { + let old = self.price_info.may_load(deps.storage)?; + match old { + Some(old) if old.time > time => { + // don't update if we have newer price info stored + } + _ => self.price_info.save( + deps.storage, + &PriceInfo { + time, + native_per_foreign: twap, + }, + )?, + } + + Ok(()) + } + + pub fn price(&self, deps: Deps, env: &Env) -> Result { + let price_info_ttl = self.price_info_ttl_in_secs.load(deps.storage)?; + let price_info = self + .price_info + .may_load(deps.storage)? + .ok_or(PriceKeeperError::NoPriceData)?; + + if env.block.time.minus_seconds(price_info_ttl) < price_info.time { + Ok(price_info.native_per_foreign) + } else { + Err(PriceKeeperError::OutdatedPriceData) + } + } +} + +#[derive(thiserror::Error, Debug, PartialEq)] +pub enum PriceKeeperError { + #[error("StdError: {0}")] + StdError(#[from] cosmwasm_std::StdError), + + #[error("Price data is outdated")] + OutdatedPriceData, + + #[error("No price data available")] + NoPriceData, +} + +#[cfg(test)] +mod tests { + use super::*; + + use cosmwasm_std::testing::{mock_dependencies, mock_env}; + + #[test] + fn happy_path() { + let mut deps = mock_dependencies(); + let mut env = mock_env(); + let keeper = PriceKeeper::new(); + + keeper.init(&mut deps.as_mut(), 600).unwrap(); + keeper + .update(deps.as_mut(), env.block.time, Decimal::one()) + .unwrap(); + + let price = keeper.price(deps.as_ref(), &env).unwrap(); + assert_eq!(price, Decimal::one()); + + env.block.time = env.block.time.plus_seconds(559); + let price = keeper.price(deps.as_ref(), &env).unwrap(); + assert_eq!(price, Decimal::one()); + } + + #[test] + fn no_initial_price_info() { + let mut deps = mock_dependencies(); + let env = mock_env(); + let keeper = PriceKeeper::new(); + + keeper.init(&mut deps.as_mut(), 600).unwrap(); + + let err = keeper.price(deps.as_ref(), &env).unwrap_err(); + assert_eq!(err, PriceKeeperError::NoPriceData); + } + + #[test] + fn outdated_price_info() { + let mut deps = mock_dependencies(); + let mut env = mock_env(); + let keeper = PriceKeeper::new(); + + keeper.init(&mut deps.as_mut(), 600).unwrap(); + keeper + .update(deps.as_mut(), env.block.time, Decimal::one()) + .unwrap(); + + env.block.time = env.block.time.plus_seconds(601); + let err = keeper.price(deps.as_ref(), &env).unwrap_err(); + assert_eq!(err, PriceKeeperError::OutdatedPriceData); + } + + #[test] + fn update_with_older_price_info_is_ignored() { + let mut deps = mock_dependencies(); + let env = mock_env(); + let keeper = PriceKeeper::new(); + + keeper.init(&mut deps.as_mut(), 600).unwrap(); + keeper + .update(deps.as_mut(), env.block.time, Decimal::one()) + .unwrap(); + keeper + .update( + deps.as_mut(), + env.block.time.minus_seconds(1), + Decimal::percent(50), + ) + .unwrap(); + + let price = keeper.price(deps.as_ref(), &env).unwrap(); + assert_eq!(price, Decimal::one()); + } +} diff --git a/contracts/consumer/band-price-feed/src/scheduler.rs b/contracts/consumer/band-price-feed/src/scheduler.rs new file mode 100644 index 00000000..ee47aabe --- /dev/null +++ b/contracts/consumer/band-price-feed/src/scheduler.rs @@ -0,0 +1,101 @@ +use cosmwasm_std::{DepsMut, Env, Response, Timestamp}; +use cw_storage_plus::Item; + +use crate::error::ContractError; + +pub trait Action: Fn(DepsMut, &Env) -> Result {} +impl Action for F where F: Fn(DepsMut, &Env) -> Result {} + +/// A component that schedules a single action to be executed regularly, +/// as in "every epoch". It relies on a trigger being called rather rapidly (every block?). +pub struct Scheduler { + last_epoch: Item<'static, Timestamp>, + epoch_in_secs: Item<'static, u64>, + action: A, +} + +impl Scheduler +where + A: Action, +{ + pub const fn new(action: A) -> Self { + Self { + last_epoch: Item::new("last_epoch"), + epoch_in_secs: Item::new("epoch"), + action, + } + } + + pub fn init(&self, deps: &mut DepsMut, epoch_in_secs: u64) -> Result<(), ContractError> { + self.last_epoch + .save(deps.storage, &Timestamp::from_seconds(0))?; + self.epoch_in_secs.save(deps.storage, &epoch_in_secs)?; + Ok(()) + } + + pub fn trigger(&self, deps: DepsMut, env: &Env) -> Result { + let last_epoch = self.last_epoch.load(deps.storage)?; + let epoch_in_secs = self.epoch_in_secs.load(deps.storage)?; + let secs_since_last_epoch = env.block.time.seconds() - last_epoch.seconds(); + if secs_since_last_epoch >= epoch_in_secs { + self.last_epoch.save(deps.storage, &env.block.time)?; + (self.action)(deps, env) + } else { + Ok(Response::new()) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::{ + testing::{mock_dependencies, mock_env}, + Binary, + }; + + #[test] + fn scheduler_first_epoch_always_fires() { + let scheduler = Scheduler::new(|_, _| Ok(Response::new().set_data(Binary::from(b"foo")))); + let mut deps = mock_dependencies(); + let env = mock_env(); + + scheduler.init(&mut deps.as_mut(), 111111).unwrap(); + assert!(scheduler + .trigger(deps.as_mut(), &env) + .unwrap() + .data + .is_some()); + } + + #[test] + fn scheduler() { + let scheduler = Scheduler::new(|_, _| Ok(Response::new().set_data(Binary::from(b"foo")))); + let mut deps = mock_dependencies(); + let mut env = mock_env(); + + scheduler.init(&mut deps.as_mut(), 10).unwrap(); + + #[track_caller] + fn assert_fired(s: &Scheduler, deps: DepsMut, env: &Env) { + assert!(s.trigger(deps, env).unwrap().data.is_some()) + } + + #[track_caller] + fn assert_noop(s: &Scheduler, deps: DepsMut, env: &Env) { + assert_eq!(s.trigger(deps, env).unwrap(), Response::new()) + } + + assert_fired(&scheduler, deps.as_mut(), &env); + + env.block.time = env.block.time.plus_seconds(5); + assert_noop(&scheduler, deps.as_mut(), &env); + env.block.time = env.block.time.plus_seconds(5); + assert_fired(&scheduler, deps.as_mut(), &env); + env.block.time = env.block.time.plus_seconds(5); + assert_noop(&scheduler, deps.as_mut(), &env); + assert_noop(&scheduler, deps.as_mut(), &env); + env.block.time = env.block.time.plus_seconds(5); + assert_fired(&scheduler, deps.as_mut(), &env); + } +} diff --git a/contracts/consumer/band-price-feed/src/state.rs b/contracts/consumer/band-price-feed/src/state.rs new file mode 100644 index 00000000..6ce189ca --- /dev/null +++ b/contracts/consumer/band-price-feed/src/state.rs @@ -0,0 +1,78 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::IbcEndpoint; +use cosmwasm_std::{Coin, Uint256, Uint64}; +use cw_storage_plus::{Item, Map}; +use cosmwasm_std::{Decimal, Timestamp}; + +#[cw_serde] +pub struct Config { + pub client_id: String, + pub oracle_script_id: Uint64, + pub ask_count: Uint64, + pub min_count: Uint64, + pub fee_limit: Vec, + pub prepare_gas: Uint64, + pub execute_gas: Uint64, + pub minimum_sources: u8, +} + +#[cw_serde] +pub struct Rate { + // Rate of an asset relative to USD + pub rate: Uint64, + // The resolve time of the request ID + pub resolve_time: Uint64, + // The request ID where the rate was derived from + pub request_id: Uint64, +} + +impl Rate { + pub fn new(rate: Uint64, resolve_time: Uint64, request_id: Uint64) -> Self { + Rate { + rate, + resolve_time, + request_id, + } + } +} + +pub const RATES: Map<&str, Rate> = Map::new("rates"); + +pub const ENDPOINT: Item = Item::new("endpoint"); + +pub const BAND_CONFIG: Item = Item::new("config"); + +#[cw_serde] +pub struct ReferenceData { + // Pair rate e.g. rate of BTC/USD + pub rate: Uint256, + // Unix time of when the base asset was last updated. e.g. Last update time of BTC in Unix time + pub last_updated_base: Uint64, + // Unix time of when the quote asset was last updated. e.g. Last update time of USD in Unix time + pub last_updated_quote: Uint64, +} + +impl ReferenceData { + pub fn new(rate: Uint256, last_updated_base: Uint64, last_updated_quote: Uint64) -> Self { + ReferenceData { + rate, + last_updated_base, + last_updated_quote, + } + } +} + + + +#[cw_serde] +pub struct TradingPair { + pub pool_id: u64, + pub base_asset: String, + pub quote_asset: String, +} + +#[cw_serde] +pub struct PriceInfo { + pub time: Timestamp, + pub native_per_foreign: Decimal, +} From a9b8ac4073e1245ee4080c136c925d0036adbb7e Mon Sep 17 00:00:00 2001 From: Trinity Date: Wed, 10 Jul 2024 15:20:42 +0700 Subject: [PATCH 02/12] rename remote-price-feeder to osmosis-price-feeder and move scheduler to separate folder --- Cargo.lock | 70 +++++++++++-------- Cargo.toml | 4 ++ codegen/codegen.js | 8 ++- .../.cargo/config | 0 .../Cargo.toml | 3 +- .../README.md | 0 .../src/bin/schema.rs | 2 +- .../src/contract.rs | 4 +- .../src/error.rs | 0 .../src/ibc.rs | 0 .../src/lib.rs | 1 - .../src/msg.rs | 0 .../src/price_keeper.rs | 0 .../src/state.rs | 0 contracts/osmosis-price-provider/Cargo.toml | 8 +-- packages/scheduler/Cargo.toml | 9 +++ packages/scheduler/src/lib.rs | 3 + .../scheduler}/src/scheduler.rs | 43 ++++++++---- 18 files changed, 100 insertions(+), 55 deletions(-) rename contracts/consumer/{remote-price-feed => osmosis-price-feed}/.cargo/config (100%) rename contracts/consumer/{remote-price-feed => osmosis-price-feed}/Cargo.toml (94%) rename contracts/consumer/{remote-price-feed => osmosis-price-feed}/README.md (100%) rename contracts/consumer/{remote-price-feed => osmosis-price-feed}/src/bin/schema.rs (68%) rename contracts/consumer/{remote-price-feed => osmosis-price-feed}/src/contract.rs (97%) rename contracts/consumer/{remote-price-feed => osmosis-price-feed}/src/error.rs (100%) rename contracts/consumer/{remote-price-feed => osmosis-price-feed}/src/ibc.rs (100%) rename contracts/consumer/{remote-price-feed => osmosis-price-feed}/src/lib.rs (83%) rename contracts/consumer/{remote-price-feed => osmosis-price-feed}/src/msg.rs (100%) rename contracts/consumer/{remote-price-feed => osmosis-price-feed}/src/price_keeper.rs (100%) rename contracts/consumer/{remote-price-feed => osmosis-price-feed}/src/state.rs (100%) create mode 100644 packages/scheduler/Cargo.toml create mode 100644 packages/scheduler/src/lib.rs rename {contracts/consumer/remote-price-feed => packages/scheduler}/src/scheduler.rs (68%) diff --git a/Cargo.lock b/Cargo.lock index 02594a41..d9230d98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,28 +25,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "band-price-feed" -version = "0.10.0-alpha.1" -dependencies = [ - "anyhow", - "cosmwasm-schema", - "cosmwasm-std", - "cw-band", - "cw-multi-test", - "cw-storage-plus", - "cw-utils", - "cw2", - "derivative", - "mesh-apis", - "obi", - "schemars", - "serde", - "sylvia", - "test-case", - "thiserror", -] - [[package]] name = "base16ct" version = "0.2.0" @@ -615,6 +593,29 @@ dependencies = [ "thiserror", ] +[[package]] +name = "mesh-band-price-feed" +version = "0.10.0-alpha.1" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-band", + "cw-multi-test", + "cw-storage-plus", + "cw-utils", + "cw2", + "derivative", + "mesh-apis", + "mesh-scheduler", + "obi", + "schemars", + "serde", + "sylvia", + "test-case", + "thiserror", +] + [[package]] name = "mesh-bindings" version = "0.10.0-alpha.1" @@ -720,43 +721,52 @@ dependencies = [ ] [[package]] -name = "mesh-osmosis-price-provider" +name = "mesh-osmosis-price-feed" version = "0.10.0-alpha.1" dependencies = [ + "anyhow", "cosmwasm-schema", "cosmwasm-std", + "cw-multi-test", "cw-storage-plus", "cw-utils", "cw2", + "derivative", "mesh-apis", - "mesh-bindings", - "osmosis-std", + "mesh-scheduler", "schemars", "serde", "sylvia", + "test-case", "thiserror", ] [[package]] -name = "mesh-remote-price-feed" +name = "mesh-osmosis-price-provider" version = "0.10.0-alpha.1" dependencies = [ - "anyhow", "cosmwasm-schema", "cosmwasm-std", - "cw-multi-test", "cw-storage-plus", "cw-utils", "cw2", - "derivative", "mesh-apis", + "mesh-bindings", + "osmosis-std", "schemars", "serde", "sylvia", - "test-case", "thiserror", ] +[[package]] +name = "mesh-scheduler" +version = "0.10.0-alpha.1" +dependencies = [ + "cosmwasm-std", + "cw-storage-plus", +] + [[package]] name = "mesh-simple-price-feed" version = "0.10.0-alpha.1" diff --git a/Cargo.toml b/Cargo.toml index 3937a620..23072ec2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ repository = "https://github.com/osmosis-labs/mesh-security" mesh-apis = { path = "./packages/apis" } mesh-bindings = { path = "./packages/bindings" } mesh-burn = { path = "./packages/burn" } +mesh-scheduler = { path = "./packages/scheduler" } mesh-sync = { path = "./packages/sync" } mesh-virtual-staking-mock = { path = "./packages/virtual-staking-mock" } @@ -43,6 +44,9 @@ thiserror = "1.0.59" semver = "1.0.22" itertools = "0.12.1" +obi = "0.0.2" +cw-band = "0.1.1" + # dev deps anyhow = "1" cw-multi-test = "0.20" diff --git a/codegen/codegen.js b/codegen/codegen.js index 0c0d03d7..097a98d8 100644 --- a/codegen/codegen.js +++ b/codegen/codegen.js @@ -23,8 +23,12 @@ codegen({ dir: './contracts/consumer/converter/schema' }, { - name: 'RemotePriceFeed', - dir: './contracts/consumer/remote-price-feed/schema' + name: 'OsmosisPriceFeed', + dir: './contracts/consumer/osmosis-price-feed/schema' + }, + { + name: 'BandPriceFeed', + dir: './contracts/consumer/band-price-feed/schema' }, { name: 'SimplePriceFeed', diff --git a/contracts/consumer/remote-price-feed/.cargo/config b/contracts/consumer/osmosis-price-feed/.cargo/config similarity index 100% rename from contracts/consumer/remote-price-feed/.cargo/config rename to contracts/consumer/osmosis-price-feed/.cargo/config diff --git a/contracts/consumer/remote-price-feed/Cargo.toml b/contracts/consumer/osmosis-price-feed/Cargo.toml similarity index 94% rename from contracts/consumer/remote-price-feed/Cargo.toml rename to contracts/consumer/osmosis-price-feed/Cargo.toml index 412217df..71efe754 100644 --- a/contracts/consumer/remote-price-feed/Cargo.toml +++ b/contracts/consumer/osmosis-price-feed/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "mesh-remote-price-feed" +name = "mesh-osmosis-price-feed" description = "Returns exchange rates of assets synchronized with an Osmosis price provider" version = { workspace = true } edition = { workspace = true } @@ -19,6 +19,7 @@ mt = ["library", "sylvia/mt"] [dependencies] mesh-apis = { workspace = true } +mesh-scheduler = { workspace = true } sylvia = { workspace = true } cosmwasm-schema = { workspace = true } diff --git a/contracts/consumer/remote-price-feed/README.md b/contracts/consumer/osmosis-price-feed/README.md similarity index 100% rename from contracts/consumer/remote-price-feed/README.md rename to contracts/consumer/osmosis-price-feed/README.md diff --git a/contracts/consumer/remote-price-feed/src/bin/schema.rs b/contracts/consumer/osmosis-price-feed/src/bin/schema.rs similarity index 68% rename from contracts/consumer/remote-price-feed/src/bin/schema.rs rename to contracts/consumer/osmosis-price-feed/src/bin/schema.rs index 830716f4..0ed55e35 100644 --- a/contracts/consumer/remote-price-feed/src/bin/schema.rs +++ b/contracts/consumer/osmosis-price-feed/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use mesh_remote_price_feed::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use mesh_osmosis_price_feed::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/contracts/consumer/remote-price-feed/src/contract.rs b/contracts/consumer/osmosis-price-feed/src/contract.rs similarity index 97% rename from contracts/consumer/remote-price-feed/src/contract.rs rename to contracts/consumer/osmosis-price-feed/src/contract.rs index 745b1ea4..826358aa 100644 --- a/contracts/consumer/remote-price-feed/src/contract.rs +++ b/contracts/consumer/osmosis-price-feed/src/contract.rs @@ -11,7 +11,7 @@ use crate::error::ContractError; use crate::ibc::{make_ibc_packet, AUTH_ENDPOINT}; use crate::msg::AuthorizedEndpoint; use crate::price_keeper::PriceKeeper; -use crate::scheduler::{Action, Scheduler}; +use mesh_scheduler::{Action, Scheduler}; use crate::state::TradingPair; pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); @@ -21,7 +21,7 @@ pub struct RemotePriceFeedContract { pub channel: Item<'static, IbcChannel>, pub trading_pair: Item<'static, TradingPair>, pub price_keeper: PriceKeeper, - pub scheduler: Scheduler>, + pub scheduler: Scheduler>, ContractError>, } impl Default for RemotePriceFeedContract { diff --git a/contracts/consumer/remote-price-feed/src/error.rs b/contracts/consumer/osmosis-price-feed/src/error.rs similarity index 100% rename from contracts/consumer/remote-price-feed/src/error.rs rename to contracts/consumer/osmosis-price-feed/src/error.rs diff --git a/contracts/consumer/remote-price-feed/src/ibc.rs b/contracts/consumer/osmosis-price-feed/src/ibc.rs similarity index 100% rename from contracts/consumer/remote-price-feed/src/ibc.rs rename to contracts/consumer/osmosis-price-feed/src/ibc.rs diff --git a/contracts/consumer/remote-price-feed/src/lib.rs b/contracts/consumer/osmosis-price-feed/src/lib.rs similarity index 83% rename from contracts/consumer/remote-price-feed/src/lib.rs rename to contracts/consumer/osmosis-price-feed/src/lib.rs index 863d5898..e29f6f83 100644 --- a/contracts/consumer/remote-price-feed/src/lib.rs +++ b/contracts/consumer/osmosis-price-feed/src/lib.rs @@ -3,5 +3,4 @@ pub mod error; pub mod ibc; pub mod msg; pub mod price_keeper; -pub mod scheduler; pub mod state; diff --git a/contracts/consumer/remote-price-feed/src/msg.rs b/contracts/consumer/osmosis-price-feed/src/msg.rs similarity index 100% rename from contracts/consumer/remote-price-feed/src/msg.rs rename to contracts/consumer/osmosis-price-feed/src/msg.rs diff --git a/contracts/consumer/remote-price-feed/src/price_keeper.rs b/contracts/consumer/osmosis-price-feed/src/price_keeper.rs similarity index 100% rename from contracts/consumer/remote-price-feed/src/price_keeper.rs rename to contracts/consumer/osmosis-price-feed/src/price_keeper.rs diff --git a/contracts/consumer/remote-price-feed/src/state.rs b/contracts/consumer/osmosis-price-feed/src/state.rs similarity index 100% rename from contracts/consumer/remote-price-feed/src/state.rs rename to contracts/consumer/osmosis-price-feed/src/state.rs diff --git a/contracts/osmosis-price-provider/Cargo.toml b/contracts/osmosis-price-provider/Cargo.toml index d407fe89..b22530ad 100644 --- a/contracts/osmosis-price-provider/Cargo.toml +++ b/contracts/osmosis-price-provider/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "mesh-osmosis-price-provider" -edition.workspace = true -version.workspace = true -license.workspace = true -repository.workspace = true +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +repository = { workspace = true } [lib] crate-type = ["cdylib", "rlib"] diff --git a/packages/scheduler/Cargo.toml b/packages/scheduler/Cargo.toml new file mode 100644 index 00000000..9b60fbc8 --- /dev/null +++ b/packages/scheduler/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "mesh-scheduler" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } + +[dependencies] +cosmwasm-std = { workspace = true } +cw-storage-plus = { workspace = true } \ No newline at end of file diff --git a/packages/scheduler/src/lib.rs b/packages/scheduler/src/lib.rs new file mode 100644 index 00000000..d57bf830 --- /dev/null +++ b/packages/scheduler/src/lib.rs @@ -0,0 +1,3 @@ +mod scheduler; + +pub use scheduler::{Action, Scheduler}; \ No newline at end of file diff --git a/contracts/consumer/remote-price-feed/src/scheduler.rs b/packages/scheduler/src/scheduler.rs similarity index 68% rename from contracts/consumer/remote-price-feed/src/scheduler.rs rename to packages/scheduler/src/scheduler.rs index ee47aabe..9d810326 100644 --- a/contracts/consumer/remote-price-feed/src/scheduler.rs +++ b/packages/scheduler/src/scheduler.rs @@ -1,39 +1,42 @@ -use cosmwasm_std::{DepsMut, Env, Response, Timestamp}; -use cw_storage_plus::Item; +use std::marker::PhantomData; -use crate::error::ContractError; +use cosmwasm_std::{DepsMut, Env, Response, StdError, Timestamp}; +use cw_storage_plus::Item; -pub trait Action: Fn(DepsMut, &Env) -> Result {} -impl Action for F where F: Fn(DepsMut, &Env) -> Result {} +pub trait Action: Fn(DepsMut, &Env) -> Result {} +impl Action for F where F: Fn(DepsMut, &Env) -> Result {} /// A component that schedules a single action to be executed regularly, /// as in "every epoch". It relies on a trigger being called rather rapidly (every block?). -pub struct Scheduler { +pub struct Scheduler { last_epoch: Item<'static, Timestamp>, epoch_in_secs: Item<'static, u64>, action: A, + _phantom_data: PhantomData, // Add a PhantomData to mark the error type } -impl Scheduler +impl Scheduler where - A: Action, + A: Action, + E: From, { pub const fn new(action: A) -> Self { Self { last_epoch: Item::new("last_epoch"), epoch_in_secs: Item::new("epoch"), action, + _phantom_data: PhantomData, // initialize PhantomData } } - pub fn init(&self, deps: &mut DepsMut, epoch_in_secs: u64) -> Result<(), ContractError> { + pub fn init(&self, deps: &mut DepsMut, epoch_in_secs: u64) -> Result<(), E> { self.last_epoch .save(deps.storage, &Timestamp::from_seconds(0))?; self.epoch_in_secs.save(deps.storage, &epoch_in_secs)?; Ok(()) } - pub fn trigger(&self, deps: DepsMut, env: &Env) -> Result { + pub fn trigger(&self, deps: DepsMut, env: &Env) -> Result { let last_epoch = self.last_epoch.load(deps.storage)?; let epoch_in_secs = self.epoch_in_secs.load(deps.storage)?; let secs_since_last_epoch = env.block.time.seconds() - last_epoch.seconds(); @@ -48,15 +51,19 @@ where #[cfg(test)] mod tests { + use cosmwasm_std::StdError; + use super::*; use cosmwasm_std::{ testing::{mock_dependencies, mock_env}, Binary, }; + type TestScheduler = Scheduler>, StdError>; + #[test] fn scheduler_first_epoch_always_fires() { - let scheduler = Scheduler::new(|_, _| Ok(Response::new().set_data(Binary::from(b"foo")))); + let scheduler = TestScheduler::new(Box::new(|_, _| Ok(Response::new().set_data(Binary::from(b"foo"))))); let mut deps = mock_dependencies(); let env = mock_env(); @@ -70,19 +77,27 @@ mod tests { #[test] fn scheduler() { - let scheduler = Scheduler::new(|_, _| Ok(Response::new().set_data(Binary::from(b"foo")))); + let scheduler = TestScheduler::new(Box::new(|_, _| Ok(Response::new().set_data(Binary::from(b"foo"))))); let mut deps = mock_dependencies(); let mut env = mock_env(); scheduler.init(&mut deps.as_mut(), 10).unwrap(); #[track_caller] - fn assert_fired(s: &Scheduler, deps: DepsMut, env: &Env) { + fn assert_fired(s: &Scheduler, deps: DepsMut, env: &Env) + where + A: Action, + E: std::fmt::Debug + From, + { assert!(s.trigger(deps, env).unwrap().data.is_some()) } #[track_caller] - fn assert_noop(s: &Scheduler, deps: DepsMut, env: &Env) { + fn assert_noop(s: &Scheduler, deps: DepsMut, env: &Env) + where + A: Action, + E: std::fmt::Debug + From, + { assert_eq!(s.trigger(deps, env).unwrap(), Response::new()) } From f3da48493a0e3a43c3f9489d0ee2c616f5cc4d7d Mon Sep 17 00:00:00 2001 From: Trinity Date: Wed, 10 Jul 2024 22:23:48 +0700 Subject: [PATCH 03/12] Make Band price feeder works with PriceFeedApi --- contracts/consumer/band-price-feed/Cargo.toml | 30 ++- .../band-price-feed/src/bin/schema.rs | 12 + .../consumer/band-price-feed/src/contract.rs | 220 +++++++++--------- .../consumer/band-price-feed/src/error.rs | 42 +++- contracts/consumer/band-price-feed/src/ibc.rs | 184 +++++++++++++++ contracts/consumer/band-price-feed/src/lib.rs | 3 +- contracts/consumer/band-price-feed/src/msg.rs | 57 ----- .../consumer/band-price-feed/src/scheduler.rs | 101 -------- .../consumer/band-price-feed/src/state.rs | 67 ++---- packages/apis/src/ibc/packet.rs | 3 + 10 files changed, 386 insertions(+), 333 deletions(-) create mode 100644 contracts/consumer/band-price-feed/src/bin/schema.rs create mode 100644 contracts/consumer/band-price-feed/src/ibc.rs delete mode 100644 contracts/consumer/band-price-feed/src/msg.rs delete mode 100644 contracts/consumer/band-price-feed/src/scheduler.rs diff --git a/contracts/consumer/band-price-feed/Cargo.toml b/contracts/consumer/band-price-feed/Cargo.toml index 14c0ccff..5c367d8f 100644 --- a/contracts/consumer/band-price-feed/Cargo.toml +++ b/contracts/consumer/band-price-feed/Cargo.toml @@ -1,15 +1,27 @@ [package] -name = "band-price-feed" -edition.workspace = true -version.workspace = true -license.workspace = true -repository.workspace = true +name = "mesh-band-price-feed" +description = "Returns exchange rates of assets fetched from Band Protocol" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] +# enables generation of mt utilities +mt = ["library", "sylvia/mt"] [dependencies] mesh-apis = { workspace = true } +mesh-scheduler = { workspace = true } sylvia = { workspace = true } cosmwasm-schema = { workspace = true } @@ -21,11 +33,15 @@ cw-utils = { workspace = true } schemars = { workspace = true } serde = { workspace = true } thiserror = { workspace = true } -obi = "0.0.2" -cw-band = "0.1.1" +obi = { workspace = true } +cw-band = { workspace = true } [dev-dependencies] cw-multi-test = { workspace = true } test-case = { workspace = true } derivative = { workspace = true } anyhow = { workspace = true } + +[[bin]] +name = "schema" +doc = false diff --git a/contracts/consumer/band-price-feed/src/bin/schema.rs b/contracts/consumer/band-price-feed/src/bin/schema.rs new file mode 100644 index 00000000..299308b9 --- /dev/null +++ b/contracts/consumer/band-price-feed/src/bin/schema.rs @@ -0,0 +1,12 @@ +use cosmwasm_schema::write_api; + +use mesh_band_price_feed::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; + +#[cfg(not(tarpaulin_include))] +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, + } +} diff --git a/contracts/consumer/band-price-feed/src/contract.rs b/contracts/consumer/band-price-feed/src/contract.rs index e6a268a4..5a2dd4d0 100644 --- a/contracts/consumer/band-price-feed/src/contract.rs +++ b/contracts/consumer/band-price-feed/src/contract.rs @@ -1,86 +1,141 @@ -#[cfg(not(feature = "library"))] -use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_json_binary, Binary, Deps, DepsMut, Empty, Env, IbcMsg, IbcTimeout, MessageInfo, Response, IbcChannel, - StdResult, Uint256, Uint64, + to_json_binary, Binary, Coin, DepsMut, Env, IbcChannel, IbcEndpoint, IbcMsg, IbcTimeout, Response, Uint64 }; use cw2::set_contract_version; use cw_storage_plus::Item; +use cw_utils::nonpayable; +use mesh_apis::price_feed_api::{PriceFeedApi, PriceResponse}; use crate::error::ContractError; -use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use crate::state::{Config, Rate, ReferenceData, BAND_CONFIG, ENDPOINT, RATES}; -use obi::enc::OBIEncode; -use cw_band::{Input, OracleRequestPacketData}; -use crate::state::TradingPair; use crate::price_keeper::PriceKeeper; -use crate::scheduler::{Action, Scheduler}; +use crate::state::{TradingPair, Config}; -const E9: Uint64 = Uint64::new(1_000_000_000u64); -const E18: Uint256 = Uint256::from_u128(1_000_000_000_000_000_000u128); +use sylvia::types::{InstantiateCtx, QueryCtx, SudoCtx}; +use sylvia::{contract, schemars}; + +use cw_band::{Input, OracleRequestPacketData}; +use mesh_scheduler::{Action, Scheduler}; +use obi::enc::OBIEncode; // Version info for migration -const CONTRACT_NAME: &str = "crates.io:band-ibc-price-feed"; +const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub struct RemotePriceFeedContract { pub channel: Item<'static, IbcChannel>, + pub config: Item<'static, Config>, pub trading_pair: Item<'static, TradingPair>, pub price_keeper: PriceKeeper, - pub scheduler: Scheduler>, + pub scheduler: Scheduler>, ContractError>, +} + +impl Default for RemotePriceFeedContract { + fn default() -> Self { + Self::new() + } } -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn instantiate( - deps: DepsMut, - _env: Env, - _info: MessageInfo, - msg: InstantiateMsg, -) -> Result { - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - BAND_CONFIG.save( - deps.storage, - &Config { - client_id: msg.client_id, - oracle_script_id: msg.oracle_script_id, - ask_count: msg.ask_count, - min_count: msg.min_count, - fee_limit: msg.fee_limit, - prepare_gas: msg.prepare_gas, - execute_gas: msg.execute_gas, - minimum_sources: msg.minimum_sources, - }, - )?; - - Ok(Response::new().add_attribute("method", "instantiate")) +#[cfg_attr(not(feature = "library"), sylvia::entry_points)] +#[contract] +#[sv::error(ContractError)] +#[sv::messages(mesh_apis::price_feed_api as PriceFeedApi)] +impl RemotePriceFeedContract { + pub fn new() -> Self { + Self { + channel: Item::new("channel"), + config: Item::new("config"), + trading_pair: Item::new("tpair"), + price_keeper: PriceKeeper::new(), + // TODO: the indirection can be removed once Sylvia supports + // generics. The constructor can then probably be constant. + // + // Stable existential types would be even better! + // https://github.com/rust-lang/rust/issues/63063 + scheduler: Scheduler::new(Box::new(try_request)), + } + } + + #[sv::msg(instantiate)] + pub fn instantiate( + &self, + ctx: InstantiateCtx, + trading_pair: TradingPair, + client_id: String, + connection_id: String, + channel_id: String, + port_id: String, + oracle_script_id: Uint64, + ask_count: Uint64, + min_count: Uint64, + fee_limit: Vec, + prepare_gas: Uint64, + execute_gas: Uint64, + minimum_sources: u8, + ) -> Result { + nonpayable(&ctx.info)?; + + set_contract_version(ctx.deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + self.trading_pair.save(ctx.deps.storage, &trading_pair)?; + + self.config.save(ctx.deps.storage, &Config{ + client_id, + connection_id, + endpoint: IbcEndpoint{ + port_id, + channel_id, + }, + oracle_script_id, + ask_count, + min_count, + fee_limit, + prepare_gas, + execute_gas, + minimum_sources, + })?; + + Ok(Response::new()) + } } -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn execute( - deps: DepsMut, - env: Env, - _info: MessageInfo, - msg: ExecuteMsg, -) -> Result { - match msg { - ExecuteMsg::Request { symbols } => try_request(deps, env, symbols), +impl PriceFeedApi for RemotePriceFeedContract { + type Error = ContractError; + // FIXME: make these under a feature flag if we need virtual-staking multitest compatibility + type ExecC = cosmwasm_std::Empty; + type QueryC = cosmwasm_std::Empty; + + /// 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 { + Ok(self + .price_keeper + .price(ctx.deps, &ctx.env) + .map(|rate| PriceResponse { + native_per_foreign: rate, + })?) + } + + fn handle_epoch(&self, ctx: SudoCtx) -> Result { + self.scheduler.trigger(ctx.deps, &ctx.env) } } // TODO: Possible features // - Request fee + Bounty logic to prevent request spam and incentivize relayer // - Whitelist who can call update price -pub fn try_request( - deps: DepsMut, - env: Env, - symbols: Vec, -) -> Result { - let endpoint = ENDPOINT.load(deps.storage)?; - let config = BAND_CONFIG.load(deps.storage)?; +pub fn try_request(deps: DepsMut, env: &Env) -> Result { + let contract = RemotePriceFeedContract::new(); + let TradingPair { + base_asset, + quote_asset, + } = contract.trading_pair.load(deps.storage)?; + let config = contract.config.load(deps.storage)?; + let channel = contract + .channel + .may_load(deps.storage)? + .ok_or(ContractError::IbcChannelNotOpen)?; let raw_calldata = Input { - symbols, + symbols: vec![base_asset, quote_asset], minimum_sources: config.minimum_sources, } .try_to_vec() @@ -101,61 +156,8 @@ pub fn try_request( }; Ok(Response::new().add_message(IbcMsg::SendPacket { - channel_id: endpoint.channel_id, + channel_id: channel.endpoint.channel_id, data: to_json_binary(&packet)?, timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(60)), })) } - -/// this is a no-op -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> StdResult { - Ok(Response::default()) -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { - match msg { - QueryMsg::GetRate { symbol } => to_json_binary(&query_rate(deps, &symbol)?), - QueryMsg::GetReferenceData { symbol_pair } => { - to_json_binary(&query_reference_data(deps, &symbol_pair)?) - } - QueryMsg::GetReferenceDataBulk { symbol_pairs } => { - to_json_binary(&query_reference_data_bulk(deps, &symbol_pairs)?) - } - } -} - -fn query_rate(deps: Deps, symbol: &str) -> StdResult { - if symbol == "USD" { - Ok(Rate::new(E9, Uint64::MAX, Uint64::new(0))) - } else { - RATES.load(deps.storage, symbol) - } -} - -fn query_reference_data(deps: Deps, symbol_pair: &(String, String)) -> StdResult { - let base = query_rate(deps, &symbol_pair.0)?; - let quote = query_rate(deps, &symbol_pair.1)?; - - Ok(ReferenceData::new( - Uint256::from(base.rate) - .checked_mul(E18)? - .checked_div(Uint256::from(quote.rate))?, - base.resolve_time, - quote.resolve_time, - )) -} - -fn query_reference_data_bulk( - deps: Deps, - symbol_pairs: &[(String, String)], -) -> StdResult> { - symbol_pairs - .iter() - .map(|pair| query_reference_data(deps, pair)) - .collect() -} -// TODO: Writing test -#[cfg(test)] -mod tests {} \ No newline at end of file diff --git a/contracts/consumer/band-price-feed/src/error.rs b/contracts/consumer/band-price-feed/src/error.rs index db8c61e1..75134a18 100644 --- a/contracts/consumer/band-price-feed/src/error.rs +++ b/contracts/consumer/band-price-feed/src/error.rs @@ -1,6 +1,10 @@ use cosmwasm_std::StdError; +use cw_utils::PaymentError; +use mesh_apis::ibc::VersionError; use thiserror::Error; +use crate::price_keeper::PriceKeeperError; + /// Never is a placeholder to ensure we don't return any errors #[derive(Error, Debug)] pub enum Never {} @@ -10,14 +14,44 @@ pub enum ContractError { #[error("{0}")] Std(#[from] StdError), + #[error("{0}")] + Payment(#[from] PaymentError), + + #[error("{0}")] + PriceKeeper(#[from] PriceKeeperError), + + #[error("Unauthorized")] + Unauthorized, + #[error("Request didn't suceess")] RequestNotSuccess {}, - #[error("Only supports channel with ibc version bandchain-1, got {version}")] - InvalidIbcVersion { version: String }, + #[error("{0}")] + IbcVersion(#[from] VersionError), + + #[error("The provided IBC channel is not open")] + IbcChannelNotOpen, + + #[error("Contract already has an open IBC channel")] + IbcChannelAlreadyOpen, + + #[error("You must start the channel handshake on the other side, it doesn't support OpenInit")] + IbcOpenInitDisallowed, + + #[error("Contract does not receive packets ack")] + IbcAckNotAccepted, + + #[error("Contract does not receive packets timeout")] + IbcTimeoutNotAccepted, + + #[error("Response packet should only contains 2 symbols")] + InvalidResponsePacket, + + #[error("Symbol must be base denom or quote denom")] + SymbolsNotMatch, - #[error("Only supports unordered channel")] - OnlyUnorderedChannel {}, + #[error("Invalid price, must be greater than 0.0")] + InvalidPrice, #[error("Custom Error val: {val:?}")] CustomError { val: String }, diff --git a/contracts/consumer/band-price-feed/src/ibc.rs b/contracts/consumer/band-price-feed/src/ibc.rs new file mode 100644 index 00000000..f0485a1a --- /dev/null +++ b/contracts/consumer/band-price-feed/src/ibc.rs @@ -0,0 +1,184 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; + +use cosmwasm_std::{ + from_json, Decimal, DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse, + IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, IbcPacket, + IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, StdError, + Uint128, +}; +use cw_band::{OracleResponsePacketData, Output, ResolveStatus}; +use mesh_apis::ibc::{ + ack_fail, ack_success, validate_channel_order, PriceFeedAck, ProtocolVersion, +}; +use obi::OBIDecode; + +use crate::contract::RemotePriceFeedContract; +use crate::error::ContractError; + +/// This is the maximum version of the Mesh Security protocol that we support +const SUPPORTED_IBC_PROTOCOL_VERSION: &str = "0.1.0"; +/// This is the minimum version that we are compatible with +const MIN_IBC_PROTOCOL_VERSION: &str = "0.1.0"; + +#[cfg_attr(not(feature = "library"), entry_point)] +/// enforces ordering and versioning constraints +pub fn ibc_channel_open( + deps: DepsMut, + _env: Env, + msg: IbcChannelOpenMsg, +) -> Result { + // ensure we have no channel yet + let contract = RemotePriceFeedContract::new(); + if contract.channel.may_load(deps.storage)?.is_some() { + return Err(ContractError::IbcChannelAlreadyOpen); + } + // ensure we are called with OpenInit + let (channel, counterparty_version) = match msg { + IbcChannelOpenMsg::OpenInit { .. } => return Err(ContractError::IbcOpenInitDisallowed), + IbcChannelOpenMsg::OpenTry { + channel, + counterparty_version, + } => (channel, counterparty_version), + }; + + // verify the ordering is correct + validate_channel_order(&channel.order)?; + + // assert expected endpoint + let config = contract.config.load(deps.storage)?; + if config.connection_id != channel.connection_id + || config.endpoint.port_id != channel.counterparty_endpoint.port_id + { + // FIXME: do we need a better error here? + return Err(ContractError::Unauthorized); + } + + // we handshake with the counterparty version, it must not be empty + let v: ProtocolVersion = from_json(counterparty_version.as_bytes())?; + // if we can build a response to this, then it is compatible. And we use the highest version there + let version = v.build_response(SUPPORTED_IBC_PROTOCOL_VERSION, MIN_IBC_PROTOCOL_VERSION)?; + + let response = Ibc3ChannelOpenResponse { + version: version.to_string()?, + }; + Ok(Some(response)) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +/// once it's established, we store data +pub fn ibc_channel_connect( + deps: DepsMut, + _env: Env, + msg: IbcChannelConnectMsg, +) -> Result { + let contract = RemotePriceFeedContract::new(); + + // ensure we have no channel yet + if contract.channel.may_load(deps.storage)?.is_some() { + return Err(ContractError::IbcChannelAlreadyOpen); + } + // ensure we are called with OpenConfirm + let channel = match msg { + IbcChannelConnectMsg::OpenConfirm { channel } => channel, + IbcChannelConnectMsg::OpenAck { .. } => return Err(ContractError::IbcOpenInitDisallowed), + }; + + // Version negotiation over, we can only store the channel + let contract = RemotePriceFeedContract::new(); + contract.channel.save(deps.storage, &channel)?; + + Ok(IbcBasicResponse::default()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn ibc_channel_close( + _deps: DepsMut, + _env: Env, + _msg: IbcChannelCloseMsg, +) -> Result { + todo!(); +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn ibc_packet_receive( + deps: DepsMut, + env: Env, + msg: IbcPacketReceiveMsg, +) -> Result { + let packet = msg.packet; + + do_ibc_packet_receive(deps, env, &packet).or_else(|err| { + let error = err.to_string(); + let ack_fail = ack_fail(err)?; + Ok(IbcReceiveResponse::new() + .set_ack(ack_fail) + .add_attributes(vec![ + ("action", "receive"), + ("success", "false"), + ("error", &error), + ])) + }) +} + +fn do_ibc_packet_receive( + deps: DepsMut, + env: Env, + packet: &IbcPacket, +) -> Result { + let contract = RemotePriceFeedContract::new(); + + let resp: OracleResponsePacketData = from_json(&packet.data)?; + if resp.resolve_status != ResolveStatus::Success { + return Err(ContractError::RequestNotSuccess {}); + } + let result: Output = OBIDecode::try_from_slice(&resp.result) + .map_err(|err| StdError::parse_err("Oracle response packet", err.to_string()))?; + + let trading_pair = contract.trading_pair.load(deps.storage)?; + let mut base_price = Uint128::zero(); + let mut quote_price = Uint128::zero(); + + if result.responses.len() != 2 { + return Err(ContractError::InvalidResponsePacket {}); + } + for r in result.responses { + if r.response_code == 0 { + if r.symbol == trading_pair.base_asset { + base_price = Uint128::from(r.rate); + } else if r.symbol == trading_pair.quote_asset { + quote_price = Uint128::from(r.rate); + } else { + return Err(ContractError::SymbolsNotMatch {}); + } + } + } + if base_price.is_zero() || quote_price.is_zero() { + return Err(ContractError::InvalidPrice {}); + } + + let rate = Decimal::from_ratio(base_price, quote_price); + contract.price_keeper.update(deps, env.block.time, rate)?; + let ack = ack_success(&PriceFeedAck {})?; + Ok(IbcReceiveResponse::new() + .set_ack(ack) + .add_attribute("action", "ibc_packet_received")) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn ibc_packet_ack( + _deps: DepsMut, + _env: Env, + _msg: IbcPacketAckMsg, +) -> Result { + Err(ContractError::IbcAckNotAccepted) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn ibc_packet_timeout( + _deps: DepsMut, + _env: Env, + _msg: IbcPacketTimeoutMsg, +) -> Result { + Err(ContractError::IbcTimeoutNotAccepted) +} diff --git a/contracts/consumer/band-price-feed/src/lib.rs b/contracts/consumer/band-price-feed/src/lib.rs index fb31a56a..bf90d1de 100644 --- a/contracts/consumer/band-price-feed/src/lib.rs +++ b/contracts/consumer/band-price-feed/src/lib.rs @@ -1,6 +1,5 @@ pub mod contract; pub mod error; -pub mod msg; pub mod state; pub mod price_keeper; -pub mod scheduler; \ No newline at end of file +pub mod ibc; \ No newline at end of file diff --git a/contracts/consumer/band-price-feed/src/msg.rs b/contracts/consumer/band-price-feed/src/msg.rs deleted file mode 100644 index 7dc4bf31..00000000 --- a/contracts/consumer/band-price-feed/src/msg.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::state::{Rate, ReferenceData}; -use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Coin, Uint64}; - -#[cw_serde] -pub struct InstantiateMsg { - // A unique ID for the oracle request - pub client_id: String, - // The oracle script ID to query - pub oracle_script_id: Uint64, - // The number of validators that are requested to respond - pub ask_count: Uint64, - // The minimum number of validators that need to respond - pub min_count: Uint64, - // The maximum amount of band in uband to be paid to the data source providers - // e.g. vec![Coin::new(100, "uband")] - pub fee_limit: Vec, - // Amount of gas to pay to prepare raw requests - pub prepare_gas: Uint64, - // Amount of gas reserved for execution - pub execute_gas: Uint64, - // Minimum number of sources required to return a successful response - pub minimum_sources: u8, - // The minimum amount that sender need to send to create a new oracle request - pub fee: Vec, -} - -#[cw_serde] -pub enum ExecuteMsg { - Request { symbols: Vec }, -} - -#[cw_serde] -#[derive(QueryResponses)] -pub enum QueryMsg { - #[returns(Rate)] - // Returns the RefData of a given symbol - GetRate { - // Symbol to query - symbol: String, - }, - #[returns(ReferenceData)] - // Returns the ReferenceData of a given asset pairing - GetReferenceData { - // Symbol pair to query where: - // symbol_pair := (base_symbol, quote_symbol) - // e.g. BTC/USD ≡ ("BTC", "USD") - symbol_pair: (String, String), - }, - #[returns(Vec)] - // Returns the ReferenceDatas of the given asset pairings - GetReferenceDataBulk { - // Vector of Symbol pair to query - // e.g. ≡ <("BTC", "USD"), ("ETH", "USD"), ("BAND", "BTC")> - symbol_pairs: Vec<(String, String)>, - }, -} diff --git a/contracts/consumer/band-price-feed/src/scheduler.rs b/contracts/consumer/band-price-feed/src/scheduler.rs deleted file mode 100644 index ee47aabe..00000000 --- a/contracts/consumer/band-price-feed/src/scheduler.rs +++ /dev/null @@ -1,101 +0,0 @@ -use cosmwasm_std::{DepsMut, Env, Response, Timestamp}; -use cw_storage_plus::Item; - -use crate::error::ContractError; - -pub trait Action: Fn(DepsMut, &Env) -> Result {} -impl Action for F where F: Fn(DepsMut, &Env) -> Result {} - -/// A component that schedules a single action to be executed regularly, -/// as in "every epoch". It relies on a trigger being called rather rapidly (every block?). -pub struct Scheduler { - last_epoch: Item<'static, Timestamp>, - epoch_in_secs: Item<'static, u64>, - action: A, -} - -impl Scheduler -where - A: Action, -{ - pub const fn new(action: A) -> Self { - Self { - last_epoch: Item::new("last_epoch"), - epoch_in_secs: Item::new("epoch"), - action, - } - } - - pub fn init(&self, deps: &mut DepsMut, epoch_in_secs: u64) -> Result<(), ContractError> { - self.last_epoch - .save(deps.storage, &Timestamp::from_seconds(0))?; - self.epoch_in_secs.save(deps.storage, &epoch_in_secs)?; - Ok(()) - } - - pub fn trigger(&self, deps: DepsMut, env: &Env) -> Result { - let last_epoch = self.last_epoch.load(deps.storage)?; - let epoch_in_secs = self.epoch_in_secs.load(deps.storage)?; - let secs_since_last_epoch = env.block.time.seconds() - last_epoch.seconds(); - if secs_since_last_epoch >= epoch_in_secs { - self.last_epoch.save(deps.storage, &env.block.time)?; - (self.action)(deps, env) - } else { - Ok(Response::new()) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use cosmwasm_std::{ - testing::{mock_dependencies, mock_env}, - Binary, - }; - - #[test] - fn scheduler_first_epoch_always_fires() { - let scheduler = Scheduler::new(|_, _| Ok(Response::new().set_data(Binary::from(b"foo")))); - let mut deps = mock_dependencies(); - let env = mock_env(); - - scheduler.init(&mut deps.as_mut(), 111111).unwrap(); - assert!(scheduler - .trigger(deps.as_mut(), &env) - .unwrap() - .data - .is_some()); - } - - #[test] - fn scheduler() { - let scheduler = Scheduler::new(|_, _| Ok(Response::new().set_data(Binary::from(b"foo")))); - let mut deps = mock_dependencies(); - let mut env = mock_env(); - - scheduler.init(&mut deps.as_mut(), 10).unwrap(); - - #[track_caller] - fn assert_fired(s: &Scheduler, deps: DepsMut, env: &Env) { - assert!(s.trigger(deps, env).unwrap().data.is_some()) - } - - #[track_caller] - fn assert_noop(s: &Scheduler, deps: DepsMut, env: &Env) { - assert_eq!(s.trigger(deps, env).unwrap(), Response::new()) - } - - assert_fired(&scheduler, deps.as_mut(), &env); - - env.block.time = env.block.time.plus_seconds(5); - assert_noop(&scheduler, deps.as_mut(), &env); - env.block.time = env.block.time.plus_seconds(5); - assert_fired(&scheduler, deps.as_mut(), &env); - env.block.time = env.block.time.plus_seconds(5); - assert_noop(&scheduler, deps.as_mut(), &env); - assert_noop(&scheduler, deps.as_mut(), &env); - env.block.time = env.block.time.plus_seconds(5); - assert_fired(&scheduler, deps.as_mut(), &env); - } -} diff --git a/contracts/consumer/band-price-feed/src/state.rs b/contracts/consumer/band-price-feed/src/state.rs index 6ce189ca..a0c7fb95 100644 --- a/contracts/consumer/band-price-feed/src/state.rs +++ b/contracts/consumer/band-price-feed/src/state.rs @@ -1,72 +1,33 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::IbcEndpoint; -use cosmwasm_std::{Coin, Uint256, Uint64}; -use cw_storage_plus::{Item, Map}; -use cosmwasm_std::{Decimal, Timestamp}; +use cosmwasm_std::{Decimal, IbcEndpoint, Timestamp}; +use cosmwasm_std::{Coin, Uint64}; #[cw_serde] pub struct Config { + // A unique ID for the oracle request pub client_id: String, + pub connection_id: String, + // Endpoint to validate when open channel + pub endpoint: IbcEndpoint, + // The oracle script ID to query pub oracle_script_id: Uint64, + // The number of validators that are requested to respond pub ask_count: Uint64, + // The minimum number of validators that need to respond pub min_count: Uint64, + // The maximum amount of band in uband to be paid to the data source providers + // e.g. vec![Coin::new(100, "uband")] pub fee_limit: Vec, + // Amount of gas to pay to prepare raw requests pub prepare_gas: Uint64, + // Amount of gas reserved for execution pub execute_gas: Uint64, + // Minimum number of sources required to return a successful response pub minimum_sources: u8, } -#[cw_serde] -pub struct Rate { - // Rate of an asset relative to USD - pub rate: Uint64, - // The resolve time of the request ID - pub resolve_time: Uint64, - // The request ID where the rate was derived from - pub request_id: Uint64, -} - -impl Rate { - pub fn new(rate: Uint64, resolve_time: Uint64, request_id: Uint64) -> Self { - Rate { - rate, - resolve_time, - request_id, - } - } -} - -pub const RATES: Map<&str, Rate> = Map::new("rates"); - -pub const ENDPOINT: Item = Item::new("endpoint"); - -pub const BAND_CONFIG: Item = Item::new("config"); - -#[cw_serde] -pub struct ReferenceData { - // Pair rate e.g. rate of BTC/USD - pub rate: Uint256, - // Unix time of when the base asset was last updated. e.g. Last update time of BTC in Unix time - pub last_updated_base: Uint64, - // Unix time of when the quote asset was last updated. e.g. Last update time of USD in Unix time - pub last_updated_quote: Uint64, -} - -impl ReferenceData { - pub fn new(rate: Uint256, last_updated_base: Uint64, last_updated_quote: Uint64) -> Self { - ReferenceData { - rate, - last_updated_base, - last_updated_quote, - } - } -} - - - #[cw_serde] pub struct TradingPair { - pub pool_id: u64, pub base_asset: String, pub quote_asset: String, } diff --git a/packages/apis/src/ibc/packet.rs b/packages/apis/src/ibc/packet.rs index 0978b9e8..5ab901ef 100644 --- a/packages/apis/src/ibc/packet.rs +++ b/packages/apis/src/ibc/packet.rs @@ -154,6 +154,9 @@ pub struct ValsetUpdateAck {} #[cw_serde] pub struct DistributeAck {} +#[cw_serde] +pub struct PriceFeedAck {} + /// This is a generic ICS acknowledgement format. /// Protobuf defined here: https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/proto/ibc/core/channel/v1/channel.proto#L141-L147 /// This is compatible with the JSON serialization. From 8e7968653d9eeb9ab1904c671aa65ea204508d7a Mon Sep 17 00:00:00 2001 From: Trinity Date: Sat, 13 Jul 2024 08:57:00 +0700 Subject: [PATCH 04/12] rename scheduler to price-feed and move PriceKeeder to price-feed --- Cargo.lock | 8 +- Cargo.toml | 2 +- contracts/consumer/band-price-feed/Cargo.toml | 2 +- .../consumer/band-price-feed/src/contract.rs | 48 +++++- .../consumer/band-price-feed/src/error.rs | 2 +- contracts/consumer/band-price-feed/src/lib.rs | 1 - .../consumer/band-price-feed/src/main.rs | 3 - .../consumer/band-price-feed/src/state.rs | 9 +- .../consumer/osmosis-price-feed/Cargo.toml | 2 +- .../osmosis-price-feed/src/contract.rs | 3 +- .../consumer/osmosis-price-feed/src/error.rs | 2 +- .../consumer/osmosis-price-feed/src/lib.rs | 1 - .../osmosis-price-feed/src/price_keeper.rs | 154 ------------------ .../consumer/osmosis-price-feed/src/state.rs | 7 - packages/{scheduler => price-feed}/Cargo.toml | 6 +- packages/price-feed/src/lib.rs | 5 + .../price-feed}/src/price_keeper.rs | 7 +- .../src/scheduler.rs | 0 packages/scheduler/src/lib.rs | 3 - 19 files changed, 73 insertions(+), 192 deletions(-) delete mode 100644 contracts/consumer/band-price-feed/src/main.rs delete mode 100644 contracts/consumer/osmosis-price-feed/src/price_keeper.rs rename packages/{scheduler => price-feed}/Cargo.toml (52%) create mode 100644 packages/price-feed/src/lib.rs rename {contracts/consumer/band-price-feed => packages/price-feed}/src/price_keeper.rs (97%) rename packages/{scheduler => price-feed}/src/scheduler.rs (100%) delete mode 100644 packages/scheduler/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d9230d98..fceea954 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -607,7 +607,7 @@ dependencies = [ "cw2", "derivative", "mesh-apis", - "mesh-scheduler", + "mesh-price-feed", "obi", "schemars", "serde", @@ -733,7 +733,7 @@ dependencies = [ "cw2", "derivative", "mesh-apis", - "mesh-scheduler", + "mesh-price-feed", "schemars", "serde", "sylvia", @@ -760,11 +760,13 @@ dependencies = [ ] [[package]] -name = "mesh-scheduler" +name = "mesh-price-feed" version = "0.10.0-alpha.1" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", + "thiserror", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 23072ec2..b71f65dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ repository = "https://github.com/osmosis-labs/mesh-security" mesh-apis = { path = "./packages/apis" } mesh-bindings = { path = "./packages/bindings" } mesh-burn = { path = "./packages/burn" } -mesh-scheduler = { path = "./packages/scheduler" } +mesh-price-feed = { path = "./packages/price-feed" } mesh-sync = { path = "./packages/sync" } mesh-virtual-staking-mock = { path = "./packages/virtual-staking-mock" } diff --git a/contracts/consumer/band-price-feed/Cargo.toml b/contracts/consumer/band-price-feed/Cargo.toml index 5c367d8f..0a77e055 100644 --- a/contracts/consumer/band-price-feed/Cargo.toml +++ b/contracts/consumer/band-price-feed/Cargo.toml @@ -21,7 +21,7 @@ mt = ["library", "sylvia/mt"] [dependencies] mesh-apis = { workspace = true } -mesh-scheduler = { workspace = true } +mesh-price-feed = { workspace = true } sylvia = { workspace = true } cosmwasm-schema = { workspace = true } diff --git a/contracts/consumer/band-price-feed/src/contract.rs b/contracts/consumer/band-price-feed/src/contract.rs index 5a2dd4d0..5b9786e5 100644 --- a/contracts/consumer/band-price-feed/src/contract.rs +++ b/contracts/consumer/band-price-feed/src/contract.rs @@ -7,14 +7,13 @@ use cw_utils::nonpayable; use mesh_apis::price_feed_api::{PriceFeedApi, PriceResponse}; use crate::error::ContractError; -use crate::price_keeper::PriceKeeper; use crate::state::{TradingPair, Config}; use sylvia::types::{InstantiateCtx, QueryCtx, SudoCtx}; use sylvia::{contract, schemars}; use cw_band::{Input, OracleRequestPacketData}; -use mesh_scheduler::{Action, Scheduler}; +use mesh_price_feed::{Action, Scheduler, PriceKeeper}; use obi::enc::OBIEncode; // Version info for migration @@ -161,3 +160,48 @@ pub fn try_request(deps: DepsMut, env: &Env) -> Result timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(60)), })) } + +#[cfg(test)] +mod tests { + use cosmwasm_std::{testing::{mock_dependencies, mock_env, mock_info}, Uint64, Uint128}; + + use super::*; + + #[test] + fn instantiation() { + let mut deps = mock_dependencies(); + let env = mock_env(); + let info = mock_info("sender", &[]); + let contract = RemotePriceFeedContract::new(); + + let trading_pair = TradingPair { + base_asset: "base".to_string(), + quote_asset: "quote".to_string(), + }; + + contract + .instantiate( + InstantiateCtx { + deps: deps.as_mut(), + env, + info, + }, + trading_pair, + "07-tendermint-0".to_string(), + "connection-0".to_string(), + "channel-0".to_string(), + "transfer".to_string(), + Uint64::new(1), + Uint64::new(10), + Uint64::new(50), + vec![Coin { + denom: "uband".to_string(), + amount: Uint128::new(1), + }], + Uint64::new(100000), + Uint64::new(200000), + 1 + ) + .unwrap(); + } +} diff --git a/contracts/consumer/band-price-feed/src/error.rs b/contracts/consumer/band-price-feed/src/error.rs index 75134a18..d99b086f 100644 --- a/contracts/consumer/band-price-feed/src/error.rs +++ b/contracts/consumer/band-price-feed/src/error.rs @@ -3,7 +3,7 @@ use cw_utils::PaymentError; use mesh_apis::ibc::VersionError; use thiserror::Error; -use crate::price_keeper::PriceKeeperError; +use mesh_price_feed::PriceKeeperError; /// Never is a placeholder to ensure we don't return any errors #[derive(Error, Debug)] diff --git a/contracts/consumer/band-price-feed/src/lib.rs b/contracts/consumer/band-price-feed/src/lib.rs index bf90d1de..98045d6e 100644 --- a/contracts/consumer/band-price-feed/src/lib.rs +++ b/contracts/consumer/band-price-feed/src/lib.rs @@ -1,5 +1,4 @@ pub mod contract; pub mod error; pub mod state; -pub mod price_keeper; pub mod ibc; \ No newline at end of file diff --git a/contracts/consumer/band-price-feed/src/main.rs b/contracts/consumer/band-price-feed/src/main.rs deleted file mode 100644 index e7a11a96..00000000 --- a/contracts/consumer/band-price-feed/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/contracts/consumer/band-price-feed/src/state.rs b/contracts/consumer/band-price-feed/src/state.rs index a0c7fb95..936d2159 100644 --- a/contracts/consumer/band-price-feed/src/state.rs +++ b/contracts/consumer/band-price-feed/src/state.rs @@ -1,6 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Decimal, IbcEndpoint, Timestamp}; -use cosmwasm_std::{Coin, Uint64}; +use cosmwasm_std::{Coin, Uint64, IbcEndpoint}; #[cw_serde] pub struct Config { @@ -31,9 +30,3 @@ pub struct TradingPair { pub base_asset: String, pub quote_asset: String, } - -#[cw_serde] -pub struct PriceInfo { - pub time: Timestamp, - pub native_per_foreign: Decimal, -} diff --git a/contracts/consumer/osmosis-price-feed/Cargo.toml b/contracts/consumer/osmosis-price-feed/Cargo.toml index 71efe754..007dd0f0 100644 --- a/contracts/consumer/osmosis-price-feed/Cargo.toml +++ b/contracts/consumer/osmosis-price-feed/Cargo.toml @@ -19,7 +19,7 @@ mt = ["library", "sylvia/mt"] [dependencies] mesh-apis = { workspace = true } -mesh-scheduler = { workspace = true } +mesh-price-feed = { workspace = true } sylvia = { workspace = true } cosmwasm-schema = { workspace = true } diff --git a/contracts/consumer/osmosis-price-feed/src/contract.rs b/contracts/consumer/osmosis-price-feed/src/contract.rs index 826358aa..52fb4e3c 100644 --- a/contracts/consumer/osmosis-price-feed/src/contract.rs +++ b/contracts/consumer/osmosis-price-feed/src/contract.rs @@ -10,8 +10,7 @@ use mesh_apis::price_feed_api::{self, PriceFeedApi, PriceResponse}; use crate::error::ContractError; use crate::ibc::{make_ibc_packet, AUTH_ENDPOINT}; use crate::msg::AuthorizedEndpoint; -use crate::price_keeper::PriceKeeper; -use mesh_scheduler::{Action, Scheduler}; +use mesh_price_feed::{Action, Scheduler, PriceKeeper}; use crate::state::TradingPair; pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); diff --git a/contracts/consumer/osmosis-price-feed/src/error.rs b/contracts/consumer/osmosis-price-feed/src/error.rs index bbaf3417..36523261 100644 --- a/contracts/consumer/osmosis-price-feed/src/error.rs +++ b/contracts/consumer/osmosis-price-feed/src/error.rs @@ -3,7 +3,7 @@ use cw_utils::PaymentError; use mesh_apis::ibc::VersionError; use thiserror::Error; -use crate::price_keeper::PriceKeeperError; +use mesh_price_feed::PriceKeeperError; #[derive(Error, Debug)] pub enum ContractError { diff --git a/contracts/consumer/osmosis-price-feed/src/lib.rs b/contracts/consumer/osmosis-price-feed/src/lib.rs index e29f6f83..10d266d8 100644 --- a/contracts/consumer/osmosis-price-feed/src/lib.rs +++ b/contracts/consumer/osmosis-price-feed/src/lib.rs @@ -2,5 +2,4 @@ pub mod contract; pub mod error; pub mod ibc; pub mod msg; -pub mod price_keeper; pub mod state; diff --git a/contracts/consumer/osmosis-price-feed/src/price_keeper.rs b/contracts/consumer/osmosis-price-feed/src/price_keeper.rs deleted file mode 100644 index 50b5d2de..00000000 --- a/contracts/consumer/osmosis-price-feed/src/price_keeper.rs +++ /dev/null @@ -1,154 +0,0 @@ -use cosmwasm_std::{Decimal, Deps, DepsMut, Env, Timestamp}; -use cw_storage_plus::Item; - -use crate::state::PriceInfo; - -/// A component that keeps track of the latest price info. -pub struct PriceKeeper { - pub price_info: Item<'static, PriceInfo>, - pub price_info_ttl_in_secs: Item<'static, u64>, -} - -impl PriceKeeper { - pub const fn new() -> Self { - Self { - price_info: Item::new("price"), - price_info_ttl_in_secs: Item::new("price_ttl"), - } - } - - pub fn init( - &self, - deps: &mut DepsMut, - price_info_ttl_in_secs: u64, - ) -> Result<(), PriceKeeperError> { - self.price_info_ttl_in_secs - .save(deps.storage, &price_info_ttl_in_secs)?; - Ok(()) - } - - pub fn update( - &self, - deps: DepsMut, - time: Timestamp, - twap: Decimal, - ) -> Result<(), PriceKeeperError> { - let old = self.price_info.may_load(deps.storage)?; - match old { - Some(old) if old.time > time => { - // don't update if we have newer price info stored - } - _ => self.price_info.save( - deps.storage, - &PriceInfo { - time, - native_per_foreign: twap, - }, - )?, - } - - Ok(()) - } - - pub fn price(&self, deps: Deps, env: &Env) -> Result { - let price_info_ttl = self.price_info_ttl_in_secs.load(deps.storage)?; - let price_info = self - .price_info - .may_load(deps.storage)? - .ok_or(PriceKeeperError::NoPriceData)?; - - if env.block.time.minus_seconds(price_info_ttl) < price_info.time { - Ok(price_info.native_per_foreign) - } else { - Err(PriceKeeperError::OutdatedPriceData) - } - } -} - -#[derive(thiserror::Error, Debug, PartialEq)] -pub enum PriceKeeperError { - #[error("StdError: {0}")] - StdError(#[from] cosmwasm_std::StdError), - - #[error("Price data is outdated")] - OutdatedPriceData, - - #[error("No price data available")] - NoPriceData, -} - -#[cfg(test)] -mod tests { - use super::*; - - use cosmwasm_std::testing::{mock_dependencies, mock_env}; - - #[test] - fn happy_path() { - let mut deps = mock_dependencies(); - let mut env = mock_env(); - let keeper = PriceKeeper::new(); - - keeper.init(&mut deps.as_mut(), 600).unwrap(); - keeper - .update(deps.as_mut(), env.block.time, Decimal::one()) - .unwrap(); - - let price = keeper.price(deps.as_ref(), &env).unwrap(); - assert_eq!(price, Decimal::one()); - - env.block.time = env.block.time.plus_seconds(559); - let price = keeper.price(deps.as_ref(), &env).unwrap(); - assert_eq!(price, Decimal::one()); - } - - #[test] - fn no_initial_price_info() { - let mut deps = mock_dependencies(); - let env = mock_env(); - let keeper = PriceKeeper::new(); - - keeper.init(&mut deps.as_mut(), 600).unwrap(); - - let err = keeper.price(deps.as_ref(), &env).unwrap_err(); - assert_eq!(err, PriceKeeperError::NoPriceData); - } - - #[test] - fn outdated_price_info() { - let mut deps = mock_dependencies(); - let mut env = mock_env(); - let keeper = PriceKeeper::new(); - - keeper.init(&mut deps.as_mut(), 600).unwrap(); - keeper - .update(deps.as_mut(), env.block.time, Decimal::one()) - .unwrap(); - - env.block.time = env.block.time.plus_seconds(601); - let err = keeper.price(deps.as_ref(), &env).unwrap_err(); - assert_eq!(err, PriceKeeperError::OutdatedPriceData); - } - - #[test] - fn update_with_older_price_info_is_ignored() { - let mut deps = mock_dependencies(); - let env = mock_env(); - let keeper = PriceKeeper::new(); - - keeper.init(&mut deps.as_mut(), 600).unwrap(); - keeper - .update(deps.as_mut(), env.block.time, Decimal::one()) - .unwrap(); - keeper - .update( - deps.as_mut(), - env.block.time.minus_seconds(1), - Decimal::percent(50), - ) - .unwrap(); - - let price = keeper.price(deps.as_ref(), &env).unwrap(); - assert_eq!(price, Decimal::one()); - } -} diff --git a/contracts/consumer/osmosis-price-feed/src/state.rs b/contracts/consumer/osmosis-price-feed/src/state.rs index a4d7f8a6..9caff437 100644 --- a/contracts/consumer/osmosis-price-feed/src/state.rs +++ b/contracts/consumer/osmosis-price-feed/src/state.rs @@ -1,5 +1,4 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Decimal, Timestamp}; #[cw_serde] pub struct TradingPair { @@ -7,9 +6,3 @@ pub struct TradingPair { pub base_asset: String, pub quote_asset: String, } - -#[cw_serde] -pub struct PriceInfo { - pub time: Timestamp, - pub native_per_foreign: Decimal, -} diff --git a/packages/scheduler/Cargo.toml b/packages/price-feed/Cargo.toml similarity index 52% rename from packages/scheduler/Cargo.toml rename to packages/price-feed/Cargo.toml index 9b60fbc8..c8dc6cbb 100644 --- a/packages/scheduler/Cargo.toml +++ b/packages/price-feed/Cargo.toml @@ -1,9 +1,11 @@ [package] -name = "mesh-scheduler" +name = "mesh-price-feed" version = { workspace = true } edition = { workspace = true } license = { workspace = true } [dependencies] +cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } -cw-storage-plus = { workspace = true } \ No newline at end of file +cw-storage-plus = { workspace = true } +thiserror = { workspace = true } \ No newline at end of file diff --git a/packages/price-feed/src/lib.rs b/packages/price-feed/src/lib.rs new file mode 100644 index 00000000..58e215e8 --- /dev/null +++ b/packages/price-feed/src/lib.rs @@ -0,0 +1,5 @@ +mod scheduler; +mod price_keeper; + +pub use scheduler::{Action, Scheduler}; +pub use price_keeper::{PriceKeeper, PriceKeeperError}; \ No newline at end of file diff --git a/contracts/consumer/band-price-feed/src/price_keeper.rs b/packages/price-feed/src/price_keeper.rs similarity index 97% rename from contracts/consumer/band-price-feed/src/price_keeper.rs rename to packages/price-feed/src/price_keeper.rs index 50b5d2de..54711d4b 100644 --- a/contracts/consumer/band-price-feed/src/price_keeper.rs +++ b/packages/price-feed/src/price_keeper.rs @@ -1,7 +1,12 @@ +use cosmwasm_schema::cw_serde; use cosmwasm_std::{Decimal, Deps, DepsMut, Env, Timestamp}; use cw_storage_plus::Item; -use crate::state::PriceInfo; +#[cw_serde] +pub struct PriceInfo { + pub time: Timestamp, + pub native_per_foreign: Decimal, +} /// A component that keeps track of the latest price info. pub struct PriceKeeper { diff --git a/packages/scheduler/src/scheduler.rs b/packages/price-feed/src/scheduler.rs similarity index 100% rename from packages/scheduler/src/scheduler.rs rename to packages/price-feed/src/scheduler.rs diff --git a/packages/scheduler/src/lib.rs b/packages/scheduler/src/lib.rs deleted file mode 100644 index d57bf830..00000000 --- a/packages/scheduler/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod scheduler; - -pub use scheduler::{Action, Scheduler}; \ No newline at end of file From c0780ce08e09a057e7aed8ba1c165e3d7f43f1f9 Mon Sep 17 00:00:00 2001 From: Trinity Date: Mon, 5 Aug 2024 11:25:12 +0700 Subject: [PATCH 05/12] lint --- .../consumer/band-price-feed/src/contract.rs | 49 +++++++++++-------- contracts/consumer/band-price-feed/src/lib.rs | 2 +- .../consumer/band-price-feed/src/state.rs | 2 +- .../osmosis-price-feed/src/contract.rs | 2 +- packages/price-feed/src/lib.rs | 4 +- packages/price-feed/src/scheduler.rs | 10 ++-- 6 files changed, 40 insertions(+), 29 deletions(-) diff --git a/contracts/consumer/band-price-feed/src/contract.rs b/contracts/consumer/band-price-feed/src/contract.rs index 5b9786e5..9b01fb32 100644 --- a/contracts/consumer/band-price-feed/src/contract.rs +++ b/contracts/consumer/band-price-feed/src/contract.rs @@ -1,5 +1,6 @@ use cosmwasm_std::{ - to_json_binary, Binary, Coin, DepsMut, Env, IbcChannel, IbcEndpoint, IbcMsg, IbcTimeout, Response, Uint64 + to_json_binary, Binary, Coin, DepsMut, Env, IbcChannel, IbcEndpoint, IbcMsg, IbcTimeout, + Response, Uint64, }; use cw2::set_contract_version; use cw_storage_plus::Item; @@ -7,13 +8,13 @@ use cw_utils::nonpayable; use mesh_apis::price_feed_api::{PriceFeedApi, PriceResponse}; use crate::error::ContractError; -use crate::state::{TradingPair, Config}; +use crate::state::{Config, TradingPair}; use sylvia::types::{InstantiateCtx, QueryCtx, SudoCtx}; use sylvia::{contract, schemars}; use cw_band::{Input, OracleRequestPacketData}; -use mesh_price_feed::{Action, Scheduler, PriceKeeper}; +use mesh_price_feed::{Action, PriceKeeper, Scheduler}; use obi::enc::OBIEncode; // Version info for migration @@ -75,22 +76,25 @@ impl RemotePriceFeedContract { set_contract_version(ctx.deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; self.trading_pair.save(ctx.deps.storage, &trading_pair)?; - - self.config.save(ctx.deps.storage, &Config{ - client_id, - connection_id, - endpoint: IbcEndpoint{ - port_id, - channel_id, + + self.config.save( + ctx.deps.storage, + &Config { + client_id, + connection_id, + endpoint: IbcEndpoint { + port_id, + channel_id, + }, + oracle_script_id, + ask_count, + min_count, + fee_limit, + prepare_gas, + execute_gas, + minimum_sources, }, - oracle_script_id, - ask_count, - min_count, - fee_limit, - prepare_gas, - execute_gas, - minimum_sources, - })?; + )?; Ok(Response::new()) } @@ -163,7 +167,10 @@ pub fn try_request(deps: DepsMut, env: &Env) -> Result #[cfg(test)] mod tests { - use cosmwasm_std::{testing::{mock_dependencies, mock_env, mock_info}, Uint64, Uint128}; + use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info}, + Uint128, Uint64, + }; use super::*; @@ -194,13 +201,13 @@ mod tests { Uint64::new(1), Uint64::new(10), Uint64::new(50), - vec![Coin { + vec![Coin { denom: "uband".to_string(), amount: Uint128::new(1), }], Uint64::new(100000), Uint64::new(200000), - 1 + 1, ) .unwrap(); } diff --git a/contracts/consumer/band-price-feed/src/lib.rs b/contracts/consumer/band-price-feed/src/lib.rs index 98045d6e..797945ab 100644 --- a/contracts/consumer/band-price-feed/src/lib.rs +++ b/contracts/consumer/band-price-feed/src/lib.rs @@ -1,4 +1,4 @@ pub mod contract; pub mod error; +pub mod ibc; pub mod state; -pub mod ibc; \ No newline at end of file diff --git a/contracts/consumer/band-price-feed/src/state.rs b/contracts/consumer/band-price-feed/src/state.rs index 936d2159..0efee827 100644 --- a/contracts/consumer/band-price-feed/src/state.rs +++ b/contracts/consumer/band-price-feed/src/state.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Coin, Uint64, IbcEndpoint}; +use cosmwasm_std::{Coin, IbcEndpoint, Uint64}; #[cw_serde] pub struct Config { diff --git a/contracts/consumer/osmosis-price-feed/src/contract.rs b/contracts/consumer/osmosis-price-feed/src/contract.rs index 52fb4e3c..048cdad7 100644 --- a/contracts/consumer/osmosis-price-feed/src/contract.rs +++ b/contracts/consumer/osmosis-price-feed/src/contract.rs @@ -10,8 +10,8 @@ use mesh_apis::price_feed_api::{self, PriceFeedApi, PriceResponse}; use crate::error::ContractError; use crate::ibc::{make_ibc_packet, AUTH_ENDPOINT}; use crate::msg::AuthorizedEndpoint; -use mesh_price_feed::{Action, Scheduler, PriceKeeper}; use crate::state::TradingPair; +use mesh_price_feed::{Action, PriceKeeper, Scheduler}; pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/packages/price-feed/src/lib.rs b/packages/price-feed/src/lib.rs index 58e215e8..94701469 100644 --- a/packages/price-feed/src/lib.rs +++ b/packages/price-feed/src/lib.rs @@ -1,5 +1,5 @@ -mod scheduler; mod price_keeper; +mod scheduler; +pub use price_keeper::{PriceKeeper, PriceKeeperError}; pub use scheduler::{Action, Scheduler}; -pub use price_keeper::{PriceKeeper, PriceKeeperError}; \ No newline at end of file diff --git a/packages/price-feed/src/scheduler.rs b/packages/price-feed/src/scheduler.rs index 9d810326..71790550 100644 --- a/packages/price-feed/src/scheduler.rs +++ b/packages/price-feed/src/scheduler.rs @@ -63,7 +63,9 @@ mod tests { #[test] fn scheduler_first_epoch_always_fires() { - let scheduler = TestScheduler::new(Box::new(|_, _| Ok(Response::new().set_data(Binary::from(b"foo"))))); + let scheduler = TestScheduler::new(Box::new(|_, _| { + Ok(Response::new().set_data(Binary::from(b"foo"))) + })); let mut deps = mock_dependencies(); let env = mock_env(); @@ -77,7 +79,9 @@ mod tests { #[test] fn scheduler() { - let scheduler = TestScheduler::new(Box::new(|_, _| Ok(Response::new().set_data(Binary::from(b"foo"))))); + let scheduler = TestScheduler::new(Box::new(|_, _| { + Ok(Response::new().set_data(Binary::from(b"foo"))) + })); let mut deps = mock_dependencies(); let mut env = mock_env(); @@ -93,7 +97,7 @@ mod tests { } #[track_caller] - fn assert_noop(s: &Scheduler, deps: DepsMut, env: &Env) + fn assert_noop(s: &Scheduler, deps: DepsMut, env: &Env) where A: Action, E: std::fmt::Debug + From, From 073dae62963fcd954bb8f6b4645fba6d1740865b Mon Sep 17 00:00:00 2001 From: vuong177 Date: Mon, 9 Sep 2024 18:00:32 +0700 Subject: [PATCH 06/12] minor --- Cargo.lock | 273 +++++++++++++++++++++++++++-------------------------- 1 file changed, 141 insertions(+), 132 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 901cc99e..0b8f13da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -15,15 +15,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "base16ct" @@ -33,9 +33,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -75,15 +75,15 @@ checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cfg-if" @@ -93,24 +93,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "num-traits", ] [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_panic" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" +checksum = "7782af8f90fe69a4bb41e460abe1727d493403d8b2cc43201a3a3e906b24379f" [[package]] name = "convert_case" @@ -123,12 +123,11 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b4c3f9c4616d6413d4b5fc4c270a4cc32a374b9be08671e80e1a019f805d8f" +checksum = "0f862b355f7e47711e0acfe6af92cb3fd8fd5936b66a9eaa338b51edabd1e77d" dependencies = [ "digest 0.10.7", - "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -137,18 +136,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c586ced10c3b00e809ee664a895025a024f60d65d34fe4c09daed4a4db68a3f3" +checksum = "cd85de6467cd1073688c86b39833679ae6db18cf4771471edd9809f15f1679f1" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8467874827d384c131955ff6f4d47d02e72a956a08eb3c0ff24f8c903a5517b4" +checksum = "5b4cd28147a66eba73720b47636a58097a979ad8c8bfdb4ed437ebcbfe362576" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -159,9 +158,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6db85d98ac80922aef465e564d5b21fa9cfac5058cb62df7f116c3682337393" +checksum = "9acd45c63d41bc9b16bc6dc7f6bd604a8c2ad29ce96c8f3c96d7fc8ef384392e" dependencies = [ "proc-macro2", "quote", @@ -170,9 +169,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712fe58f39d55c812f7b2c84e097cdede3a39d520f89b6dc3153837e31741927" +checksum = "2685c2182624b2e9e17f7596192de49a3f86b7a0c9a5f6b25c1df5e24592e836" dependencies = [ "base64", "bech32", @@ -192,18 +191,18 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] [[package]] name = "crypto-bigint" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -247,7 +246,7 @@ dependencies = [ "cw-utils", "derivative", "itertools 0.12.1", - "prost 0.12.4", + "prost 0.12.6", "schemars", "serde", "sha2 0.10.8", @@ -297,9 +296,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", @@ -339,15 +338,15 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.13" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest 0.10.7", @@ -374,15 +373,15 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" -version = "0.13.6" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", @@ -432,9 +431,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -463,9 +462,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hex" @@ -484,12 +483,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.5", ] [[package]] @@ -512,15 +511,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "k256" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa", @@ -532,9 +531,9 @@ dependencies = [ [[package]] name = "konst" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030400e39b2dff8beaa55986a17e0014ad657f569ca92426aafcb5e8e71faee7" +checksum = "50a0ba6de5f7af397afff922f22c149ff605c766cd3269cf6c1cd5e466dbe3b9" dependencies = [ "const_panic", "konst_kernel", @@ -544,9 +543,9 @@ dependencies = [ [[package]] name = "konst_kernel" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3376133edc39f027d551eb77b077c2865a0ef252b2e7d0dd6b6dc303db95d8b5" +checksum = "be0a455a1719220fd6adf756088e1c69a85bf14b6a9e24537a5cc04f503edb2b" dependencies = [ "typewit", ] @@ -559,15 +558,15 @@ checksum = "4e28ab1dc35e09d60c2b8c90d12a9a8d9666c876c10a3739a3196db0103b6043" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "memchr" -version = "2.6.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76fc44e2588d5b436dbc3c6cf62aef290f90dab6235744a93dfe1cc18f451e2c" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mesh-apis" @@ -813,24 +812,24 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "osmosis-std" @@ -907,9 +906,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -926,12 +925,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", - "prost-derive 0.12.4", + "prost-derive 0.12.6", ] [[package]] @@ -949,15 +948,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] @@ -971,9 +970,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1005,15 +1004,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schemars" -version = "0.8.17" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "schemars_derive", @@ -1023,14 +1022,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.17" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] @@ -1049,15 +1048,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -1091,33 +1090,34 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] name = "serde_derive_internals" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1148,9 +1148,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core 0.6.4", @@ -1158,9 +1158,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -1174,9 +1174,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sylvia" @@ -1209,7 +1209,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] @@ -1225,9 +1225,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -1252,7 +1252,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] @@ -1263,41 +1263,41 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", "test-case-core", ] [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", @@ -1306,33 +1306,42 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typewit" -version = "1.5.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5cee357cc77d1e02f10a3e6c4e13b8462fafab05998b62d331b7d9485589ff" +checksum = "c6fb9ae6a3cafaf0a5d14c2302ca525f9ae8e07a0f0e6949de88d882c37a6e24" +dependencies = [ + "typewit_proc_macros", +] + +[[package]] +name = "typewit_proc_macros" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1342,15 +1351,15 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] name = "zeroize" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" From da560f398b59d3a7430efbc1e71cbb0bcf62ad7a Mon Sep 17 00:00:00 2001 From: Trinity Date: Thu, 19 Sep 2024 14:31:30 +0700 Subject: [PATCH 07/12] Use the correct logic for ibc channel open and connect --- .../consumer/band-price-feed/src/contract.rs | 11 --- .../consumer/band-price-feed/src/error.rs | 8 +- contracts/consumer/band-price-feed/src/ibc.rs | 78 ++++++++++--------- .../consumer/band-price-feed/src/state.rs | 5 +- 4 files changed, 46 insertions(+), 56 deletions(-) diff --git a/contracts/consumer/band-price-feed/src/contract.rs b/contracts/consumer/band-price-feed/src/contract.rs index 9b01fb32..d14301a8 100644 --- a/contracts/consumer/band-price-feed/src/contract.rs +++ b/contracts/consumer/band-price-feed/src/contract.rs @@ -61,9 +61,6 @@ impl RemotePriceFeedContract { ctx: InstantiateCtx, trading_pair: TradingPair, client_id: String, - connection_id: String, - channel_id: String, - port_id: String, oracle_script_id: Uint64, ask_count: Uint64, min_count: Uint64, @@ -81,11 +78,6 @@ impl RemotePriceFeedContract { ctx.deps.storage, &Config { client_id, - connection_id, - endpoint: IbcEndpoint { - port_id, - channel_id, - }, oracle_script_id, ask_count, min_count, @@ -195,9 +187,6 @@ mod tests { }, trading_pair, "07-tendermint-0".to_string(), - "connection-0".to_string(), - "channel-0".to_string(), - "transfer".to_string(), Uint64::new(1), Uint64::new(10), Uint64::new(50), diff --git a/contracts/consumer/band-price-feed/src/error.rs b/contracts/consumer/band-price-feed/src/error.rs index d99b086f..67dfb090 100644 --- a/contracts/consumer/band-price-feed/src/error.rs +++ b/contracts/consumer/band-price-feed/src/error.rs @@ -1,6 +1,5 @@ use cosmwasm_std::StdError; use cw_utils::PaymentError; -use mesh_apis::ibc::VersionError; use thiserror::Error; use mesh_price_feed::PriceKeeperError; @@ -26,8 +25,11 @@ pub enum ContractError { #[error("Request didn't suceess")] RequestNotSuccess {}, - #[error("{0}")] - IbcVersion(#[from] VersionError), + #[error("Only supports channel with ibc version bandchain-1, got {version}")] + InvalidIbcVersion { version: String }, + + #[error("Only supports unordered channel")] + OnlyUnorderedChannel {}, #[error("The provided IBC channel is not open")] IbcChannelNotOpen, diff --git a/contracts/consumer/band-price-feed/src/ibc.rs b/contracts/consumer/band-price-feed/src/ibc.rs index f0485a1a..51bb77b6 100644 --- a/contracts/consumer/band-price-feed/src/ibc.rs +++ b/contracts/consumer/band-price-feed/src/ibc.rs @@ -2,10 +2,7 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{ - from_json, Decimal, DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse, - IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, IbcPacket, - IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, StdError, - Uint128, + from_json, Decimal, DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, IbcOrder, IbcPacket, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, StdError, Uint128 }; use cw_band::{OracleResponsePacketData, Output, ResolveStatus}; use mesh_apis::ibc::{ @@ -16,10 +13,7 @@ use obi::OBIDecode; use crate::contract::RemotePriceFeedContract; use crate::error::ContractError; -/// This is the maximum version of the Mesh Security protocol that we support -const SUPPORTED_IBC_PROTOCOL_VERSION: &str = "0.1.0"; -/// This is the minimum version that we are compatible with -const MIN_IBC_PROTOCOL_VERSION: &str = "0.1.0"; +pub const IBC_APP_VERSION: &str = "bandchain-1"; #[cfg_attr(not(feature = "library"), entry_point)] /// enforces ordering and versioning constraints @@ -34,33 +28,27 @@ pub fn ibc_channel_open( return Err(ContractError::IbcChannelAlreadyOpen); } // ensure we are called with OpenInit - let (channel, counterparty_version) = match msg { - IbcChannelOpenMsg::OpenInit { .. } => return Err(ContractError::IbcOpenInitDisallowed), - IbcChannelOpenMsg::OpenTry { - channel, - counterparty_version, - } => (channel, counterparty_version), - }; - - // verify the ordering is correct - validate_channel_order(&channel.order)?; - - // assert expected endpoint - let config = contract.config.load(deps.storage)?; - if config.connection_id != channel.connection_id - || config.endpoint.port_id != channel.counterparty_endpoint.port_id - { - // FIXME: do we need a better error here? - return Err(ContractError::Unauthorized); + let channel = msg.channel(); + let counterparty_version = msg.counterparty_version(); + + if channel.version != IBC_APP_VERSION { + return Err(ContractError::InvalidIbcVersion { + version: channel.version.clone(), + }); + } + if let Some(version) = counterparty_version { + if version != IBC_APP_VERSION { + return Err(ContractError::InvalidIbcVersion { + version: version.to_string(), + }); + } + } + if channel.order != IbcOrder::Unordered { + return Err(ContractError::OnlyUnorderedChannel {}); } - - // we handshake with the counterparty version, it must not be empty - let v: ProtocolVersion = from_json(counterparty_version.as_bytes())?; - // if we can build a response to this, then it is compatible. And we use the highest version there - let version = v.build_response(SUPPORTED_IBC_PROTOCOL_VERSION, MIN_IBC_PROTOCOL_VERSION)?; let response = Ibc3ChannelOpenResponse { - version: version.to_string()?, + version: channel.version.clone(), }; Ok(Some(response)) } @@ -78,11 +66,25 @@ pub fn ibc_channel_connect( if contract.channel.may_load(deps.storage)?.is_some() { return Err(ContractError::IbcChannelAlreadyOpen); } - // ensure we are called with OpenConfirm - let channel = match msg { - IbcChannelConnectMsg::OpenConfirm { channel } => channel, - IbcChannelConnectMsg::OpenAck { .. } => return Err(ContractError::IbcOpenInitDisallowed), - }; + + let channel = msg.channel(); + let counterparty_version = msg.counterparty_version(); + + if channel.version != IBC_APP_VERSION { + return Err(ContractError::InvalidIbcVersion { + version: channel.version.clone(), + }); + } + if let Some(version) = counterparty_version { + if version != IBC_APP_VERSION { + return Err(ContractError::InvalidIbcVersion { + version: version.to_string(), + }); + } + } + if channel.order != IbcOrder::Unordered { + return Err(ContractError::OnlyUnorderedChannel {}); + } // Version negotiation over, we can only store the channel let contract = RemotePriceFeedContract::new(); @@ -97,7 +99,7 @@ pub fn ibc_channel_close( _env: Env, _msg: IbcChannelCloseMsg, ) -> Result { - todo!(); + unimplemented!(); } #[cfg_attr(not(feature = "library"), entry_point)] diff --git a/contracts/consumer/band-price-feed/src/state.rs b/contracts/consumer/band-price-feed/src/state.rs index 0efee827..4686030f 100644 --- a/contracts/consumer/band-price-feed/src/state.rs +++ b/contracts/consumer/band-price-feed/src/state.rs @@ -1,13 +1,10 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Coin, IbcEndpoint, Uint64}; +use cosmwasm_std::{Coin, Uint64}; #[cw_serde] pub struct Config { // A unique ID for the oracle request pub client_id: String, - pub connection_id: String, - // Endpoint to validate when open channel - pub endpoint: IbcEndpoint, // The oracle script ID to query pub oracle_script_id: Uint64, // The number of validators that are requested to respond From 815e88872d67cc6232b7b63e4f220718afe78e76 Mon Sep 17 00:00:00 2001 From: Trinity Date: Thu, 19 Sep 2024 17:21:56 +0700 Subject: [PATCH 08/12] Fix band error on ack --- .../consumer/band-price-feed/src/contract.rs | 27 ++++++++++++++++--- contracts/consumer/band-price-feed/src/ibc.rs | 3 ++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/contracts/consumer/band-price-feed/src/contract.rs b/contracts/consumer/band-price-feed/src/contract.rs index d14301a8..61a355a1 100644 --- a/contracts/consumer/band-price-feed/src/contract.rs +++ b/contracts/consumer/band-price-feed/src/contract.rs @@ -10,7 +10,7 @@ use mesh_apis::price_feed_api::{PriceFeedApi, PriceResponse}; use crate::error::ContractError; use crate::state::{Config, TradingPair}; -use sylvia::types::{InstantiateCtx, QueryCtx, SudoCtx}; +use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, SudoCtx}; use sylvia::{contract, schemars}; use cw_band::{Input, OracleRequestPacketData}; @@ -58,7 +58,7 @@ impl RemotePriceFeedContract { #[sv::msg(instantiate)] pub fn instantiate( &self, - ctx: InstantiateCtx, + mut ctx: InstantiateCtx, trading_pair: TradingPair, client_id: String, oracle_script_id: Uint64, @@ -68,12 +68,12 @@ impl RemotePriceFeedContract { prepare_gas: Uint64, execute_gas: Uint64, minimum_sources: u8, + price_info_ttl_in_secs: u64, ) -> Result { nonpayable(&ctx.info)?; set_contract_version(ctx.deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; self.trading_pair.save(ctx.deps.storage, &trading_pair)?; - self.config.save( ctx.deps.storage, &Config { @@ -87,9 +87,27 @@ impl RemotePriceFeedContract { minimum_sources, }, )?; - + self.price_keeper + .init(&mut ctx.deps, price_info_ttl_in_secs)?; Ok(Response::new()) } + + #[sv::msg(query)] + pub fn get_price(&self, ctx: QueryCtx) -> Result { + Ok(self + .price_keeper + .price(ctx.deps, &ctx.env) + .map(|rate| PriceResponse { + native_per_foreign: rate, + })?) + } + + #[sv::msg(exec)] + pub fn request(&self, ctx: ExecCtx) -> Result { + let ExecCtx { deps, env, info: _ } = ctx; + try_request(deps, &env) + } + } impl PriceFeedApi for RemotePriceFeedContract { @@ -197,6 +215,7 @@ mod tests { Uint64::new(100000), Uint64::new(200000), 1, + 60, ) .unwrap(); } diff --git a/contracts/consumer/band-price-feed/src/ibc.rs b/contracts/consumer/band-price-feed/src/ibc.rs index 51bb77b6..a5444a55 100644 --- a/contracts/consumer/band-price-feed/src/ibc.rs +++ b/contracts/consumer/band-price-feed/src/ibc.rs @@ -173,7 +173,8 @@ pub fn ibc_packet_ack( _env: Env, _msg: IbcPacketAckMsg, ) -> Result { - Err(ContractError::IbcAckNotAccepted) + // We ignore acknowledgement from BandChain becuase it doesn't neccessary to know request id when handle result. + Ok(IbcBasicResponse::new().add_attribute("action", "ibc_packet_ack")) } #[cfg_attr(not(feature = "library"), entry_point)] From 9645e320ec349cead3ea23e3f57270d01a9936f8 Mon Sep 17 00:00:00 2001 From: Trinity Date: Thu, 19 Sep 2024 17:25:31 +0700 Subject: [PATCH 09/12] Remove redundant query --- contracts/consumer/band-price-feed/src/contract.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/contracts/consumer/band-price-feed/src/contract.rs b/contracts/consumer/band-price-feed/src/contract.rs index 61a355a1..dcdecb42 100644 --- a/contracts/consumer/band-price-feed/src/contract.rs +++ b/contracts/consumer/band-price-feed/src/contract.rs @@ -92,16 +92,6 @@ impl RemotePriceFeedContract { Ok(Response::new()) } - #[sv::msg(query)] - pub fn get_price(&self, ctx: QueryCtx) -> Result { - Ok(self - .price_keeper - .price(ctx.deps, &ctx.env) - .map(|rate| PriceResponse { - native_per_foreign: rate, - })?) - } - #[sv::msg(exec)] pub fn request(&self, ctx: ExecCtx) -> Result { let ExecCtx { deps, env, info: _ } = ctx; From abe70bf0712706c4f6fe6e373ddbb3d834616c5f Mon Sep 17 00:00:00 2001 From: Trinity Date: Thu, 19 Sep 2024 17:29:14 +0700 Subject: [PATCH 10/12] lint --- contracts/consumer/band-price-feed/src/contract.rs | 1 - contracts/consumer/band-price-feed/src/ibc.rs | 11 +++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/contracts/consumer/band-price-feed/src/contract.rs b/contracts/consumer/band-price-feed/src/contract.rs index dcdecb42..0434ae02 100644 --- a/contracts/consumer/band-price-feed/src/contract.rs +++ b/contracts/consumer/band-price-feed/src/contract.rs @@ -97,7 +97,6 @@ impl RemotePriceFeedContract { let ExecCtx { deps, env, info: _ } = ctx; try_request(deps, &env) } - } impl PriceFeedApi for RemotePriceFeedContract { diff --git a/contracts/consumer/band-price-feed/src/ibc.rs b/contracts/consumer/band-price-feed/src/ibc.rs index a5444a55..3113ff05 100644 --- a/contracts/consumer/band-price-feed/src/ibc.rs +++ b/contracts/consumer/band-price-feed/src/ibc.rs @@ -2,7 +2,10 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{ - from_json, Decimal, DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, IbcOrder, IbcPacket, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, StdError, Uint128 + from_json, Decimal, DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse, + IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, IbcOrder, + IbcPacket, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, + StdError, Uint128, }; use cw_band::{OracleResponsePacketData, Output, ResolveStatus}; use mesh_apis::ibc::{ @@ -30,7 +33,7 @@ pub fn ibc_channel_open( // ensure we are called with OpenInit let channel = msg.channel(); let counterparty_version = msg.counterparty_version(); - + if channel.version != IBC_APP_VERSION { return Err(ContractError::InvalidIbcVersion { version: channel.version.clone(), @@ -66,10 +69,10 @@ pub fn ibc_channel_connect( if contract.channel.may_load(deps.storage)?.is_some() { return Err(ContractError::IbcChannelAlreadyOpen); } - + let channel = msg.channel(); let counterparty_version = msg.counterparty_version(); - + if channel.version != IBC_APP_VERSION { return Err(ContractError::InvalidIbcVersion { version: channel.version.clone(), From 9e07070cb59423bfab8fef59c3c755f8b070ef36 Mon Sep 17 00:00:00 2001 From: Trinity Date: Thu, 19 Sep 2024 17:33:06 +0700 Subject: [PATCH 11/12] lint --- contracts/consumer/converter/src/ibc.rs | 2 +- contracts/provider/external-staking/src/contract.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/consumer/converter/src/ibc.rs b/contracts/consumer/converter/src/ibc.rs index 3f4209e8..93e3c903 100644 --- a/contracts/consumer/converter/src/ibc.rs +++ b/contracts/consumer/converter/src/ibc.rs @@ -258,7 +258,7 @@ pub fn ibc_packet_receive( 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()); + .add_attribute("rewards", rewards.amount.to_string()); let ack = ack_success(&TransferRewardsAck {})?; IbcReceiveResponse::new() .set_ack(ack) diff --git a/contracts/provider/external-staking/src/contract.rs b/contracts/provider/external-staking/src/contract.rs index 2d565016..93d3ee5d 100644 --- a/contracts/provider/external-staking/src/contract.rs +++ b/contracts/provider/external-staking/src/contract.rs @@ -515,7 +515,7 @@ impl ExternalStakingContract<'_> { for ((user, validator), stake) in stakes.iter() { let mut new_stake = stake.clone(); - let amount = new_stake.stake.low().clone(); + let amount = new_stake.stake.low(); let unbond = PendingUnbond { amount, release_at: env.block.time, @@ -525,7 +525,7 @@ impl ExternalStakingContract<'_> { let mut distribution = self .distribution - .may_load(deps.storage, &validator)? + .may_load(deps.storage, validator)? .unwrap_or_default(); new_stake .points_alignment From f51b2c6cb4f28f708092fbf1c29553079f7e2469 Mon Sep 17 00:00:00 2001 From: Trinity Date: Thu, 19 Sep 2024 17:38:27 +0700 Subject: [PATCH 12/12] lint --- contracts/consumer/band-price-feed/src/contract.rs | 3 +-- contracts/consumer/band-price-feed/src/ibc.rs | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/contracts/consumer/band-price-feed/src/contract.rs b/contracts/consumer/band-price-feed/src/contract.rs index 0434ae02..b7a3c397 100644 --- a/contracts/consumer/band-price-feed/src/contract.rs +++ b/contracts/consumer/band-price-feed/src/contract.rs @@ -1,6 +1,5 @@ use cosmwasm_std::{ - to_json_binary, Binary, Coin, DepsMut, Env, IbcChannel, IbcEndpoint, IbcMsg, IbcTimeout, - Response, Uint64, + to_json_binary, Binary, Coin, DepsMut, Env, IbcChannel, IbcMsg, IbcTimeout, Response, Uint64, }; use cw2::set_contract_version; use cw_storage_plus::Item; diff --git a/contracts/consumer/band-price-feed/src/ibc.rs b/contracts/consumer/band-price-feed/src/ibc.rs index 3113ff05..92fede5a 100644 --- a/contracts/consumer/band-price-feed/src/ibc.rs +++ b/contracts/consumer/band-price-feed/src/ibc.rs @@ -8,9 +8,7 @@ use cosmwasm_std::{ StdError, Uint128, }; use cw_band::{OracleResponsePacketData, Output, ResolveStatus}; -use mesh_apis::ibc::{ - ack_fail, ack_success, validate_channel_order, PriceFeedAck, ProtocolVersion, -}; +use mesh_apis::ibc::{ack_fail, ack_success, PriceFeedAck}; use obi::OBIDecode; use crate::contract::RemotePriceFeedContract; @@ -91,7 +89,7 @@ pub fn ibc_channel_connect( // Version negotiation over, we can only store the channel let contract = RemotePriceFeedContract::new(); - contract.channel.save(deps.storage, &channel)?; + contract.channel.save(deps.storage, channel)?; Ok(IbcBasicResponse::default()) }