Skip to content

Commit d706e50

Browse files
authored
Merge pull request #206 from osmosis-labs/trinity/osmosis-price-feed-icq
feat: Using async icq for osmosis price feeder
2 parents 17e76e7 + 44c6e65 commit d706e50

File tree

19 files changed

+269
-413
lines changed

19 files changed

+269
-413
lines changed

Cargo.lock

Lines changed: 3 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ members = [
33
"packages/*",
44
"contracts/provider/*",
55
"contracts/consumer/*",
6-
"contracts/osmosis-price-provider",
76
]
87
resolver = "2"
98

@@ -37,6 +36,7 @@ cw-storage-plus = "1.2.0"
3736
cw-utils = "1.0.3"
3837
cw2 = "1.1.2"
3938
osmosis-std = "0.20.1"
39+
prost = { version = "0.11.0", default-features = false, features = ["prost-derive"] }
4040
schemars = "0.8.17"
4141
serde = { version = "1.0.199", default-features = false, features = ["derive"] }
4242
thiserror = "1.0.59"

contracts/consumer/band-price-feed/src/contract.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ impl RemotePriceFeedContract {
6767
prepare_gas: Uint64,
6868
execute_gas: Uint64,
6969
minimum_sources: u8,
70+
epoch_in_secs: u64,
7071
price_info_ttl_in_secs: u64,
7172
) -> Result<Response, ContractError> {
7273
nonpayable(&ctx.info)?;
@@ -86,6 +87,7 @@ impl RemotePriceFeedContract {
8687
minimum_sources,
8788
},
8889
)?;
90+
self.scheduler.init(&mut ctx.deps, epoch_in_secs)?;
8991
self.price_keeper
9092
.init(&mut ctx.deps, price_info_ttl_in_secs)?;
9193
Ok(Response::new())
@@ -204,6 +206,7 @@ mod tests {
204206
Uint64::new(200000),
205207
1,
206208
60,
209+
60,
207210
)
208211
.unwrap();
209212
}

contracts/consumer/band-price-feed/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use cosmwasm_std::StdError;
22
use cw_utils::PaymentError;
3+
use mesh_apis::ibc::VersionError;
34
use thiserror::Error;
45

