1use crate::error::ContractError;
2use crate::migration::PRICE_LAST_V100;
3use crate::querier::{query_cumulative_prices, query_prices};
4use crate::state::{
5 get_precision, store_precisions, Config, PriceCumulativeLast, CONFIG, PRICE_LAST,
6};
7use astroport::asset::{Asset, AssetInfo};
8use astroport::oracle::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
9use astroport::pair::TWAP_PRECISION;
10use astroport::querier::query_pair_info;
11
12use cosmwasm_std::{
13 entry_point, to_json_binary, Binary, Decimal256, Deps, DepsMut, Env, MessageInfo, Response,
14 StdError, StdResult, Uint128, Uint256,
15};
16use cw2::{get_contract_version, set_contract_version};
17
18const CONTRACT_NAME: &str = "astroport-oracle";
20const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
22
23pub const PERIOD: u64 = 86400;
25
26#[cfg_attr(not(feature = "library"), entry_point)]
28pub fn instantiate(
29 mut deps: DepsMut,
30 env: Env,
31 info: MessageInfo,
32 msg: InstantiateMsg,
33) -> Result<Response, ContractError> {
34 let factory_contract = deps.api.addr_validate(&msg.factory_contract)?;
35
36 for asset_info in &msg.asset_infos {
37 asset_info.check(deps.api)?;
38 store_precisions(deps.branch(), asset_info, &factory_contract)?;
39 }
40
41 set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
42 let pair_info = query_pair_info(&deps.querier, &factory_contract, &msg.asset_infos)?;
43
44 let config = Config {
45 owner: info.sender,
46 factory: factory_contract,
47 asset_infos: msg.asset_infos,
48 pair: pair_info.clone(),
49 };
50 CONFIG.save(deps.storage, &config)?;
51
52 let prices = query_cumulative_prices(deps.querier, pair_info.contract_addr)?;
53 let average_prices = prices
54 .cumulative_prices
55 .iter()
56 .cloned()
57 .map(|(from, to, _)| (from, to, Decimal256::zero()))
58 .collect();
59
60 let price = PriceCumulativeLast {
61 cumulative_prices: prices.cumulative_prices,
62 average_prices,
63 block_timestamp_last: env.block.time.seconds(),
64 };
65 PRICE_LAST.save(deps.storage, &price)?;
66
67 Ok(Response::default())
68}
69
70#[cfg_attr(not(feature = "library"), entry_point)]
75pub fn execute(
76 deps: DepsMut,
77 env: Env,
78 _info: MessageInfo,
79 msg: ExecuteMsg,
80) -> Result<Response, ContractError> {
81 match msg {
82 ExecuteMsg::Update {} => update(deps, env),
83 }
84}
85
86pub fn update(deps: DepsMut, env: Env) -> Result<Response, ContractError> {
88 let config = CONFIG.load(deps.storage)?;
89 let price_last = PRICE_LAST.load(deps.storage)?;
90
91 let prices = query_cumulative_prices(deps.querier, config.pair.contract_addr)?;
92 let time_elapsed = env.block.time.seconds() - price_last.block_timestamp_last;
93
94 if time_elapsed < PERIOD {
96 return Err(ContractError::WrongPeriod {});
97 }
98
99 let mut average_prices = vec![];
100 for (asset1_last, asset2_last, price_last) in price_last.cumulative_prices.iter() {
101 for (asset1, asset2, price) in prices.cumulative_prices.iter() {
102 if asset1.equal(asset1_last) && asset2.equal(asset2_last) {
103 average_prices.push((
104 asset1.clone(),
105 asset2.clone(),
106 Decimal256::from_ratio(
107 Uint256::from(price.wrapping_sub(*price_last)),
108 time_elapsed,
109 ),
110 ));
111 }
112 }
113 }
114
115 let prices = PriceCumulativeLast {
116 cumulative_prices: prices.cumulative_prices,
117 average_prices,
118 block_timestamp_last: env.block.time.seconds(),
119 };
120 PRICE_LAST.save(deps.storage, &prices)?;
121 Ok(Response::default())
122}
123
124#[cfg_attr(not(feature = "library"), entry_point)]
130pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
131 match msg {
132 QueryMsg::Consult { token, amount } => to_json_binary(&consult(deps, token, amount)?),
133 }
134}
135
136fn consult(
141 deps: Deps,
142 token: AssetInfo,
143 amount: Uint128,
144) -> Result<Vec<(AssetInfo, Uint256)>, StdError> {
145 let config = CONFIG.load(deps.storage)?;
146 let price_last = PRICE_LAST.load(deps.storage)?;
147
148 let mut average_prices = vec![];
149 for (from, to, value) in price_last.average_prices {
150 if from.equal(&token) {
151 average_prices.push((to, value));
152 }
153 }
154
155 if average_prices.is_empty() {
156 return Err(StdError::generic_err("Invalid Token"));
157 }
158
159 let p = get_precision(deps.storage, &token)?;
161 let one = Uint128::new(10_u128.pow(p.into()));
162
163 average_prices
164 .iter()
165 .map(|(asset, price_average)| {
166 if price_average.is_zero() {
167 let price = query_prices(
168 deps.querier,
169 config.pair.contract_addr.clone(),
170 Asset {
171 info: token.clone(),
172 amount: one,
173 },
174 Some(asset.clone()),
175 )?
176 .return_amount;
177 Ok((
178 asset.clone(),
179 Uint256::from(price).multiply_ratio(Uint256::from(amount), Uint256::from(one)),
180 ))
181 } else {
182 let price_precision = Uint256::from(10_u128.pow(TWAP_PRECISION.into()));
183 Ok((
184 asset.clone(),
185 Uint256::from(amount) * *price_average / price_precision,
186 ))
187 }
188 })
189 .collect::<Result<Vec<(AssetInfo, Uint256)>, StdError>>()
190}
191
192#[cfg_attr(not(feature = "library"), entry_point)]
194pub fn migrate(mut deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
195 let contract_version = get_contract_version(deps.storage)?;
196
197 match contract_version.contract.as_ref() {
198 "astroport-oracle" => match contract_version.version.as_ref() {
199 "1.0.0" | "1.0.1" | "1.0.2" => {
200 let config = CONFIG.load(deps.storage)?;
201 let price_last_v100 = PRICE_LAST_V100.load(deps.storage)?;
202
203 let cumulative_prices = vec![
204 (
205 config.asset_infos[0].clone(),
206 config.asset_infos[1].clone(),
207 price_last_v100.price0_cumulative_last,
208 ),
209 (
210 config.asset_infos[1].clone(),
211 config.asset_infos[0].clone(),
212 price_last_v100.price1_cumulative_last,
213 ),
214 ];
215 let average_prices = vec![
216 (
217 config.asset_infos[0].clone(),
218 config.asset_infos[1].clone(),
219 price_last_v100.price_0_average,
220 ),
221 (
222 config.asset_infos[1].clone(),
223 config.asset_infos[0].clone(),
224 price_last_v100.price_1_average,
225 ),
226 ];
227
228 PRICE_LAST.save(
229 deps.storage,
230 &PriceCumulativeLast {
231 cumulative_prices,
232 average_prices,
233 block_timestamp_last: price_last_v100.block_timestamp_last,
234 },
235 )?;
236 for asset_info in &config.asset_infos {
237 store_precisions(deps.branch(), asset_info, &config.factory)?;
238 }
239 }
240 _ => return Err(ContractError::MigrationError {}),
241 },
242 _ => return Err(ContractError::MigrationError {}),
243 }
244
245 set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
246
247 Ok(Response::new()
248 .add_attribute("previous_contract_name", &contract_version.contract)
249 .add_attribute("previous_contract_version", &contract_version.version)
250 .add_attribute("new_contract_name", CONTRACT_NAME)
251 .add_attribute("new_contract_version", CONTRACT_VERSION))
252}