Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 158 additions & 93 deletions Cargo.lock

Large diffs are not rendered by default.

21 changes: 11 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ members = [
]

[workspace.package]
version = "1.0.1"
version = "1.1.0"
authors = [
"Larry Engineer <larry@delphidigital.io>",
"Piotr Babel <piotr@delphilabs.io>",
Expand All @@ -30,21 +30,22 @@ documentation = "https://docs.marsprotocol.io/"
keywords = ["mars", "cosmos", "cosmwasm"]

[workspace.dependencies]
anyhow = "1.0.68"
anyhow = "1.0.71"
bech32 = "0.9.1"
cosmwasm-schema = "1.1.9"
cosmwasm-std = "1.1.9"
cw2 = { git = "https://github.com/mars-protocol/cw-plus", rev = "4014255" }
cw-multi-test = "0.16.1"
cosmwasm-schema = "1.2.6"
cosmwasm-std = "1.2.6"
cw2 = { git = "https://github.com/CosmWasm/cw-plus", rev = "de1fb0b" }
cw-multi-test = "0.16.5"
cw-storage-plus = "1.0.1"
cw-utils = "1.0.1"
mars-owner = { version = "1.2.0", features = ["emergency-owner"] }
osmosis-std = "0.14.0"
osmosis-std = "0.15.3"
osmosis-test-tube = "15.1.0"
prost = { version = "0.11.5", default-features = false, features = ["prost-derive"] }
schemars = "0.8.11"
serde = { version = "1.0.152", default-features = false, features = ["derive"] }
thiserror = "1.0.38"
pyth-sdk-cw = "1.2.0"
schemars = "0.8.12"
serde = { version = "1.0.163", default-features = false, features = ["derive"] }
thiserror = "1.0.40"

# packages
mars-health = { version = "1.0.0", path = "./packages/health" }
Expand Down
4 changes: 2 additions & 2 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ args = ["build", "--release", "--target", "wasm32-unknown-unknown", "--locked"]
[tasks.rust-optimizer]
script = """
if [[ $(arch) == "arm64" ]]; then
image="cosmwasm/workspace-optimizer-arm64:0.12.11"
image="cosmwasm/workspace-optimizer-arm64:0.12.13"
else
image="cosmwasm/workspace-optimizer:0.12.11"
image="cosmwasm/workspace-optimizer:0.12.13"
fi
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
Expand Down
64 changes: 46 additions & 18 deletions contracts/oracle/base/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ use mars_red_bank_types::oracle::{
};
use mars_utils::helpers::validate_native_denom;

use crate::{error::ContractResult, PriceSource};
use crate::{error::ContractResult, PriceSourceChecked, PriceSourceUnchecked};

const DEFAULT_LIMIT: u32 = 10;
const MAX_LIMIT: u32 = 30;

pub struct OracleBase<'a, P, C>
pub struct OracleBase<'a, P, PU, C>
where
P: PriceSource<C>,
P: PriceSourceChecked<C>,
PU: PriceSourceUnchecked<P, C>,
C: CustomQuery,
{
/// Contract's owner
Expand All @@ -28,28 +29,33 @@ where
pub config: Item<'a, Config>,
/// The price source of each coin denom
pub price_sources: Map<'a, &'a str, P>,
/// Phantom data holds the unchecked price source type
pub unchecked_price_source: PhantomData<PU>,
/// Phantom data holds the custom query type
pub custom_query: PhantomData<C>,
}

impl<'a, P, C> Default for OracleBase<'a, P, C>
impl<'a, P, PU, C> Default for OracleBase<'a, P, PU, C>
where
P: PriceSource<C>,
P: PriceSourceChecked<C>,
PU: PriceSourceUnchecked<P, C>,
C: CustomQuery,
{
fn default() -> Self {
Self {
owner: Owner::new("owner"),
config: Item::new("config"),
price_sources: Map::new("price_sources"),
unchecked_price_source: PhantomData,
custom_query: PhantomData,
}
}
}

impl<'a, P, C> OracleBase<'a, P, C>
impl<'a, P, PU, C> OracleBase<'a, P, PU, C>
where
P: PriceSource<C>,
P: PriceSourceChecked<C>,
PU: PriceSourceUnchecked<P, C>,
C: CustomQuery,
{
pub fn instantiate(&self, deps: DepsMut<C>, msg: InstantiateMsg) -> ContractResult<Response> {
Expand Down Expand Up @@ -77,7 +83,7 @@ where
&self,
deps: DepsMut<C>,
info: MessageInfo,
msg: ExecuteMsg<P>,
msg: ExecuteMsg<PU>,
) -> ContractResult<Response> {
match msg {
ExecuteMsg::UpdateOwner(update) => self.update_owner(deps, info, update),
Expand All @@ -88,6 +94,9 @@ where
ExecuteMsg::RemovePriceSource {
denom,
} => self.remove_price_source(deps, info.sender, denom),
ExecuteMsg::UpdateConfig {
base_denom,
} => self.update_config(deps, info.sender, base_denom),
}
}

Expand Down Expand Up @@ -126,14 +135,14 @@ where
deps: DepsMut<C>,
sender_addr: Addr,
denom: String,
price_source: P,
price_source: PU,
) -> ContractResult<Response> {
self.owner.assert_owner(deps.storage, &sender_addr)?;

validate_native_denom(&denom)?;

let cfg = self.config.load(deps.storage)?;
price_source.validate(&deps.querier, &denom, &cfg.base_denom)?;
let price_source = price_source.validate(deps.as_ref(), &denom, &cfg.base_denom)?;
self.price_sources.save(deps.storage, &denom, &price_source)?;

Ok(Response::new()
Expand All @@ -157,6 +166,31 @@ where
.add_attribute("denom", denom))
}

fn update_config(
&self,
deps: DepsMut<C>,
sender_addr: Addr,
base_denom: Option<String>,
) -> ContractResult<Response> {
self.owner.assert_owner(deps.storage, &sender_addr)?;

if let Some(bd) = &base_denom {
validate_native_denom(bd)?;
};

let mut config = self.config.load(deps.storage)?;
let prev_base_denom = config.base_denom.clone();
config.base_denom = base_denom.unwrap_or(config.base_denom);
self.config.save(deps.storage, &config)?;

let response = Response::new()
.add_attribute("action", "update_config")
.add_attribute("prev_base_denom", prev_base_denom)
.add_attribute("base_denom", config.base_denom);

Ok(response)
}

fn query_config(&self, deps: Deps<C>) -> StdResult<ConfigResponse> {
let owner_state = self.owner.query(deps.storage)?;
let cfg = self.config.load(deps.storage)?;
Expand Down Expand Up @@ -204,13 +238,7 @@ where
let cfg = self.config.load(deps.storage)?;
let price_source = self.price_sources.load(deps.storage, &denom)?;
Ok(PriceResponse {
price: price_source.query_price(
&deps,
&env,
&denom,
&cfg.base_denom,
&self.price_sources,
)?,
price: price_source.query_price(&deps, &env, &denom, &cfg, &self.price_sources)?,
denom,
})
}
Expand All @@ -233,7 +261,7 @@ where
.map(|item| {
let (k, v) = item?;
Ok(PriceResponse {
price: v.query_price(&deps, &env, &k, &cfg.base_denom, &self.price_sources)?,
price: v.query_price(&deps, &env, &k, &cfg, &self.price_sources)?,
denom: k,
})
})
Expand Down
14 changes: 13 additions & 1 deletion contracts/oracle/base/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use cosmwasm_std::{ConversionOverflowError, OverflowError, StdError};
use cosmwasm_std::{
CheckedFromRatioError, CheckedMultiplyRatioError, ConversionOverflowError,
DecimalRangeExceeded, OverflowError, StdError,
};
use mars_owner::OwnerError;
use mars_red_bank_types::error::MarsError;
use mars_utils::error::ValidationError;
Expand Down Expand Up @@ -27,6 +30,15 @@ pub enum ContractError {
#[error("{0}")]
Overflow(#[from] OverflowError),

#[error("{0}")]
CheckedMultiplyRatio(#[from] CheckedMultiplyRatioError),

#[error("{0}")]
CheckedFromRatio(#[from] CheckedFromRatioError),

#[error("{0}")]
DecimalRangeExceeded(#[from] DecimalRangeExceeded),

#[error("Invalid price source: {reason}")]
InvalidPriceSource {
reason: String,
Expand Down
25 changes: 14 additions & 11 deletions contracts/oracle/base/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
use std::fmt::{Debug, Display};

use cosmwasm_std::{CustomQuery, Decimal, Deps, Env, QuerierWrapper};
use cosmwasm_std::{CustomQuery, Decimal, Deps, Env};
use cw_storage_plus::Map;
use mars_red_bank_types::oracle::Config;
use schemars::JsonSchema;
use serde::{de::DeserializeOwned, Serialize};

use crate::ContractResult;

pub trait PriceSource<C>:
Serialize + DeserializeOwned + Clone + Debug + Display + PartialEq + JsonSchema
pub trait PriceSourceUnchecked<P, C>:
Serialize + DeserializeOwned + Clone + Debug + PartialEq + JsonSchema
where
P: PriceSourceChecked<C>,
C: CustomQuery,
{
/// Validate whether the price source is valid for a given denom
fn validate(
&self,
querier: &QuerierWrapper<C>,
denom: &str,
base_denom: &str,
) -> ContractResult<()>;
fn validate(self, deps: Deps<C>, denom: &str, base_denom: &str) -> ContractResult<P>;
}

pub trait PriceSourceChecked<C>:
Serialize + DeserializeOwned + Clone + Debug + Display + PartialEq + JsonSchema
where
C: CustomQuery,
{
/// Query the price of an asset based on the given price source
///
/// Notable arguments:
///
/// - `denom`: The coin whose price is to be queried.
///
/// - `base_denom`: The coin in which the price is to be denominated in.
/// - `config.base_denom`: The coin in which the price is to be denominated in.
/// For example, if `denom` is uatom and `base_denom` is uosmo, the
/// function should return how many uosmo is per one uatom.
///
Expand All @@ -39,7 +42,7 @@ where
deps: &Deps<C>,
env: &Env,
denom: &str,
base_denom: &str,
config: &Config,
price_sources: &Map<&str, Self>,
) -> ContractResult<Decimal>;
}
3 changes: 3 additions & 0 deletions contracts/oracle/osmosis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ doctest = false
backtraces = ["cosmwasm-std/backtraces"]

[dependencies]
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw2 = { workspace = true }
cw-storage-plus = { workspace = true }
mars-owner = { workspace = true }
mars-oracle-base = { workspace = true }
mars-osmosis = { workspace = true }
mars-red-bank-types = { workspace = true }
osmosis-std = { workspace = true }
pyth-sdk-cw = { workspace = true }
schemars = { workspace = true }
serde = { workspace = true }
thiserror = { workspace = true }
Expand Down
4 changes: 2 additions & 2 deletions contracts/oracle/osmosis/examples/schema.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use cosmwasm_schema::write_api;
use mars_oracle_osmosis::OsmosisPriceSource;
use mars_oracle_osmosis::OsmosisPriceSourceUnchecked;
use mars_red_bank_types::oracle::{ExecuteMsg, InstantiateMsg, QueryMsg};

fn main() {
write_api! {
instantiate: InstantiateMsg,
execute: ExecuteMsg<OsmosisPriceSource>,
execute: ExecuteMsg<OsmosisPriceSourceUnchecked>,
query: QueryMsg,
}
}
9 changes: 5 additions & 4 deletions contracts/oracle/osmosis/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use cosmwasm_std::Empty;
use mars_oracle_base::OracleBase;

use crate::OsmosisPriceSource;
use crate::price_source::{OsmosisPriceSourceChecked, OsmosisPriceSourceUnchecked};

/// The Osmosis oracle contract inherits logics from the base oracle contract, with the Osmosis query
/// and price source plugins
pub type OsmosisOracle<'a> = OracleBase<'a, OsmosisPriceSource, Empty>;
pub type OsmosisOracle<'a> =
OracleBase<'a, OsmosisPriceSourceChecked, OsmosisPriceSourceUnchecked, Empty>;

pub const CONTRACT_NAME: &str = "crates.io:mars-oracle-osmosis";
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
Expand Down Expand Up @@ -35,7 +36,7 @@ pub mod entry {
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: ExecuteMsg<OsmosisPriceSource>,
msg: ExecuteMsg<OsmosisPriceSourceUnchecked>,
) -> ContractResult<Response> {
OsmosisOracle::default().execute(deps, info, msg)
}
Expand All @@ -47,6 +48,6 @@ pub mod entry {

#[entry_point]
pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> ContractResult<Response> {
migrations::v1_0_0::migrate(deps)
migrations::v1_0_1::migrate(deps)
}
}
6 changes: 5 additions & 1 deletion contracts/oracle/osmosis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ mod helpers;
mod migrations;
pub mod msg;
mod price_source;
pub mod stride;

pub use price_source::{Downtime, DowntimeDetector, OsmosisPriceSource};
pub use price_source::{
scale_pyth_price, Downtime, DowntimeDetector, GeometricTwap, OsmosisPriceSourceChecked,
OsmosisPriceSourceUnchecked, RedemptionRate,
};
Loading