56
use mesh_price_feed::PriceKeeperError;
@@ -16,6 +17,9 @@ pub enum ContractError {
1617
#[error("{0}")]
1718
Payment(#[from] PaymentError),
1819

20+
#[error("{0}")]
21+
IbcVersion(#[from] VersionError),
22+
1923
#[error("{0}")]
2024
PriceKeeper(#[from] PriceKeeperError),
2125

contracts/consumer/band-price-feed/src/ibc.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use cosmwasm_std::{
88
StdError, Uint128,
99
};
1010
use cw_band::{OracleResponsePacketData, Output, ResolveStatus};
11-
use mesh_apis::ibc::{ack_fail, ack_success, PriceFeedAck};
11+
use mesh_apis::ibc::{ack_fail, ack_success, validate_channel_order, PriceFeedAck};
1212
use obi::OBIDecode;
1313

1414
use crate::contract::RemotePriceFeedContract;
@@ -28,10 +28,12 @@ pub fn ibc_channel_open(
2828
if contract.channel.may_load(deps.storage)?.is_some() {
2929
return Err(ContractError::IbcChannelAlreadyOpen);
3030
}
31-
// ensure we are called with OpenInit
3231
let channel = msg.channel();
3332
let counterparty_version = msg.counterparty_version();
3433

34+
// verify the ordering is correct
35+
validate_channel_order(&channel.order)?;
36+
3537
if channel.version != IBC_APP_VERSION {
3638
return Err(ContractError::InvalidIbcVersion {
3739
version: channel.version.clone(),
@@ -83,9 +85,8 @@ pub fn ibc_channel_connect(
8385
});
8486
}
8587
}
86-
if channel.order != IbcOrder::Unordered {
87-
return Err(ContractError::OnlyUnorderedChannel {});
88-
}
88+
89+
validate_channel_order(&channel.order)?;
8990

9091
// Version negotiation over, we can only store the channel
9192
let contract = RemotePriceFeedContract::new();

contracts/consumer/osmosis-price-feed/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ cw-storage-plus = { workspace = true }
2828
cw2 = { workspace = true }
2929
cw-utils = { workspace = true }
3030

31+
osmosis-std = { workspace = true }
32+
3133
schemars = { workspace = true }
3234
serde = { workspace = true }
3335
thiserror = { workspace = true }

contracts/consumer/osmosis-price-feed/src/contract.rs

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
1+
use std::vec;
2+
13
use cosmwasm_std::{Decimal, DepsMut, Env, IbcChannel, Response, Timestamp};
24
use cw2::set_contract_version;
35
use cw_storage_plus::Item;
46
use cw_utils::nonpayable;
5-
use sylvia::types::{InstantiateCtx, QueryCtx, SudoCtx};
7+
use mesh_apis::ibc::{encode_request, ibc_query_packet, ArithmeticTwapToNowRequest, CosmosQuery};
8+
use osmosis_std::shim::Timestamp as OsmosisTimestamp;
9+
use osmosis_std::types::tendermint::abci::RequestQuery;
10+
use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, SudoCtx};
611
use sylvia::{contract, schemars};
712

813
use mesh_apis::price_feed_api::{self, PriceFeedApi, PriceResponse};
914

1015
use crate::error::ContractError;
11-
use crate::ibc::{make_ibc_packet, AUTH_ENDPOINT};
12-
use crate::msg::AuthorizedEndpoint;
16+
use crate::ibc::make_ibc_packet;
1317
use crate::state::TradingPair;
1418
use mesh_price_feed::{Action, PriceKeeper, Scheduler};
1519

1620
pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
1721
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
1822

23+
pub const OSMOSIS_QUERY_TWAP_PATH: &str = "/osmosis.twap.v1beta1.Query/ArithmeticTwapToNow";
24+
1925
pub struct RemotePriceFeedContract {
2026
pub channel: Item<'static, IbcChannel>,
2127
pub trading_pair: Item<'static, TradingPair>,
@@ -53,7 +59,6 @@ impl RemotePriceFeedContract {
5359
&self,
5460
mut ctx: InstantiateCtx,
5561
trading_pair: TradingPair,
56-
auth_endpoint: AuthorizedEndpoint,
5762
epoch_in_secs: u64,
5863
price_info_ttl_in_secs: u64,
5964
) -> Result<Response, ContractError> {
@@ -65,12 +70,15 @@ impl RemotePriceFeedContract {
6570
self.price_keeper
6671
.init(&mut ctx.deps, price_info_ttl_in_secs)?;
6772
self.scheduler.init(&mut ctx.deps, epoch_in_secs)?;
68-
69-
AUTH_ENDPOINT.save(ctx.deps.storage, &auth_endpoint)?;
70-
7173
Ok(Response::new())
7274
}
7375

76+
#[sv::msg(exec)]
77+
pub fn request(&self, ctx: ExecCtx) -> Result<Response, ContractError> {
78+
let ExecCtx { deps, env, info: _ } = ctx;
79+
query_twap(deps, &env)
80+
}
81+
7482
pub(crate) fn update_twap(
7583
&self,
7684
deps: DepsMut,
@@ -116,19 +124,39 @@ pub fn query_twap(deps: DepsMut, env: &Env) -> Result<Response, ContractError> {
116124
.may_load(deps.storage)?
117125
.ok_or(ContractError::IbcChannelNotOpen)?;
118126

119-
let packet = mesh_apis::ibc::RemotePriceFeedPacket::QueryTwap {
127+
let request = ArithmeticTwapToNowRequest {
120128
pool_id,
121129
base_asset,
122130
quote_asset,
131+
start_time: Some(OsmosisTimestamp {
132+
seconds: env.block.time.seconds() as i64,
133+
nanos: 0,
134+
}),
135+
};
136+
let packet = CosmosQuery {
137+
requests: vec![RequestQuery {
138+
path: OSMOSIS_QUERY_TWAP_PATH.to_string(),
139+
data: encode_request(&request),
140+
height: 0,
141+
prove: false,
142+
}],
123143
};
124-
let msg = make_ibc_packet(&env.block.time, channel, packet)?;
144+
145+
let msg = make_ibc_packet(&env.block.time, channel, ibc_query_packet(packet))?;
125146

126147
Ok(Response::new().add_message(msg))
127148
}
128149

129150
#[cfg(test)]
130151
mod tests {
131-
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
152+
use cosmwasm_std::{
153+
from_json,
154+
testing::{mock_dependencies, mock_env, mock_info},
155+
Binary,
156+
};
157+
use mesh_apis::ibc::{
158+
decode_response, AcknowledgementResult, CosmosResponse, InterchainQueryPacketAck,
159+
};
132160

133161
use super::*;
134162

@@ -144,10 +172,6 @@ mod tests {
144172
base_asset: "base".to_string(),
145173
quote_asset: "quote".to_string(),
146174
};
147-
let auth_endpoint = AuthorizedEndpoint {
148-
connection_id: "connection".to_string(),
149-
port_id: "port".to_string(),
150-
};
151175

152176
contract
153177
.instantiate(
@@ -157,10 +181,29 @@ mod tests {
157181
info,
158182
},
159183
trading_pair,
160-
auth_endpoint,
161184
10,
162185
50,
163186
)
164187
.unwrap();
165188
}
189+
190+
#[test]
191+
fn json_binary() {
192+
let resp = Binary::from_base64("eyJyZXN1bHQiOiJleUprWVhSaElqb2lRMmhqTmtaUmIxUk5WRUYzVFVSQmQwMUVRWGROUkVGM1RVUkJkMDFFUVhkTlFUMDlJbjA9In0=").unwrap();
193+
194+
let ack_result: AcknowledgementResult = from_json(resp).unwrap();
195+
assert_eq!(
196+
ack_result.result.to_string(),
197+
String::from("eyJkYXRhIjoiQ2hjNkZRb1RNVEF3TURBd01EQXdNREF3TURBd01EQXdNQT09In0=")
198+
);
199+
200+
let packet_ack: InterchainQueryPacketAck = from_json(&ack_result.result).unwrap();
201+
assert_eq!(
202+
packet_ack.data.to_string(),
203+
String::from("Chc6FQoTMTAwMDAwMDAwMDAwMDAwMDAwMA==")
204+
);
205+
206+
let response: CosmosResponse = decode_response(&packet_ack.data).unwrap();
207+
assert_eq!(response.responses.len(), 1);
208+
}
166209
}

contracts/consumer/osmosis-price-feed/src/error.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ pub enum ContractError {
2525
#[error("Invalid authorized endpoint: {0}")]
2626
InvalidEndpoint(String),
2727

28+
#[error("Only supports channel with ibc version icq-1, got {version}")]
29+
InvalidIbcVersion { version: String },
30+
31+
#[error("invalid ibc packet, result should only contains 1 ResponseQuery")]
32+
InvalidResponseQuery,
33+
34+
#[error("failed to send interchain query")]
35+
InvalidResponseQueryCode,
36+
37+
#[error("twap data is empty")]
38+
EmptyTwap,
39+
2840
#[error("Contract doesn't have an open IBC channel")]
2941
IbcChannelNotOpen,
3042

0 commit comments

Comments
 (0)