Skip to content

Commit

Permalink
Dshd oracle (#43)
Browse files Browse the repository at this point in the history
* rough start

* rename

* dshd-oracle done

* tweaks

* started test framing, needs mock dshd

* mock dshd & harness

* mock dshd
  • Loading branch information
DrPresident authored Jul 10, 2024
1 parent 326f26b commit 3ed2309
Show file tree
Hide file tree
Showing 18 changed files with 1,112 additions and 29 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ members = [
"contracts/mock_band",
"contracts/mock_ojo",
"contracts/oracle_router",
"contracts/dshd_oracle",
"contracts/mock_dshd",
"contracts/shade_staking_derivatives_oracle",
"contracts/shadeswap_market_oracle",
"contracts/shadeswap_spot_oracle",
Expand Down
26 changes: 26 additions & 0 deletions contracts/dshd_oracle/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "dshd_oracle"
version = "0.2.0"
authors = ["Jackson Swenson <[email protected]>"]
edition = "2021"

exclude = [
# Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication.
"contract.wasm",
"hash.txt",
]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = []
# for quicker tests, cargo test --lib

[dependencies]
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
shade-oracles = { path = "../../packages/shade_oracles", features = ["core"] }
shade-toolkit = { workspace = true }
213 changes: 213 additions & 0 deletions contracts/dshd_oracle/src/contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
use cosmwasm_std::{entry_point, QuerierWrapper, StdError, Uint128, Uint256};
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};

use shade_oracles::better_secret_math::common::exp10;
use shade_oracles::core::{pad_query_result, validate_admin, AdminPermissions};
use shade_oracles::interfaces::common::{OraclePrice, OracleQuery, PriceResponse};
use shade_oracles::interfaces::providers::ReferenceData;
use shade_oracles::ssp::Item;
use shade_toolkit::{Contract, Query, BLOCK_SIZE};

use crate::{dshd, msg::*};

// Key used to query router
pub const UNDERLYING_KEY: &str = "SHD";
// Key for the "price" (underlying * redemption_rate)
pub const PRICE_KEY: &str = "Shade Derivative";
// Key for the redemption rate
pub const RATE_KEY: &str = "Shade Derivative Rate";

// Storage
const CONFIG: Item<Config> = Item::new("config");

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
msg: InstantiateMsg,
) -> StdResult<Response> {
let admin_auth = msg.admin_auth.validate(deps.api)?;
let router = msg.router.validate(deps.api)?;
let dshd = msg.dshd.validate(deps.api)?;

let config = Config {
admin_auth,
router,
dshd,
enabled: true,
};

CONFIG.save(deps.storage, &config)?;

Ok(Response::default())
}

fn require_enabled(config: &Config) -> StdResult<()> {
if !config.enabled {
return Err(StdError::generic_err("Contract is disabled"));
}
Ok(())
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> StdResult<Response> {
let mut config = CONFIG.load(deps.storage)?;
match msg {
ExecuteMsg::UpdateConfig {
router,
dshd,
admin_auth,
enabled,
} => {
validate_admin(
&deps.querier,
AdminPermissions::OraclesAdmin,
info.sender,
&config.admin_auth,
)?;
let mut resp = Response::default();
if let Some(router) = router {
config.router = router.validate(deps.api)?;
resp = resp.add_attribute("router", config.router.address.clone());
}
if let Some(admin_auth) = admin_auth {
config.admin_auth = admin_auth.validate(deps.api)?;
resp = resp.add_attribute("admin_auth", config.admin_auth.address.clone());
}
if let Some(dshd) = dshd {
config.dshd = dshd.validate(deps.api)?;
resp = resp.add_attribute("dshd", config.dshd.address.clone());
}
if let Some(enabled) = enabled {
config.enabled = enabled;
resp = resp.add_attribute("enabled", config.enabled.to_string());
}
CONFIG.save(deps.storage, &config)?;
Ok(resp.add_attribute("action", "update_config"))
}
}
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
let config = CONFIG.load(deps.storage)?;
require_enabled(&config)?;
pad_query_result(
match msg {
QueryMsg::GetPrice { key } => to_binary(&query_oracle_price_by_key(
&deps,
&env,
key,
&config.router,
&config.dshd,
)?),
QueryMsg::GetPrices { keys } => {
let mut results = vec![];
for key in keys {
results.push(query_oracle_price_by_key(
&deps,
&env,
key,
&config.router,
&config.dshd,
)?);
}
to_binary(&results)
}
QueryMsg::GetConfig {} => to_binary(&config),
},
BLOCK_SIZE,
)
}

fn query_oracle_price_by_key(
deps: &Deps,
env: &Env,
key: String,
router: &Contract,
dshd: &Contract,
) -> StdResult<OraclePrice> {
if key == PRICE_KEY.to_string() {
query_price(deps, env, router, dshd)
} else if key == RATE_KEY.to_string() {
query_rate(deps, env, dshd)
} else {
Err(StdError::generic_err(format!(
"Invalid Key, expected one of {}, {}",
PRICE_KEY, RATE_KEY
)))
}
}

fn query_price(
deps: &Deps,
env: &Env,
router: &Contract,
dshd: &Contract,
) -> StdResult<OraclePrice> {
let underlying_price = query_router_price(&router, &deps.querier, UNDERLYING_KEY.to_string())?;
let rate = query_rate(deps, env, dshd)?;
Ok(OraclePrice {
key: PRICE_KEY.to_string(),
data: ReferenceData {
rate: (underlying_price.data.rate * rate.data.rate)
/ Uint256::from(exp10(18).as_u128()),
last_updated_base: underlying_price.data.last_updated_base,
last_updated_quote: underlying_price.data.last_updated_quote,
},
})
}

fn query_rate(deps: &Deps, env: &Env, dshd: &Contract) -> StdResult<OraclePrice> {
let staking_info = query_staking_info(&dshd, &deps.querier)?;
let now = env.block.time.seconds();
// normalize from 10^6 to 10^18
Ok(OraclePrice {
key: RATE_KEY.to_string(),
data: ReferenceData {
// price is in utkn (8 decimal SHD), upscaling by 10^10 to get 10^18
rate: Uint256::from(staking_info.price * Uint128::new(exp10(10).as_u128())),
last_updated_base: now,
last_updated_quote: now,
},
})
}

pub fn query_router_price(
router: &Contract,
querier: &QuerierWrapper,
key: impl Into<String>,
) -> StdResult<PriceResponse> {
OracleQuery::GetPrice { key: key.into() }.query(querier, router)
}

pub fn query_staking_info(
dshd: &Contract,
querier: &QuerierWrapper,
) -> StdResult<dshd::StakingInfoResponse> {
match (dshd::QueryMsg::StakingInfo {}.query(querier, dshd)?) {
dshd::QueryResponse::StakingInfo {
unbonding_time,
bonded_shd,
rewards,
total_derivative_token_supply,
price,
fee_info,
status,
} => Ok(dshd::StakingInfoResponse {
unbonding_time,
bonded_shd,
rewards,
total_derivative_token_supply,
price,
fee_info,
status,
}),
}
}
63 changes: 63 additions & 0 deletions contracts/dshd_oracle/src/dshd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{entry_point, Addr, QuerierWrapper, StdError, Storage, Uint128, Uint256};
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};

