diff --git a/contracts/margined_insurance_fund/src/handle.rs b/contracts/margined_insurance_fund/src/handle.rs index 1fceceda..84169952 100644 --- a/contracts/margined_insurance_fund/src/handle.rs +++ b/contracts/margined_insurance_fund/src/handle.rs @@ -80,7 +80,9 @@ pub fn shutdown_all_vamm(deps: DepsMut, env: Env, info: MessageInfo) -> StdResul msgs.push(execute_vamm_shutdown(vamm.clone())?); } - Ok(Response::default().add_submessages(msgs)) + Ok(Response::default() + .add_submessages(msgs) + .add_attributes(vec![("action", "vamm_shutdown")])) } pub fn withdraw( diff --git a/contracts/margined_vamm/src/contract.rs b/contracts/margined_vamm/src/contract.rs index 853fa07a..3b8235bb 100644 --- a/contracts/margined_vamm/src/contract.rs +++ b/contracts/margined_vamm/src/contract.rs @@ -11,8 +11,8 @@ use margined_common::{ }; use margined_perp::margined_vamm::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use crate::error::ContractError; use crate::querier::{query_underlying_price, query_underlying_twap_price}; +use crate::{error::ContractError, handle::rebase_vamm}; use crate::{ handle::{set_open, settle_funding, swap_input, swap_output, update_config, update_owner}, query::{ @@ -170,6 +170,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ), ExecuteMsg::SettleFunding {} => settle_funding(deps, env, info), ExecuteMsg::SetOpen { open } => set_open(deps, env, info, open), + ExecuteMsg::RebaseVamm {} => rebase_vamm(deps, info, env), } } diff --git a/contracts/margined_vamm/src/handle.rs b/contracts/margined_vamm/src/handle.rs index aed6748c..51579f7f 100644 --- a/contracts/margined_vamm/src/handle.rs +++ b/contracts/margined_vamm/src/handle.rs @@ -9,7 +9,7 @@ use crate::{ contract::{ ONE_DAY_IN_SECONDS, ONE_HOUR_IN_SECONDS, ONE_MINUTE_IN_SECONDS, ONE_WEEK_IN_SECONDS, OWNER, }, - querier::query_underlying_twap_price, + querier::{query_underlying_price, query_underlying_twap_price}, query::query_twap_price, state::{read_config, read_state, store_config, store_state, Config, State}, utils::{ @@ -129,6 +129,37 @@ pub fn set_open(deps: DepsMut, env: Env, info: MessageInfo, open: bool) -> StdRe Ok(Response::new().add_attribute("action", "set_open")) } +// This function will rebase the vamm according to the current oracle price +pub fn rebase_vamm(deps: DepsMut, info: MessageInfo, env: Env) -> StdResult { + let config: Config = read_config(deps.storage)?; + let mut state: State = read_state(deps.storage)?; + + if !OWNER.is_admin(deps.as_ref(), &info.sender)? { + return Err(StdError::generic_err("unauthorized")); + } + + let oracle_price = query_underlying_price(&deps.as_ref())?; + + // let P be oracle price, Q and B_old be current quote and base asset reserves + // B_new is the final base_asset_reserve we want. + // Current price is Q / B_old, P = Q / B_new + // Therefore B_new = Q / P + + state.base_asset_reserve = state + .quote_asset_reserve + .checked_mul(config.decimals)? + .checked_div(oracle_price)?; + + store_state(deps.storage, &state)?; + + Ok(Response::new().add_attributes(vec![ + ("action", "rebase_vamm"), + ("vamm", env.contract.address.as_ref()), + ("new_base_asset", &state.base_asset_reserve.to_string()), + ("new_price", &oracle_price.to_string()), + ])) +} + // Function should only be called by the margin engine pub fn swap_input( deps: DepsMut, diff --git a/contracts/margined_vamm/src/testing/get_price_tests.rs b/contracts/margined_vamm/src/testing/get_price_tests.rs index 203bc0b9..02bf5b11 100644 --- a/contracts/margined_vamm/src/testing/get_price_tests.rs +++ b/contracts/margined_vamm/src/testing/get_price_tests.rs @@ -2,8 +2,9 @@ use crate::contract::{instantiate, query}; use crate::handle::{get_input_price_with_reserves, get_output_price_with_reserves}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{from_binary, Uint128}; +use cw_multi_test::Executor; use margined_perp::margined_vamm::{Direction, InstantiateMsg, QueryMsg, StateResponse}; -use margined_utils::scenarios::to_decimals; +use margined_utils::scenarios::{to_decimals, SimpleScenario}; /// Unit tests #[test] @@ -329,3 +330,31 @@ fn test_get_input_and_output_price_with_reserves() { .unwrap(); assert_eq!(result, to_decimals(600)); } + +#[test] +fn test_rebase_vamm() { + let SimpleScenario { + mut router, + owner, + vamm, + pricefeed, + .. + } = SimpleScenario::new(); + + let spot_price = vamm.spot_price(&router).unwrap(); + assert_eq!(spot_price, to_decimals(10u64)); + + let price: Uint128 = Uint128::from(20u128); + let timestamp: u64 = 1_000_000_000; + + let msg = pricefeed + .append_price("ETH".to_string(), price, timestamp) + .unwrap(); + router.execute(owner.clone(), msg).unwrap(); + + let msg = vamm.rebase_vamm().unwrap(); + router.execute(owner.clone(), msg).unwrap(); + + let new_spot_price = vamm.spot_price(&router).unwrap(); + assert_eq!(new_spot_price, Uint128::from(20u64)); +} diff --git a/packages/margined_perp/src/margined_vamm.rs b/packages/margined_perp/src/margined_vamm.rs index 9f0f1ae5..d9073d7f 100644 --- a/packages/margined_perp/src/margined_vamm.rs +++ b/packages/margined_perp/src/margined_vamm.rs @@ -62,6 +62,7 @@ pub enum ExecuteMsg { SetOpen { open: bool, }, + RebaseVamm {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] diff --git a/packages/margined_utils/src/contracts/helpers/margined_vamm.rs b/packages/margined_utils/src/contracts/helpers/margined_vamm.rs index c21d8309..7b5b417f 100644 --- a/packages/margined_utils/src/contracts/helpers/margined_vamm.rs +++ b/packages/margined_utils/src/contracts/helpers/margined_vamm.rs @@ -61,6 +61,11 @@ impl VammController { self.call(msg, vec![]) } + pub fn rebase_vamm(&self) -> StdResult { + let msg = ExecuteMsg::RebaseVamm {}; + self.call(msg, vec![]) + } + pub fn set_toll_ratio(&self, toll_ratio: Uint128) -> StdResult { let msg = ExecuteMsg::UpdateConfig { base_asset_holding_cap: None,