diff --git a/contracts/astroport/oracle/src/contract.rs b/contracts/astroport/oracle/src/contract.rs index 7e25dd52..3d89b72f 100644 --- a/contracts/astroport/oracle/src/contract.rs +++ b/contracts/astroport/oracle/src/contract.rs @@ -7,8 +7,8 @@ use astroport::oracle::{ExecuteMsg, InstantiateMsg, QueryMsg}; use astroport::pair::TWAP_PRECISION; use astroport::querier::{query_pair_info, query_token_precision}; use cosmwasm_std::{ - entry_point, to_binary, Binary, Decimal256, Deps, DepsMut, Env, MessageInfo, Response, - StdError, StdResult, Uint128, Uint256, Uint64, + entry_point, to_binary, Binary, Decimal256, Deps, DepsMut, Env, MessageInfo, Response, Uint128, + Uint256, Uint64, }; use cw2::set_contract_version; use std::ops::Div; @@ -22,40 +22,23 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, - env: Env, + _env: Env, info: MessageInfo, msg: InstantiateMsg, ) -> Result { - msg.asset_infos[0].check(deps.api)?; - msg.asset_infos[1].check(deps.api)?; - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - let factory_contract = addr_validate_to_lower(deps.api, &msg.factory_contract)?; - let pair_info = query_pair_info(&deps.querier, &factory_contract, &msg.asset_infos)?; let config = Config { owner: info.sender, factory: factory_contract, - asset_infos: msg.asset_infos, - pair: pair_info.clone(), + asset_infos: None, + pair: None, period: msg.period, + manager: deps.api.addr_validate(&msg.manager)?, }; CONFIG.save(deps.storage, &config)?; - let prices = query_cumulative_prices(deps.querier, pair_info.contract_addr)?; - let average_prices = prices - .cumulative_prices - .iter() - .cloned() - .map(|(from, to, _)| (from, to, Decimal256::zero())) - .collect(); - let price = PriceCumulativeLast { - cumulative_prices: prices.cumulative_prices, - average_prices, - block_timestamp_last: env.block.time.seconds(), - }; - PRICE_LAST.save(deps.storage, &price, env.block.height)?; LAST_UPDATE_HEIGHT.save(deps.storage, &Uint64::zero())?; Ok(Response::default()) } @@ -74,6 +57,8 @@ pub fn execute( match msg { ExecuteMsg::Update {} => update(deps, env), ExecuteMsg::UpdatePeriod { new_period } => update_period(deps, env, info, new_period), + ExecuteMsg::UpdateManager { new_manager } => update_manager(deps, env, info, new_manager), + ExecuteMsg::SetAssetInfos(asset_infos) => set_asset_infos(deps, env, info, asset_infos), } } @@ -96,12 +81,75 @@ pub fn update_period( .add_attribute("new_period", config.period.to_string())) } +pub fn update_manager( + deps: DepsMut, + _env: Env, + info: MessageInfo, + new_manager: String, +) -> Result { + let mut config = CONFIG.load(deps.storage)?; + if info.sender != config.owner { + return Err(ContractError::Unauthorized {}); + } + + config.manager = deps.api.addr_validate(&new_manager)?; + CONFIG.save(deps.storage, &config)?; + + Ok(Response::new() + .add_attribute("action", "update_manager") + .add_attribute("new_manager", new_manager)) +} + +pub fn set_asset_infos( + deps: DepsMut, + env: Env, + info: MessageInfo, + asset_infos: Vec, +) -> Result { + let config = CONFIG.load(deps.storage)?; + if info.sender != config.manager { + return Err(ContractError::Unauthorized {}); + } + + asset_infos[0].check(deps.api)?; + asset_infos[1].check(deps.api)?; + + let pair_info = query_pair_info(&deps.querier, &config.factory, &asset_infos)?; + + let prices = query_cumulative_prices(deps.querier, &pair_info.contract_addr)?; + let average_prices = prices + .cumulative_prices + .iter() + .cloned() + .map(|(from, to, _)| (from, to, Decimal256::zero())) + .collect(); + let price = PriceCumulativeLast { + cumulative_prices: prices.cumulative_prices, + average_prices, + block_timestamp_last: env.block.time.seconds(), + }; + PRICE_LAST.save(deps.storage, &price, env.block.height)?; + + let config = Config { + owner: config.owner, + factory: config.factory, + asset_infos: Some(asset_infos), + pair: Some(pair_info), + period: config.period, + manager: config.manager, + }; + CONFIG.save(deps.storage, &config)?; + + Ok(Response::default()) +} + /// Updates the local TWAP values for the tokens in the target Astroport pool. pub fn update(deps: DepsMut, env: Env) -> Result { let config = CONFIG.load(deps.storage)?; + let pair = config.pair.ok_or(ContractError::AssetInfosNotSet {})?; let price_last = PRICE_LAST.load(deps.storage)?; - let prices = query_cumulative_prices(deps.querier, config.pair.contract_addr)?; + let prices = query_cumulative_prices(deps.querier, pair.contract_addr)?; let time_elapsed = env.block.time.seconds() - price_last.block_timestamp_last; // Ensure that at least one full period has passed since the last update @@ -141,11 +189,11 @@ pub fn update(deps: DepsMut, env: Env) -> Result { /// * **QueryMsg::Consult { token, amount }** Validates assets and calculates a new average /// amount with updated precision #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result { match msg { - QueryMsg::Consult { token, amount } => to_binary(&consult(deps, token, amount)?), + QueryMsg::Consult { token, amount } => Ok(to_binary(&consult(deps, token, amount)?)?), QueryMsg::TWAPAtHeight { token, height } => { - to_binary(&twap_at_height(deps, token, height)?) + Ok(to_binary(&twap_at_height(deps, token, height)?)?) } } } @@ -158,8 +206,9 @@ fn consult( deps: Deps, token: AssetInfo, amount: Uint128, -) -> Result, StdError> { +) -> Result, ContractError> { let config = CONFIG.load(deps.storage)?; + let pair = config.pair.ok_or(ContractError::AssetInfosNotSet {})?; let price_last = PRICE_LAST.load(deps.storage)?; let mut average_prices = vec![]; @@ -170,7 +219,7 @@ fn consult( } if average_prices.is_empty() { - return Err(StdError::generic_err("Invalid Token")); + return Err(ContractError::InvalidToken {}); } // Get the token's precision @@ -183,7 +232,7 @@ fn consult( if price_average.is_zero() { let price = query_prices( deps.querier, - config.pair.contract_addr.clone(), + pair.contract_addr.clone(), Asset { info: token.clone(), amount: one, @@ -203,7 +252,7 @@ fn consult( )) } }) - .collect::, StdError>>() + .collect::, ContractError>>() } /// Returns token TWAP value for given height. @@ -214,8 +263,9 @@ fn twap_at_height( deps: Deps, token: AssetInfo, height: Uint64, -) -> Result, StdError> { +) -> Result, ContractError> { let config = CONFIG.load(deps.storage)?; + let pair = config.pair.ok_or(ContractError::AssetInfosNotSet {})?; let last_height = LAST_UPDATE_HEIGHT.load(deps.storage)?; let mut query_height = height; // if requested height > last snapshoted time, SnapshotItem.may_load_at_height() will return primary (default) value @@ -235,7 +285,7 @@ fn twap_at_height( } if average_prices.is_empty() { - return Err(StdError::generic_err("Invalid Token")); + return Err(ContractError::InvalidToken {}); } // Get the token's precision @@ -248,7 +298,7 @@ fn twap_at_height( if price_average.is_zero() { let price = query_prices( deps.querier, - config.pair.contract_addr.clone(), + pair.contract_addr.clone(), Asset { info: token.clone(), amount: one, @@ -266,5 +316,5 @@ fn twap_at_height( Ok((asset.clone(), *price_average / price_precision)) } }) - .collect::, StdError>>() + .collect::, ContractError>>() } diff --git a/contracts/astroport/oracle/src/error.rs b/contracts/astroport/oracle/src/error.rs index cd75f9df..a698d7d1 100644 --- a/contracts/astroport/oracle/src/error.rs +++ b/contracts/astroport/oracle/src/error.rs @@ -2,7 +2,7 @@ use cosmwasm_std::StdError; use thiserror::Error; /// This enum describes oracle contract errors -#[derive(Error, Debug)] +#[derive(PartialEq, Error, Debug)] pub enum ContractError { #[error("{0}")] Std(#[from] StdError), @@ -15,4 +15,10 @@ pub enum ContractError { #[error("Contract can't be migrated!")] MigrationError {}, + + #[error("Asset infos are not set")] + AssetInfosNotSet {}, + + #[error("Invalid token")] + InvalidToken {}, } diff --git a/contracts/astroport/oracle/src/state.rs b/contracts/astroport/oracle/src/state.rs index e0d8939f..ee298c8f 100644 --- a/contracts/astroport/oracle/src/state.rs +++ b/contracts/astroport/oracle/src/state.rs @@ -36,9 +36,11 @@ pub struct Config { /// The factory contract address pub factory: Addr, /// The assets in the pool. Each asset is described using a [`AssetInfo`] - pub asset_infos: Vec, + pub asset_infos: Option>, /// Information about the pair (LP token address, pair type etc) - pub pair: PairInfo, + pub pair: Option, /// Time between two consecutive TWAP updates. pub period: u64, + /// Manager is the only one who can set pair info, if not set already + pub manager: Addr, } diff --git a/contracts/astroport/oracle/src/testing.rs b/contracts/astroport/oracle/src/testing.rs index ccc490c3..ee2aae52 100644 --- a/contracts/astroport/oracle/src/testing.rs +++ b/contracts/astroport/oracle/src/testing.rs @@ -1,9 +1,10 @@ use crate::contract::{execute, instantiate}; +use crate::error::ContractError; use crate::mock_querier::mock_dependencies; use astroport::asset::{Asset, AssetInfo}; use astroport::oracle::{ExecuteMsg, InstantiateMsg}; use cosmwasm_std::testing::{mock_env, mock_info}; -use cosmwasm_std::{Addr, Decimal256, Uint128, Uint256}; +use cosmwasm_std::{Addr, Decimal256, DepsMut, Env, MessageInfo, Uint128, Uint256}; use std::ops::Mul; #[test] @@ -52,8 +53,8 @@ fn oracle_overflow() { let instantiate_msg = InstantiateMsg { factory_contract: factory.to_string(), - asset_infos: vec![astro_asset_info, usdc_asset_info], period: 1, + manager: String::from("manager"), }; // Set cumulative price to 192738282u128 @@ -76,6 +77,13 @@ fn oracle_overflow() { ); let res = instantiate(deps.as_mut(), env.clone(), info.clone(), instantiate_msg).unwrap(); assert_eq!(0, res.messages.len()); + execute( + deps.as_mut(), + env.clone(), + mock_info("manager", &[]), + ExecuteMsg::SetAssetInfos(vec![astro_asset_info, usdc_asset_info]), + ) + .unwrap(); // Set cumulative price to 100 (overflow) deps.querier.set_cumulative_price( Addr::unchecked("pair"), @@ -97,3 +105,50 @@ fn oracle_overflow() { env.block.time = env.block.time.plus_seconds(86400); execute(deps.as_mut(), env, info, ExecuteMsg::Update {}).unwrap(); } + +fn setup(deps: DepsMut, env: Env, info: MessageInfo) { + instantiate( + deps, + env, + info, + InstantiateMsg { + factory_contract: String::from("factory"), + period: 0, + manager: String::from("manager"), + }, + ) + .unwrap(); +} + +#[test] +fn update_does_not_work_without_pair_info() { + let mut deps = mock_dependencies(&[]); + let env = mock_env(); + setup(deps.as_mut(), env.clone(), mock_info("dao", &[])); + + for caller in ["someone", "dao"] { + let res = execute( + deps.as_mut(), + env.clone(), + mock_info(caller, &[]), + ExecuteMsg::Update {}, + ) + .unwrap_err(); + assert_eq!(res, ContractError::AssetInfosNotSet {}); + } +} + +#[test] +fn update_period_works_without_pair_info() { + let mut deps = mock_dependencies(&[]); + let env = mock_env(); + setup(deps.as_mut(), env.clone(), mock_info("dao", &[])); + + execute( + deps.as_mut(), + env, + mock_info("dao", &[]), + ExecuteMsg::UpdatePeriod { new_period: 0 }, + ) + .unwrap(); +} diff --git a/contracts/astroport/oracle/tests/integration.rs b/contracts/astroport/oracle/tests/integration.rs index d4f96d81..d8f6cdb6 100644 --- a/contracts/astroport/oracle/tests/integration.rs +++ b/contracts/astroport/oracle/tests/integration.rs @@ -16,6 +16,7 @@ use astroport::factory::{PairConfig, PairType}; use astroport::oracle::QueryMsg::{Consult, TWAPAtHeight}; use astroport::oracle::{ExecuteMsg, InstantiateMsg}; use astroport::pair::StablePoolParams; +use astroport_oracle::error::ContractError; fn mock_app(owner: Option, coins: Option>) -> App { if let (Some(own), Some(coinz)) = ((owner), (coins)) { @@ -547,8 +548,8 @@ fn consult() { let msg = InstantiateMsg { factory_contract: factory_instance.to_string(), - asset_infos, period: 86400, + manager: String::from("manager"), }; let oracle_instance = router .instantiate_contract( @@ -561,6 +562,15 @@ fn consult() { ) .unwrap(); + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); + let e = router .execute_contract( owner.clone(), @@ -679,8 +689,8 @@ fn twap_at_height() { let msg = InstantiateMsg { factory_contract: factory_instance.to_string(), - asset_infos, period: 86400, + manager: String::from("manager"), }; let oracle_instance = router .instantiate_contract( @@ -693,6 +703,15 @@ fn twap_at_height() { ) .unwrap(); + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); + let e = router .execute_contract( owner.clone(), @@ -820,8 +839,8 @@ fn consult_pair_stable() { let msg = InstantiateMsg { factory_contract: factory_instance.to_string(), - asset_infos, period: 86400, + manager: String::from("manager"), }; let oracle_instance = router .instantiate_contract( @@ -834,6 +853,15 @@ fn consult_pair_stable() { ) .unwrap(); + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); + let e = router .execute_contract( owner.clone(), @@ -959,8 +987,8 @@ fn twap_at_height_pair_stable() { let msg = InstantiateMsg { factory_contract: factory_instance.to_string(), - asset_infos, period: 86400, + manager: String::from("manager"), }; let oracle_instance = router .instantiate_contract( @@ -973,6 +1001,15 @@ fn twap_at_height_pair_stable() { ) .unwrap(); + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); + let e = router .execute_contract( owner.clone(), @@ -1149,8 +1186,8 @@ fn consult2() { let msg = InstantiateMsg { factory_contract: factory_instance.to_string(), - asset_infos, period: 86400, + manager: String::from("manager"), }; let oracle_instance = router .instantiate_contract( @@ -1163,6 +1200,15 @@ fn consult2() { ) .unwrap(); + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); + let e = router .execute_contract( owner.clone(), @@ -1364,8 +1410,8 @@ fn consult_zero_price() { router.update_block(next_day); let msg = InstantiateMsg { factory_contract: factory_instance.to_string(), - asset_infos, period: 86400, + manager: String::from("manager"), }; let oracle_instance = router .instantiate_contract( @@ -1378,6 +1424,15 @@ fn consult_zero_price() { ) .unwrap(); + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); + let e = router .execute_contract( owner.clone(), @@ -1436,7 +1491,7 @@ fn consult_zero_price() { ); assert_eq!( res.unwrap_err().to_string(), - "Generic error: Querier contract error: Generic error: Invalid Token" + "Generic error: Querier contract error: Invalid token" ); // Consult zero price @@ -1491,8 +1546,8 @@ fn consult_zero_price() { owner, &InstantiateMsg { factory_contract: factory_instance.to_string(), - asset_infos: asset_infos.clone(), period: 86400, + manager: String::from("manager"), }, &[], String::from("ORACLE 2"), @@ -1500,6 +1555,15 @@ fn consult_zero_price() { ) .unwrap(); + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos.clone()), + &[], + ) + .unwrap(); + let res: Vec<(AssetInfo, Uint128)> = router .wrap() .query_wasm_smart( @@ -1603,8 +1667,8 @@ fn consult_multiple_assets() { let msg = InstantiateMsg { factory_contract: factory_instance.to_string(), - asset_infos, period: 86400, + manager: String::from("manager"), }; let oracle_instance = router .instantiate_contract( @@ -1617,6 +1681,15 @@ fn consult_multiple_assets() { ) .unwrap(); + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); + let e = router .execute_contract( owner.clone(), @@ -1945,8 +2018,8 @@ fn twap_at_height_multiple_assets() { let msg = InstantiateMsg { factory_contract: factory_instance.to_string(), - asset_infos, period: 86400, + manager: String::from("manager"), }; let oracle_instance = router .instantiate_contract( @@ -1959,6 +2032,15 @@ fn twap_at_height_multiple_assets() { ) .unwrap(); + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); + let e = router .execute_contract( owner.clone(), @@ -2356,8 +2438,8 @@ fn twap_at_height_multiple_assets_non_accurate_heights() { let msg = InstantiateMsg { factory_contract: factory_instance.to_string(), - asset_infos, period: 86400, + manager: String::from("manager"), }; let oracle_instance = router .instantiate_contract( @@ -2370,6 +2452,15 @@ fn twap_at_height_multiple_assets_non_accurate_heights() { ) .unwrap(); + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); + let e = router .execute_contract( owner.clone(), @@ -2677,3 +2768,380 @@ fn twap_at_height_multiple_assets_non_accurate_heights() { assert_eq!(res, amount_exp); } } + +#[test] +fn contract_works_after_pair_info_is_set() { + let mut router = mock_app(None, None); + let owner = Addr::unchecked("dao"); + let user = Addr::unchecked("user"); + let (astro_token_instance, factory_instance, oracle_code_id) = + instantiate_contracts(&mut router, owner.clone()); + let usdc_token_instance = instantiate_token( + &mut router, + owner.clone(), + "Usdc token".to_string(), + "USDC".to_string(), + ); + + let asset_infos = vec![ + AssetInfo::Token { + contract_addr: usdc_token_instance.clone(), + }, + AssetInfo::Token { + contract_addr: astro_token_instance.clone(), + }, + ]; + + let assets = vec![ + Asset { + info: asset_infos[0].clone(), + amount: Uint128::from(100_000_u128), + }, + Asset { + info: asset_infos[1].clone(), + amount: Uint128::from(100_000_u128), + }, + ]; + + let pair_info = create_pair( + &mut router, + owner.clone(), + user.clone(), + &factory_instance, + assets.clone(), + ); + provide_liquidity(&mut router, owner.clone(), user.clone(), &pair_info, assets).unwrap(); + + router.update_block(next_day); + let pair_info: PairInfo = router + .wrap() + .query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: factory_instance.clone().to_string(), + msg: to_binary(&astroport::factory::QueryMsg::Pair { + asset_infos: asset_infos.clone(), + }) + .unwrap(), + })) + .unwrap(); + + change_provide_liquidity( + &mut router, + owner.clone(), + user.clone(), + pair_info.contract_addr.clone(), + vec![ + (astro_token_instance.clone(), Uint128::from(50_000_u128)), + (usdc_token_instance.clone(), Uint128::from(50_000_u128)), + ], + ); + router.update_block(next_day); + + let msg = InstantiateMsg { + factory_contract: factory_instance.to_string(), + period: 86400, + manager: String::from("manager"), + }; + let oracle_instance = router + .instantiate_contract( + oracle_code_id, + owner.clone(), + &msg, + &[], + String::from("ORACLE"), + None, + ) + .unwrap(); + + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); + + let e = router + .execute_contract( + owner.clone(), + oracle_instance.clone(), + &ExecuteMsg::Update {}, + &[], + ) + .unwrap_err(); + assert_eq!(e.root_cause().to_string(), "Period not elapsed",); + router.update_block(next_day); + + // Change pair liquidity + change_provide_liquidity( + &mut router, + owner.clone(), + user, + pair_info.contract_addr, + vec![ + (astro_token_instance.clone(), Uint128::from(10_000_u128)), + (usdc_token_instance.clone(), Uint128::from(10_000_u128)), + ], + ); + router.update_block(next_day); + router + .execute_contract(owner, oracle_instance.clone(), &ExecuteMsg::Update {}, &[]) + .unwrap(); + + for (addr, amount) in [ + (astro_token_instance, Uint128::from(1000u128)), + (usdc_token_instance, Uint128::from(100u128)), + ] { + let msg = Consult { + token: AssetInfo::Token { + contract_addr: addr, + }, + amount, + }; + let res: Vec<(AssetInfo, Uint128)> = router + .wrap() + .query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: oracle_instance.to_string(), + msg: to_binary(&msg).unwrap(), + })) + .unwrap(); + assert_eq!(res[0].1, amount); + } +} + +#[test] +fn only_manager_can_set_pair_info() { + let mut router = mock_app(None, None); + let owner = Addr::unchecked("dao"); + let user = Addr::unchecked("user"); + let (astro_token_instance, factory_instance, oracle_code_id) = + instantiate_contracts(&mut router, owner.clone()); + let usdc_token_instance = instantiate_token( + &mut router, + owner.clone(), + "Usdc token".to_string(), + "USDC".to_string(), + ); + + let asset_infos = vec![ + AssetInfo::Token { + contract_addr: usdc_token_instance.clone(), + }, + AssetInfo::Token { + contract_addr: astro_token_instance.clone(), + }, + ]; + + let assets = vec![ + Asset { + info: asset_infos[0].clone(), + amount: Uint128::from(100_000_u128), + }, + Asset { + info: asset_infos[1].clone(), + amount: Uint128::from(100_000_u128), + }, + ]; + + let pair_info = create_pair( + &mut router, + owner.clone(), + user.clone(), + &factory_instance, + assets.clone(), + ); + provide_liquidity(&mut router, owner.clone(), user.clone(), &pair_info, assets).unwrap(); + + router.update_block(next_day); + let pair_info: PairInfo = router + .wrap() + .query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: factory_instance.clone().to_string(), + msg: to_binary(&astroport::factory::QueryMsg::Pair { + asset_infos: asset_infos.clone(), + }) + .unwrap(), + })) + .unwrap(); + + change_provide_liquidity( + &mut router, + owner.clone(), + user, + pair_info.contract_addr, + vec![ + (astro_token_instance, Uint128::from(50_000_u128)), + (usdc_token_instance, Uint128::from(50_000_u128)), + ], + ); + router.update_block(next_day); + + let msg = InstantiateMsg { + factory_contract: factory_instance.to_string(), + period: 86400, + manager: String::from("manager"), + }; + let oracle_instance = router + .instantiate_contract( + oracle_code_id, + owner, + &msg, + &[], + String::from("ORACLE"), + None, + ) + .unwrap(); + + let res = router + .execute_contract( + Addr::unchecked("someone"), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos.clone()), + &[], + ) + .unwrap_err() + .downcast::() + .unwrap(); + assert_eq!(res, ContractError::Unauthorized {}); + + router + .execute_contract( + Addr::unchecked("manager"), + oracle_instance, + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); +} + +#[test] +fn only_owner_can_change_manager() { + let mut router = mock_app(None, None); + let owner = Addr::unchecked("dao"); + let user = Addr::unchecked("user"); + let (astro_token_instance, factory_instance, oracle_code_id) = + instantiate_contracts(&mut router, owner.clone()); + let usdc_token_instance = instantiate_token( + &mut router, + owner.clone(), + "Usdc token".to_string(), + "USDC".to_string(), + ); + + let asset_infos = vec![ + AssetInfo::Token { + contract_addr: usdc_token_instance.clone(), + }, + AssetInfo::Token { + contract_addr: astro_token_instance.clone(), + }, + ]; + + let assets = vec![ + Asset { + info: asset_infos[0].clone(), + amount: Uint128::from(100_000_u128), + }, + Asset { + info: asset_infos[1].clone(), + amount: Uint128::from(100_000_u128), + }, + ]; + + let pair_info = create_pair( + &mut router, + owner.clone(), + user.clone(), + &factory_instance, + assets.clone(), + ); + provide_liquidity(&mut router, owner.clone(), user.clone(), &pair_info, assets).unwrap(); + + router.update_block(next_day); + let pair_info: PairInfo = router + .wrap() + .query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: factory_instance.clone().to_string(), + msg: to_binary(&astroport::factory::QueryMsg::Pair { + asset_infos: asset_infos.clone(), + }) + .unwrap(), + })) + .unwrap(); + + change_provide_liquidity( + &mut router, + owner.clone(), + user, + pair_info.contract_addr, + vec![ + (astro_token_instance, Uint128::from(50_000_u128)), + (usdc_token_instance, Uint128::from(50_000_u128)), + ], + ); + router.update_block(next_day); + + let msg = InstantiateMsg { + factory_contract: factory_instance.to_string(), + period: 86400, + manager: String::from("manager1"), + }; + let oracle_instance = router + .instantiate_contract( + oracle_code_id, + owner, + &msg, + &[], + String::from("ORACLE"), + None, + ) + .unwrap(); + + let res = router + .execute_contract( + Addr::unchecked("someone"), + oracle_instance.clone(), + &ExecuteMsg::UpdateManager { + new_manager: String::from("someone"), + }, + &[], + ) + .unwrap_err() + .downcast::() + .unwrap(); + assert_eq!(res, ContractError::Unauthorized {}); + + router + .execute_contract( + Addr::unchecked("dao"), + oracle_instance.clone(), + &ExecuteMsg::UpdateManager { + new_manager: String::from("manager2"), + }, + &[], + ) + .unwrap(); + + for caller in ["someone", "manager1"] { + let res = router + .execute_contract( + Addr::unchecked(caller), + oracle_instance.clone(), + &ExecuteMsg::SetAssetInfos(asset_infos.clone()), + &[], + ) + .unwrap_err() + .downcast::() + .unwrap(); + assert_eq!(res, ContractError::Unauthorized {}); + } + + router + .execute_contract( + Addr::unchecked("manager2"), + oracle_instance, + &ExecuteMsg::SetAssetInfos(asset_infos), + &[], + ) + .unwrap(); +} diff --git a/contracts/vesting-lp/schema/raw/execute.json b/contracts/vesting-lp/schema/raw/execute.json index 19fba561..7fd8e6ec 100644 --- a/contracts/vesting-lp/schema/raw/execute.json +++ b/contracts/vesting-lp/schema/raw/execute.json @@ -184,6 +184,7 @@ "additionalProperties": false }, { + "description": "Sets the vesting token", "type": "object", "required": [ "set_vesting_token" diff --git a/contracts/vesting-lp/schema/raw/instantiate.json b/contracts/vesting-lp/schema/raw/instantiate.json index 081844ef..044bc78c 100644 --- a/contracts/vesting-lp/schema/raw/instantiate.json +++ b/contracts/vesting-lp/schema/raw/instantiate.json @@ -14,7 +14,7 @@ "type": "string" }, "token_info_manager": { - "description": "Initial list of whitelisted vesting managers", + "description": "Token info manager address", "type": "string" }, "vesting_managers": { diff --git a/contracts/vesting-lp/schema/vesting-lp.json b/contracts/vesting-lp/schema/vesting-lp.json index 70d23f1e..d1c51a13 100644 --- a/contracts/vesting-lp/schema/vesting-lp.json +++ b/contracts/vesting-lp/schema/vesting-lp.json @@ -18,7 +18,7 @@ "type": "string" }, "token_info_manager": { - "description": "Initial list of whitelisted vesting managers", + "description": "Token info manager address", "type": "string" }, "vesting_managers": { @@ -217,6 +217,7 @@ "additionalProperties": false }, { + "description": "Sets the vesting token", "type": "object", "required": [ "set_vesting_token" diff --git a/contracts/vesting-managed/schema/raw/execute.json b/contracts/vesting-managed/schema/raw/execute.json index 19fba561..7fd8e6ec 100644 --- a/contracts/vesting-managed/schema/raw/execute.json +++ b/contracts/vesting-managed/schema/raw/execute.json @@ -184,6 +184,7 @@ "additionalProperties": false }, { + "description": "Sets the vesting token", "type": "object", "required": [ "set_vesting_token" diff --git a/contracts/vesting-managed/schema/raw/instantiate.json b/contracts/vesting-managed/schema/raw/instantiate.json index 081844ef..044bc78c 100644 --- a/contracts/vesting-managed/schema/raw/instantiate.json +++ b/contracts/vesting-managed/schema/raw/instantiate.json @@ -14,7 +14,7 @@ "type": "string" }, "token_info_manager": { - "description": "Initial list of whitelisted vesting managers", + "description": "Token info manager address", "type": "string" }, "vesting_managers": { diff --git a/contracts/vesting-managed/schema/vesting-managed.json b/contracts/vesting-managed/schema/vesting-managed.json index 0378039a..4c061ada 100644 --- a/contracts/vesting-managed/schema/vesting-managed.json +++ b/contracts/vesting-managed/schema/vesting-managed.json @@ -18,7 +18,7 @@ "type": "string" }, "token_info_manager": { - "description": "Initial list of whitelisted vesting managers", + "description": "Token info manager address", "type": "string" }, "vesting_managers": { @@ -217,6 +217,7 @@ "additionalProperties": false }, { + "description": "Sets the vesting token", "type": "object", "required": [ "set_vesting_token" diff --git a/packages/astroport/src/oracle.rs b/packages/astroport/src/oracle.rs index 5f85e4c4..d5ca1b42 100644 --- a/packages/astroport/src/oracle.rs +++ b/packages/astroport/src/oracle.rs @@ -8,10 +8,10 @@ use cosmwasm_std::{Decimal256, Uint128, Uint256, Uint64}; pub struct InstantiateMsg { /// The factory contract address pub factory_contract: String, - /// The assets that have a pool for which this contract provides price feeds - pub asset_infos: Vec, /// Minimal interval between Update{}'s pub period: u64, + /// Manager is the only one who can set pair info, if not set already + pub manager: String, } /// This structure describes the execute functions available in the contract. @@ -21,6 +21,11 @@ pub enum ExecuteMsg { Update {}, /// Update period UpdatePeriod { new_period: u64 }, + /// Set a new manager, only owner can use this message + UpdateManager { new_manager: String }, + /// Set asset infos that have a pool for which this contract provides price feeds. + /// Only manager can use this message + SetAssetInfos(Vec), } /// This structure describes the query messages available in the contract.