use shade_oracles::better_secret_math::common::exp10;
use shade_oracles::core::{pad_query_result, validate_admin, AdminPermissions, ResponseStatus};
use shade_oracles::interfaces::common::{OraclePrice, OracleQuery, PriceResponse};
use shade_oracles::interfaces::providers::ReferenceData;
use shade_oracles::ssp::{Item, Map};
use shade_toolkit::{Contract, Query, RawContract, BLOCK_SIZE};

#[cw_serde]
pub enum QueryMsg {
StakingInfo {},
}

impl Query for QueryMsg {
const BLOCK_SIZE: usize = BLOCK_SIZE;
}

#[cw_serde]
pub struct Fee {
pub rate: u32,
pub decimal_places: u8,
}

#[cw_serde]
pub struct FeeInfo {
pub staking: Fee,
pub unbonding: Fee,
pub collector: Addr,
}

#[cw_serde]
pub enum ContractStatusLevel {
NormalRun,
Panicked,
StopAll,
}

#[cw_serde]
pub enum QueryResponse {
StakingInfo {
unbonding_time: Uint128,
bonded_shd: Uint128,
rewards: Uint128,
total_derivative_token_supply: Uint128,
price: Uint128,
fee_info: FeeInfo,
status: ContractStatusLevel,
},
}

#[cw_serde]
pub struct StakingInfoResponse {
pub unbonding_time: Uint128,
pub bonded_shd: Uint128,
pub rewards: Uint128,
pub total_derivative_token_supply: Uint128,
pub price: Uint128,
pub fee_info: FeeInfo,
pub status: ContractStatusLevel,
}
3 changes: 3 additions & 0 deletions contracts/dshd_oracle/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod contract;
pub mod dshd;
pub mod msg;
35 changes: 35 additions & 0 deletions contracts/dshd_oracle/src/msg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use cosmwasm_schema::cw_serde;

use shade_toolkit::{Contract, RawContract};

#[cw_serde]
pub struct Config {
pub router: Contract,
pub dshd: Contract,
pub admin_auth: Contract,
pub enabled: bool,
}

#[cw_serde]
pub struct InstantiateMsg {
pub router: RawContract,
pub dshd: RawContract,
pub admin_auth: RawContract,
}

#[cw_serde]
pub enum ExecuteMsg {
UpdateConfig {
router: Option<RawContract>,
dshd: Option<RawContract>,
admin_auth: Option<RawContract>,
enabled: Option<bool>,
},
}

#[cw_serde]
pub enum QueryMsg {
GetPrice { key: String },
GetPrices { keys: Vec<String> },
GetConfig {},
}
26 changes: 26 additions & 0 deletions contracts/mock_dshd/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "mock_dshd"
version = "0.2.0"
authors = ["Jackson Swenson <[email protected]>"]
edition = "2021"

exclude = [
# Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication.
"contract.wasm",
"hash.txt",
]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = []
# for quicker tests, cargo test --lib

[dependencies]
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
shade-oracles = { path = "../../packages/shade_oracles", features = ["core"] }
shade-toolkit = { workspace = true }
Loading

0 comments on commit 3ed2309

Please sign in to comment.