-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: band price feeder #195
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
6efb90d
init band oracle price feed
vuong177 a9b8ac4
rename remote-price-feeder to osmosis-price-feeder and move scheduler…
trinitys7 f3da484
Make Band price feeder works with PriceFeedApi
trinitys7 8e79686
rename scheduler to price-feed and move PriceKeeder to price-feed
trinitys7 c0780ce
lint
trinitys7 073dae6
minor
vuong177 79bd951
Merge branch 'main' into vuong/band-oracle
vuong177 da560f3
Use the correct logic for ibc channel open and connect
trinitys7 815e888
Fix band error on ack
trinitys7 9645e32
Remove redundant query
trinitys7 abe70bf
lint
trinitys7 9e07070
lint
trinitys7 f51b2c6
lint
trinitys7 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
[package] | ||
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-price-feed = { 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 = { 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 |
2 changes: 1 addition & 1 deletion
2
...sumer/remote-price-feed/src/bin/schema.rs → ...onsumer/band-price-feed/src/bin/schema.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
use cosmwasm_std::{ | ||
to_json_binary, Binary, Coin, DepsMut, Env, IbcChannel, 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::state::{Config, TradingPair}; | ||
|
||
use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, SudoCtx}; | ||
use sylvia::{contract, schemars}; | ||
|
||
use cw_band::{Input, OracleRequestPacketData}; | ||
use mesh_price_feed::{Action, PriceKeeper, Scheduler}; | ||
use obi::enc::OBIEncode; | ||
|
||
// Version info for migration | ||
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<Box<dyn Action<ContractError>>, ContractError>, | ||
} | ||
|
||
impl Default for RemotePriceFeedContract { | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} | ||
|
||
#[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, | ||
mut ctx: InstantiateCtx, | ||
trading_pair: TradingPair, | ||
client_id: String, | ||
oracle_script_id: Uint64, | ||
ask_count: Uint64, | ||
min_count: Uint64, | ||
fee_limit: Vec<Coin>, | ||
prepare_gas: Uint64, | ||
execute_gas: Uint64, | ||
minimum_sources: u8, | ||
price_info_ttl_in_secs: u64, | ||
) -> Result<Response, ContractError> { | ||
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, | ||
oracle_script_id, | ||
ask_count, | ||
min_count, | ||
fee_limit, | ||
prepare_gas, | ||
execute_gas, | ||
minimum_sources, | ||
}, | ||
)?; | ||
self.price_keeper | ||
.init(&mut ctx.deps, price_info_ttl_in_secs)?; | ||
Ok(Response::new()) | ||
} | ||
|
||
#[sv::msg(exec)] | ||
pub fn request(&self, ctx: ExecCtx) -> Result<Response, ContractError> { | ||
let ExecCtx { deps, env, info: _ } = ctx; | ||
try_request(deps, &env) | ||
} | ||
} | ||
|
||
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<PriceResponse, Self::Error> { | ||
Ok(self | ||
.price_keeper | ||
.price(ctx.deps, &ctx.env) | ||
.map(|rate| PriceResponse { | ||
native_per_foreign: rate, | ||
})?) | ||
} | ||
|
||
fn handle_epoch(&self, ctx: SudoCtx) -> Result<Response, Self::Error> { | ||
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) -> Result<Response, ContractError> { | ||
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: vec![base_asset, quote_asset], | ||
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: channel.endpoint.channel_id, | ||
data: to_json_binary(&packet)?, | ||
timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(60)), | ||
})) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use cosmwasm_std::{ | ||
testing::{mock_dependencies, mock_env, mock_info}, | ||
Uint128, Uint64, | ||
}; | ||
|
||
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(), | ||
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, | ||
60, | ||
) | ||
.unwrap(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
use cosmwasm_std::StdError; | ||
use cw_utils::PaymentError; | ||
use thiserror::Error; | ||
|
||
use mesh_price_feed::PriceKeeperError; | ||
|
||
/// 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("{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("Only supports unordered channel")] | ||
OnlyUnorderedChannel {}, | ||
|
||
#[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("Invalid price, must be greater than 0.0")] | ||
InvalidPrice, | ||
|
||
#[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. | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to move this file or simply copy it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see other files were moved too, did you kill the old
remote-price-feed
contract? Fine if you did, just make sure it's cleaned up. ❤️There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just move the schema.rs file into correct folder. I don't remove anything.