From 721c451b4664b18f035decdb3891a6c0b1a5144c Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Tue, 25 Jul 2023 13:00:53 +0100 Subject: [PATCH 01/27] add credit manager to address provider --- packages/types/src/address_provider.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/types/src/address_provider.rs b/packages/types/src/address_provider.rs index bbcb134a5..9d6a6ea2f 100644 --- a/packages/types/src/address_provider.rs +++ b/packages/types/src/address_provider.rs @@ -34,6 +34,7 @@ pub enum MarsAddressType { SafetyFund, /// The swapper contract on the chain Swapper, + CreditManager, } impl fmt::Display for MarsAddressType { @@ -48,6 +49,7 @@ impl fmt::Display for MarsAddressType { MarsAddressType::RewardsCollector => "rewards_collector", MarsAddressType::SafetyFund => "safety_fund", MarsAddressType::Swapper => "swapper", + MarsAddressType::CreditManager => "credit_manager", }; write!(f, "{s}") } From fd4f8d626092d2f62b3c875686bcf7ae48e9ba55 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Tue, 25 Jul 2023 13:50:27 +0100 Subject: [PATCH 02/27] extract interest rate logics to a separate library --- Cargo.lock | 11 + Cargo.toml | 2 + contracts/red-bank/Cargo.toml | 1 + contracts/red-bank/src/execute.rs | 9 +- contracts/red-bank/src/health.rs | 2 +- contracts/red-bank/src/interest_rates.rs | 232 +-------------------- contracts/red-bank/src/query.rs | 8 +- contracts/red-bank/tests/helpers.rs | 8 +- contracts/red-bank/tests/test_admin.rs | 2 +- contracts/red-bank/tests/test_borrow.rs | 8 +- contracts/red-bank/tests/test_deposit.rs | 2 +- contracts/red-bank/tests/test_liquidate.rs | 8 +- contracts/red-bank/tests/test_misc.rs | 8 +- contracts/red-bank/tests/test_query.rs | 2 +- contracts/red-bank/tests/test_withdraw.rs | 8 +- packages/interest-rate/Cargo.toml | 25 +++ packages/interest-rate/src/lib.rs | 222 ++++++++++++++++++++ 17 files changed, 299 insertions(+), 259 deletions(-) create mode 100644 packages/interest-rate/Cargo.toml create mode 100644 packages/interest-rate/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 7c3d55765..e08f94d9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1822,6 +1822,16 @@ dependencies = [ "serde", ] +[[package]] +name = "mars-interest-rate" +version = "1.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "mars-red-bank-types 1.1.0", + "mars-utils 1.1.0", +] + [[package]] name = "mars-oracle-base" version = "1.1.0" @@ -1943,6 +1953,7 @@ dependencies = [ "cw-utils 1.0.1", "cw2 1.1.0", "mars-health", + "mars-interest-rate", "mars-owner", "mars-params 1.0.2", "mars-red-bank-types 1.1.0", diff --git a/Cargo.toml b/Cargo.toml index f80c4770a..c4facf73f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "contracts/rewards-collector", "packages/chains/*", "packages/health", + "packages/interest-rate", "packages/testing", "packages/types", "packages/utils", @@ -66,6 +67,7 @@ proptest = "1.1.0" # packages mars-health = { version = "1.0.0", path = "./packages/health" } +mars-interest-rate = { version = "1.0.0", path = "./packages/interest-rate" } mars-osmosis = { version = "1.0.0", path = "./packages/chains/osmosis" } mars-params = "=1.0.2" mars-red-bank-types = { version = "1.0.0", path = "./packages/types" } diff --git a/contracts/red-bank/Cargo.toml b/contracts/red-bank/Cargo.toml index 56de49c9f..9a17a1d37 100644 --- a/contracts/red-bank/Cargo.toml +++ b/contracts/red-bank/Cargo.toml @@ -27,6 +27,7 @@ cw2 = { workspace = true } cw-storage-plus = { workspace = true } cw-utils = { workspace = true } mars-health = { workspace = true } +mars-interest-rate = { workspace = true } mars-owner = { workspace = true } mars-params = { workspace = true } mars-red-bank-types = { workspace = true } diff --git a/contracts/red-bank/src/execute.rs b/contracts/red-bank/src/execute.rs index f3f0919b5..c87806682 100644 --- a/contracts/red-bank/src/execute.rs +++ b/contracts/red-bank/src/execute.rs @@ -3,6 +3,10 @@ use std::{cmp::min, str}; use cosmwasm_std::{ Addr, Decimal, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Uint128, }; +use mars_interest_rate::{ + get_scaled_debt_amount, get_scaled_liquidity_amount, get_underlying_debt_amount, + get_underlying_liquidity_amount, +}; use mars_owner::{OwnerInit::SetInitialOwner, OwnerUpdate}; use mars_params::types::AssetParams; use mars_red_bank_types::{ @@ -24,10 +28,7 @@ use crate::{ assert_liquidatable, }, helpers::{query_asset_params, query_close_factor}, - interest_rates::{ - apply_accumulated_interests, get_scaled_debt_amount, get_scaled_liquidity_amount, - get_underlying_debt_amount, get_underlying_liquidity_amount, update_interest_rates, - }, + interest_rates::{apply_accumulated_interests, update_interest_rates}, state::{COLLATERALS, CONFIG, DEBTS, MARKETS, OWNER, UNCOLLATERALIZED_LOAN_LIMITS}, user::User, }; diff --git a/contracts/red-bank/src/health.rs b/contracts/red-bank/src/health.rs index bce9c0fd1..9655bb336 100644 --- a/contracts/red-bank/src/health.rs +++ b/contracts/red-bank/src/health.rs @@ -2,12 +2,12 @@ use std::collections::{HashMap, HashSet}; use cosmwasm_std::{Addr, Deps, Env, Order, StdError, StdResult, Uint128}; use mars_health::health::{Health, Position as HealthPosition}; +use mars_interest_rate::{get_underlying_debt_amount, get_underlying_liquidity_amount}; use mars_red_bank_types::{oracle, red_bank::Position}; use crate::{ error::ContractError, helpers::query_asset_params, - interest_rates::{get_underlying_debt_amount, get_underlying_liquidity_amount}, state::{COLLATERALS, DEBTS, MARKETS}, }; diff --git a/contracts/red-bank/src/interest_rates.rs b/contracts/red-bank/src/interest_rates.rs index ce01a6a4f..980fd9a29 100644 --- a/contracts/red-bank/src/interest_rates.rs +++ b/contracts/red-bank/src/interest_rates.rs @@ -1,16 +1,14 @@ use std::str; -use cosmwasm_std::{Addr, Decimal, Env, Event, Response, StdError, StdResult, Storage, Uint128}; +use cosmwasm_std::{Addr, Decimal, Env, Event, Response, StdResult, Storage, Uint128}; +use mars_interest_rate::{ + calculate_applied_linear_interest_rate, compute_scaled_amount, compute_underlying_amount, + get_underlying_debt_amount, get_underlying_liquidity_amount, ScalingOperation, +}; use mars_red_bank_types::red_bank::Market; -use mars_utils::math; use crate::{error::ContractError, user::User}; -/// Scaling factor used to keep more precision during division / multiplication by index. -pub const SCALING_FACTOR: Uint128 = Uint128::new(1_000_000); - -const SECONDS_PER_YEAR: u64 = 31536000u64; - /// Calculates accumulated interest for the time between last time market index was updated /// and current block. /// Applies desired side effects: @@ -95,181 +93,6 @@ pub fn apply_accumulated_interests( Ok(response) } -pub fn calculate_applied_linear_interest_rate( - index: Decimal, - rate: Decimal, - time_elapsed: u64, -) -> StdResult { - let rate_factor = rate.checked_mul(Decimal::from_ratio( - Uint128::from(time_elapsed), - Uint128::from(SECONDS_PER_YEAR), - ))?; - index.checked_mul(Decimal::one() + rate_factor).map_err(StdError::from) -} - -/// Get scaled liquidity amount from an underlying amount, a Market and timestamp in seconds -/// Liquidity amounts are always truncated to make sure rounding errors accumulate in favor of -/// the protocol -/// NOTE: Calling this function when interests for the market are up to date with the current block -/// and index is not, will use the wrong interest rate to update the index. -/// NOTE: This function should not be used when calculating how much scaled amount is getting -/// burned from given underlying withdraw amount. In that case, all math should be done in underlying -/// amounts then get scaled back again -pub fn get_scaled_liquidity_amount( - amount: Uint128, - market: &Market, - timestamp: u64, -) -> StdResult { - compute_scaled_amount( - amount, - get_updated_liquidity_index(market, timestamp)?, - ScalingOperation::Truncate, - ) -} - -/// Get underlying liquidity amount from a scaled amount, a Market and timestamp in seconds -/// Liquidity amounts are always truncated to make sure rounding errors accumulate in favor of -/// the protocol -/// NOTE: Calling this function when interests for the market are up to date with the current block -/// and index is not, will use the wrong interest rate to update the index. -pub fn get_underlying_liquidity_amount( - amount_scaled: Uint128, - market: &Market, - timestamp: u64, -) -> StdResult { - compute_underlying_amount( - amount_scaled, - get_updated_liquidity_index(market, timestamp)?, - ScalingOperation::Truncate, - ) -} - -/// Get scaled borrow amount from an underlying amount, a Market and timestamp in seconds -/// Debt amounts are always ceiled to make sure rounding errors accumulate in favor of -/// the protocol -/// NOTE: Calling this function when interests for the market are up to date with the current block -/// and index is not, will use the wrong interest rate to update the index. -/// NOTE: This function should not be used when calculating how much scaled amount is getting -/// repaid from a sent underlying amount. In that case, all math should be done in underlying -/// amounts then get scaled back again -pub fn get_scaled_debt_amount( - amount: Uint128, - market: &Market, - timestamp: u64, -) -> StdResult { - compute_scaled_amount( - amount, - get_updated_borrow_index(market, timestamp)?, - ScalingOperation::Ceil, - ) -} - -/// Get underlying borrow amount from a scaled amount, a Market and timestamp in seconds -/// Debt amounts are always ceiled so as for rounding errors to accumulate in favor of -/// the protocol -/// NOTE: Calling this function when interests for the market are up to date with the current block -/// and index is not, will use the wrong interest rate to update the index. -pub fn get_underlying_debt_amount( - amount_scaled: Uint128, - market: &Market, - timestamp: u64, -) -> StdResult { - compute_underlying_amount( - amount_scaled, - get_updated_borrow_index(market, timestamp)?, - ScalingOperation::Ceil, - ) -} - -pub enum ScalingOperation { - Truncate, - Ceil, -} - -/// Scales the amount dividing by an index in order to compute interest rates. Before dividing, -/// the value is multiplied by SCALING_FACTOR for greater precision. -/// Example: -/// Current index is 10. We deposit 6.123456 OSMO (6123456 uosmo). Scaled amount will be -/// 6123456 / 10 = 612345 so we loose some precision. In order to avoid this situation -/// we scale the amount by SCALING_FACTOR. -pub fn compute_scaled_amount( - amount: Uint128, - index: Decimal, - scaling_operation: ScalingOperation, -) -> StdResult { - // Scale by SCALING_FACTOR to have better precision - let scaled_amount = amount.checked_mul(SCALING_FACTOR)?; - match scaling_operation { - ScalingOperation::Truncate => math::divide_uint128_by_decimal(scaled_amount, index), - ScalingOperation::Ceil => math::divide_uint128_by_decimal_and_ceil(scaled_amount, index), - } -} - -/// Descales the amount introduced by `get_scaled_amount`, returning the underlying amount. -/// As interest rate is accumulated the index used to descale the amount should be bigger than the one used to scale it. -pub fn compute_underlying_amount( - scaled_amount: Uint128, - index: Decimal, - scaling_operation: ScalingOperation, -) -> StdResult { - // Multiply scaled amount by decimal (index) - let before_scaling_factor = scaled_amount * index; - - // Descale by SCALING_FACTOR which is introduced when scaling the amount - match scaling_operation { - ScalingOperation::Truncate => Ok(before_scaling_factor.checked_div(SCALING_FACTOR)?), - ScalingOperation::Ceil => { - math::uint128_checked_div_with_ceil(before_scaling_factor, SCALING_FACTOR) - } - } -} - -/// Return applied interest rate for borrow index according to passed blocks -/// NOTE: Calling this function when interests for the market are up to date with the current block -/// and index is not, will use the wrong interest rate to update the index. -pub fn get_updated_borrow_index(market: &Market, timestamp: u64) -> StdResult { - if market.indexes_last_updated < timestamp { - let time_elapsed = timestamp - market.indexes_last_updated; - - if !market.borrow_rate.is_zero() { - let updated_index = calculate_applied_linear_interest_rate( - market.borrow_index, - market.borrow_rate, - time_elapsed, - ); - return updated_index; - } - } - - Ok(market.borrow_index) -} - -/// Return applied interest rate for liquidity index according to passed blocks -/// NOTE: Calling this function when interests for the market are up to date with the current block -/// and index is not, will use the wrong interest rate to update the index. -pub fn get_updated_liquidity_index(market: &Market, timestamp: u64) -> StdResult { - if market.indexes_last_updated > timestamp { - return Err(StdError::generic_err( - format!("Cannot compute updated liquidity index for a timestamp: {} smaller than last updated timestamp for market: {}", timestamp, market.indexes_last_updated) - )); - } - - if market.indexes_last_updated < timestamp { - let time_elapsed = timestamp - market.indexes_last_updated; - - if !market.liquidity_rate.is_zero() { - let updated_index = calculate_applied_linear_interest_rate( - market.liquidity_index, - market.liquidity_rate, - time_elapsed, - ); - return updated_index; - } - } - - Ok(market.liquidity_index) -} - /// Update interest rates for current liquidity and debt levels /// Note it does not save the market to the store (that is left to the caller) /// Returns response with appended interest rates updated event @@ -306,48 +129,3 @@ pub fn build_interests_updated_event(denom: &str, market: &Market) -> Event { .add_attribute("borrow_rate", market.borrow_rate.to_string()) .add_attribute("liquidity_rate", market.liquidity_rate.to_string()) } - -#[cfg(test)] -mod tests { - use cosmwasm_std::{Decimal, Uint128}; - use mars_red_bank_types::red_bank::Market; - - use crate::interest_rates::{ - calculate_applied_linear_interest_rate, get_scaled_debt_amount, - get_scaled_liquidity_amount, get_underlying_debt_amount, get_underlying_liquidity_amount, - }; - - #[test] - fn accumulated_index_calculation() { - let index = Decimal::from_ratio(1u128, 10u128); - let rate = Decimal::from_ratio(2u128, 10u128); - let time_elapsed = 15768000; // half a year - let accumulated = - calculate_applied_linear_interest_rate(index, rate, time_elapsed).unwrap(); - - assert_eq!(accumulated, Decimal::from_ratio(11u128, 100u128)); - } - - #[test] - fn liquidity_and_debt_rounding() { - let start = Uint128::from(100_000_000_000_u128); - let market = Market { - liquidity_index: Decimal::from_ratio(3_u128, 1_u128), - borrow_index: Decimal::from_ratio(3_u128, 1_u128), - indexes_last_updated: 1, - ..Default::default() - }; - - let scaled_amount_liquidity = get_scaled_liquidity_amount(start, &market, 1).unwrap(); - let scaled_amount_debt = get_scaled_debt_amount(start, &market, 1).unwrap(); - assert_eq!(Uint128::from(33_333_333_333_333_333_u128), scaled_amount_liquidity); - assert_eq!(Uint128::from(33_333_333_333_333_334_u128), scaled_amount_debt); - - let back_to_underlying_liquidity = - get_underlying_liquidity_amount(scaled_amount_liquidity, &market, 1).unwrap(); - let back_to_underlying_debt = - get_underlying_debt_amount(scaled_amount_debt, &market, 1).unwrap(); - assert_eq!(Uint128::from(99_999_999_999_u128), back_to_underlying_liquidity); - assert_eq!(Uint128::from(100_000_000_001_u128), back_to_underlying_debt); - } -} diff --git a/contracts/red-bank/src/query.rs b/contracts/red-bank/src/query.rs index dbb9698c2..c570d2e9f 100644 --- a/contracts/red-bank/src/query.rs +++ b/contracts/red-bank/src/query.rs @@ -1,5 +1,9 @@ use cosmwasm_std::{Addr, BlockInfo, Deps, Env, Order, StdError, StdResult, Uint128}; use cw_storage_plus::Bound; +use mars_interest_rate::{ + get_scaled_debt_amount, get_scaled_liquidity_amount, get_underlying_debt_amount, + get_underlying_liquidity_amount, +}; use mars_red_bank_types::{ address_provider::{self, MarsAddressType}, red_bank::{ @@ -11,10 +15,6 @@ use mars_red_bank_types::{ use crate::{ error::ContractError, health, - interest_rates::{ - get_scaled_debt_amount, get_scaled_liquidity_amount, get_underlying_debt_amount, - get_underlying_liquidity_amount, - }, state::{COLLATERALS, CONFIG, DEBTS, MARKETS, OWNER, UNCOLLATERALIZED_LOAN_LIMITS}, }; diff --git a/contracts/red-bank/tests/helpers.rs b/contracts/red-bank/tests/helpers.rs index 39bdc5bfe..4007fa62a 100644 --- a/contracts/red-bank/tests/helpers.rs +++ b/contracts/red-bank/tests/helpers.rs @@ -6,13 +6,13 @@ use cosmwasm_std::{ testing::{MockApi, MockStorage}, Addr, Coin, Decimal, Deps, DepsMut, Event, OwnedDeps, Uint128, }; +use mars_interest_rate::{ + calculate_applied_linear_interest_rate, compute_scaled_amount, compute_underlying_amount, + ScalingOperation, +}; use mars_params::types::{AssetParams, HighLeverageStrategyParams, RedBankSettings, RoverSettings}; use mars_red_bank::{ contract::{instantiate, query}, - interest_rates::{ - calculate_applied_linear_interest_rate, compute_scaled_amount, compute_underlying_amount, - ScalingOperation, - }, state::{COLLATERALS, DEBTS, MARKETS}, }; use mars_red_bank_types::red_bank::{ diff --git a/contracts/red-bank/tests/test_admin.rs b/contracts/red-bank/tests/test_admin.rs index 53428ba80..9447a782d 100644 --- a/contracts/red-bank/tests/test_admin.rs +++ b/contracts/red-bank/tests/test_admin.rs @@ -1,9 +1,9 @@ use cosmwasm_std::{attr, coin, from_binary, testing::mock_info, Addr, Decimal, Event, Uint128}; +use mars_interest_rate::{compute_scaled_amount, compute_underlying_amount, ScalingOperation}; use mars_owner::OwnerError::NotOwner; use mars_red_bank::{ contract::{execute, instantiate, query}, error::ContractError, - interest_rates::{compute_scaled_amount, compute_underlying_amount, ScalingOperation}, state::{COLLATERALS, MARKETS}, }; use mars_red_bank_types::{ diff --git a/contracts/red-bank/tests/test_borrow.rs b/contracts/red-bank/tests/test_borrow.rs index 71fc93ba4..c33301bb4 100644 --- a/contracts/red-bank/tests/test_borrow.rs +++ b/contracts/red-bank/tests/test_borrow.rs @@ -6,14 +6,14 @@ use helpers::{ has_collateral_position, has_debt_position, set_collateral, th_build_interests_updated_event, th_get_expected_indices_and_rates, th_init_market, th_setup, TestUtilizationDeltaInfo, }; +use mars_interest_rate::{ + calculate_applied_linear_interest_rate, compute_scaled_amount, compute_underlying_amount, + ScalingOperation, SCALING_FACTOR, +}; use mars_params::types::{AssetParams, HighLeverageStrategyParams, RedBankSettings, RoverSettings}; use mars_red_bank::{ contract::execute, error::ContractError, - interest_rates::{ - calculate_applied_linear_interest_rate, compute_scaled_amount, compute_underlying_amount, - ScalingOperation, SCALING_FACTOR, - }, state::{DEBTS, MARKETS, UNCOLLATERALIZED_LOAN_LIMITS}, }; use mars_red_bank_types::red_bank::{ExecuteMsg, Market}; diff --git a/contracts/red-bank/tests/test_deposit.rs b/contracts/red-bank/tests/test_deposit.rs index 05607b90b..eaece9c67 100644 --- a/contracts/red-bank/tests/test_deposit.rs +++ b/contracts/red-bank/tests/test_deposit.rs @@ -9,11 +9,11 @@ use cw_utils::PaymentError; use helpers::{ set_collateral, th_build_interests_updated_event, th_get_expected_indices_and_rates, th_setup, }; +use mars_interest_rate::{compute_scaled_amount, ScalingOperation, SCALING_FACTOR}; use mars_params::types::{AssetParams, HighLeverageStrategyParams, RedBankSettings, RoverSettings}; use mars_red_bank::{ contract::execute, error::ContractError, - interest_rates::{compute_scaled_amount, ScalingOperation, SCALING_FACTOR}, state::{COLLATERALS, MARKETS}, }; use mars_red_bank_types::{ diff --git a/contracts/red-bank/tests/test_liquidate.rs b/contracts/red-bank/tests/test_liquidate.rs index b763d3505..04c8a39ab 100644 --- a/contracts/red-bank/tests/test_liquidate.rs +++ b/contracts/red-bank/tests/test_liquidate.rs @@ -14,15 +14,15 @@ use helpers::{ th_get_expected_indices, th_get_expected_indices_and_rates, th_init_market, th_setup, TestUtilizationDeltaInfo, }; +use mars_interest_rate::{ + compute_scaled_amount, compute_underlying_amount, get_scaled_liquidity_amount, + ScalingOperation, SCALING_FACTOR, +}; use mars_params::types::AssetParams; use mars_red_bank::{ contract::execute, error::ContractError, execute::liquidation_compute_amounts, - interest_rates::{ - compute_scaled_amount, compute_underlying_amount, get_scaled_liquidity_amount, - ScalingOperation, SCALING_FACTOR, - }, state::{COLLATERALS, DEBTS, MARKETS}, }; use mars_red_bank_types::{ diff --git a/contracts/red-bank/tests/test_misc.rs b/contracts/red-bank/tests/test_misc.rs index 0cddaf106..a86fa9b9b 100644 --- a/contracts/red-bank/tests/test_misc.rs +++ b/contracts/red-bank/tests/test_misc.rs @@ -6,16 +6,16 @@ use helpers::{ th_build_interests_updated_event, th_get_expected_indices_and_rates, th_init_market, th_setup, TestUtilizationDeltaInfo, }; +use mars_interest_rate::{ + compute_scaled_amount, compute_underlying_amount, get_scaled_debt_amount, + get_updated_liquidity_index, ScalingOperation, SCALING_FACTOR, +}; use mars_owner::OwnerError::NotOwner; use mars_params::types::AssetParams; use mars_red_bank::{ contract::execute, error::ContractError, health, - interest_rates::{ - compute_scaled_amount, compute_underlying_amount, get_scaled_debt_amount, - get_updated_liquidity_index, ScalingOperation, SCALING_FACTOR, - }, state::{DEBTS, MARKETS, UNCOLLATERALIZED_LOAN_LIMITS}, }; use mars_red_bank_types::red_bank::{Debt, ExecuteMsg, Market}; diff --git a/contracts/red-bank/tests/test_query.rs b/contracts/red-bank/tests/test_query.rs index 8539dccaf..82ac958e1 100644 --- a/contracts/red-bank/tests/test_query.rs +++ b/contracts/red-bank/tests/test_query.rs @@ -1,7 +1,7 @@ use cosmwasm_std::{testing::mock_env, Addr, Decimal, Uint128}; use helpers::{set_collateral, th_init_market, th_setup}; +use mars_interest_rate::{get_scaled_debt_amount, get_underlying_debt_amount, SCALING_FACTOR}; use mars_red_bank::{ - interest_rates::{get_scaled_debt_amount, get_underlying_debt_amount, SCALING_FACTOR}, query::{query_user_collaterals, query_user_debt, query_user_debts}, state::DEBTS, }; diff --git a/contracts/red-bank/tests/test_withdraw.rs b/contracts/red-bank/tests/test_withdraw.rs index 77db18ea4..75a32f0a0 100644 --- a/contracts/red-bank/tests/test_withdraw.rs +++ b/contracts/red-bank/tests/test_withdraw.rs @@ -7,14 +7,14 @@ use helpers::{ has_collateral_position, set_collateral, th_build_interests_updated_event, th_get_expected_indices_and_rates, th_setup, TestUtilizationDeltaInfo, }; +use mars_interest_rate::{ + compute_scaled_amount, compute_underlying_amount, get_scaled_liquidity_amount, + get_updated_borrow_index, get_updated_liquidity_index, ScalingOperation, SCALING_FACTOR, +}; use mars_params::types::AssetParams; use mars_red_bank::{ contract::execute, error::ContractError, - interest_rates::{ - compute_scaled_amount, compute_underlying_amount, get_scaled_liquidity_amount, - get_updated_borrow_index, get_updated_liquidity_index, ScalingOperation, SCALING_FACTOR, - }, state::{COLLATERALS, DEBTS, MARKETS}, }; use mars_red_bank_types::{ diff --git a/packages/interest-rate/Cargo.toml b/packages/interest-rate/Cargo.toml new file mode 100644 index 000000000..905f8f5d8 --- /dev/null +++ b/packages/interest-rate/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "mars-interest-rate" +description = "Computations related to interest rates" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +documentation = { workspace = true } +keywords = { workspace = true } + +[lib] +doctest = false + +[features] +# for quicker tests, cargo test --lib +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] + +[dependencies] +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +mars-red-bank-types = { workspace = true } +mars-utils = { workspace = true } diff --git a/packages/interest-rate/src/lib.rs b/packages/interest-rate/src/lib.rs new file mode 100644 index 000000000..8f8a37591 --- /dev/null +++ b/packages/interest-rate/src/lib.rs @@ -0,0 +1,222 @@ +use cosmwasm_std::{Decimal, StdError, StdResult, Uint128}; +use mars_red_bank_types::red_bank::Market; +use mars_utils::math; + +/// Scaling factor used to keep more precision during division / multiplication by index. +pub const SCALING_FACTOR: Uint128 = Uint128::new(1_000_000); + +const SECONDS_PER_YEAR: u64 = 31536000u64; + +pub fn calculate_applied_linear_interest_rate( + index: Decimal, + rate: Decimal, + time_elapsed: u64, +) -> StdResult { + let rate_factor = rate.checked_mul(Decimal::from_ratio( + Uint128::from(time_elapsed), + Uint128::from(SECONDS_PER_YEAR), + ))?; + index.checked_mul(Decimal::one() + rate_factor).map_err(StdError::from) +} + +/// Get scaled liquidity amount from an underlying amount, a Market and timestamp in seconds +/// Liquidity amounts are always truncated to make sure rounding errors accumulate in favor of +/// the protocol +/// NOTE: Calling this function when interests for the market are up to date with the current block +/// and index is not, will use the wrong interest rate to update the index. +/// NOTE: This function should not be used when calculating how much scaled amount is getting +/// burned from given underlying withdraw amount. In that case, all math should be done in underlying +/// amounts then get scaled back again +pub fn get_scaled_liquidity_amount( + amount: Uint128, + market: &Market, + timestamp: u64, +) -> StdResult { + compute_scaled_amount( + amount, + get_updated_liquidity_index(market, timestamp)?, + ScalingOperation::Truncate, + ) +} + +/// Get underlying liquidity amount from a scaled amount, a Market and timestamp in seconds +/// Liquidity amounts are always truncated to make sure rounding errors accumulate in favor of +/// the protocol +/// NOTE: Calling this function when interests for the market are up to date with the current block +/// and index is not, will use the wrong interest rate to update the index. +pub fn get_underlying_liquidity_amount( + amount_scaled: Uint128, + market: &Market, + timestamp: u64, +) -> StdResult { + compute_underlying_amount( + amount_scaled, + get_updated_liquidity_index(market, timestamp)?, + ScalingOperation::Truncate, + ) +} + +/// Get scaled borrow amount from an underlying amount, a Market and timestamp in seconds +/// Debt amounts are always ceiled to make sure rounding errors accumulate in favor of +/// the protocol +/// NOTE: Calling this function when interests for the market are up to date with the current block +/// and index is not, will use the wrong interest rate to update the index. +/// NOTE: This function should not be used when calculating how much scaled amount is getting +/// repaid from a sent underlying amount. In that case, all math should be done in underlying +/// amounts then get scaled back again +pub fn get_scaled_debt_amount( + amount: Uint128, + market: &Market, + timestamp: u64, +) -> StdResult { + compute_scaled_amount( + amount, + get_updated_borrow_index(market, timestamp)?, + ScalingOperation::Ceil, + ) +} + +/// Get underlying borrow amount from a scaled amount, a Market and timestamp in seconds +/// Debt amounts are always ceiled so as for rounding errors to accumulate in favor of +/// the protocol +/// NOTE: Calling this function when interests for the market are up to date with the current block +/// and index is not, will use the wrong interest rate to update the index. +pub fn get_underlying_debt_amount( + amount_scaled: Uint128, + market: &Market, + timestamp: u64, +) -> StdResult { + compute_underlying_amount( + amount_scaled, + get_updated_borrow_index(market, timestamp)?, + ScalingOperation::Ceil, + ) +} + +pub enum ScalingOperation { + Truncate, + Ceil, +} + +/// Scales the amount dividing by an index in order to compute interest rates. Before dividing, +/// the value is multiplied by SCALING_FACTOR for greater precision. +/// Example: +/// Current index is 10. We deposit 6.123456 OSMO (6123456 uosmo). Scaled amount will be +/// 6123456 / 10 = 612345 so we loose some precision. In order to avoid this situation +/// we scale the amount by SCALING_FACTOR. +pub fn compute_scaled_amount( + amount: Uint128, + index: Decimal, + scaling_operation: ScalingOperation, +) -> StdResult { + // Scale by SCALING_FACTOR to have better precision + let scaled_amount = amount.checked_mul(SCALING_FACTOR)?; + match scaling_operation { + ScalingOperation::Truncate => math::divide_uint128_by_decimal(scaled_amount, index), + ScalingOperation::Ceil => math::divide_uint128_by_decimal_and_ceil(scaled_amount, index), + } +} + +/// Descales the amount introduced by `get_scaled_amount`, returning the underlying amount. +/// As interest rate is accumulated the index used to descale the amount should be bigger than the one used to scale it. +pub fn compute_underlying_amount( + scaled_amount: Uint128, + index: Decimal, + scaling_operation: ScalingOperation, +) -> StdResult { + // Multiply scaled amount by decimal (index) + let before_scaling_factor = scaled_amount * index; + + // Descale by SCALING_FACTOR which is introduced when scaling the amount + match scaling_operation { + ScalingOperation::Truncate => Ok(before_scaling_factor.checked_div(SCALING_FACTOR)?), + ScalingOperation::Ceil => { + math::uint128_checked_div_with_ceil(before_scaling_factor, SCALING_FACTOR) + } + } +} + +/// Return applied interest rate for borrow index according to passed blocks +/// NOTE: Calling this function when interests for the market are up to date with the current block +/// and index is not, will use the wrong interest rate to update the index. +pub fn get_updated_borrow_index(market: &Market, timestamp: u64) -> StdResult { + if market.indexes_last_updated < timestamp { + let time_elapsed = timestamp - market.indexes_last_updated; + + if !market.borrow_rate.is_zero() { + let updated_index = calculate_applied_linear_interest_rate( + market.borrow_index, + market.borrow_rate, + time_elapsed, + ); + return updated_index; + } + } + + Ok(market.borrow_index) +} + +/// Return applied interest rate for liquidity index according to passed blocks +/// NOTE: Calling this function when interests for the market are up to date with the current block +/// and index is not, will use the wrong interest rate to update the index. +pub fn get_updated_liquidity_index(market: &Market, timestamp: u64) -> StdResult { + if market.indexes_last_updated > timestamp { + return Err(StdError::generic_err( + format!("Cannot compute updated liquidity index for a timestamp: {} smaller than last updated timestamp for market: {}", timestamp, market.indexes_last_updated) + )); + } + + if market.indexes_last_updated < timestamp { + let time_elapsed = timestamp - market.indexes_last_updated; + + if !market.liquidity_rate.is_zero() { + let updated_index = calculate_applied_linear_interest_rate( + market.liquidity_index, + market.liquidity_rate, + time_elapsed, + ); + return updated_index; + } + } + + Ok(market.liquidity_index) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn accumulated_index_calculation() { + let index = Decimal::from_ratio(1u128, 10u128); + let rate = Decimal::from_ratio(2u128, 10u128); + let time_elapsed = 15768000; // half a year + let accumulated = + calculate_applied_linear_interest_rate(index, rate, time_elapsed).unwrap(); + + assert_eq!(accumulated, Decimal::from_ratio(11u128, 100u128)); + } + + #[test] + fn liquidity_and_debt_rounding() { + let start = Uint128::from(100_000_000_000_u128); + let market = Market { + liquidity_index: Decimal::from_ratio(3_u128, 1_u128), + borrow_index: Decimal::from_ratio(3_u128, 1_u128), + indexes_last_updated: 1, + ..Default::default() + }; + + let scaled_amount_liquidity = get_scaled_liquidity_amount(start, &market, 1).unwrap(); + let scaled_amount_debt = get_scaled_debt_amount(start, &market, 1).unwrap(); + assert_eq!(Uint128::from(33_333_333_333_333_333_u128), scaled_amount_liquidity); + assert_eq!(Uint128::from(33_333_333_333_333_334_u128), scaled_amount_debt); + + let back_to_underlying_liquidity = + get_underlying_liquidity_amount(scaled_amount_liquidity, &market, 1).unwrap(); + let back_to_underlying_debt = + get_underlying_debt_amount(scaled_amount_debt, &market, 1).unwrap(); + assert_eq!(Uint128::from(99_999_999_999_u128), back_to_underlying_liquidity); + assert_eq!(Uint128::from(100_000_000_001_u128), back_to_underlying_debt); + } +} From 56efc271658d466330c6c329f325d35aec614e39 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Tue, 25 Jul 2023 14:05:49 +0100 Subject: [PATCH 03/27] query to return `None` on nonexistent market --- contracts/red-bank/src/query.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/contracts/red-bank/src/query.rs b/contracts/red-bank/src/query.rs index c570d2e9f..7e79000b1 100644 --- a/contracts/red-bank/src/query.rs +++ b/contracts/red-bank/src/query.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Addr, BlockInfo, Deps, Env, Order, StdError, StdResult, Uint128}; +use cosmwasm_std::{Addr, BlockInfo, Deps, Env, Order, StdResult, Uint128}; use cw_storage_plus::Bound; use mars_interest_rate::{ get_scaled_debt_amount, get_scaled_liquidity_amount, get_underlying_debt_amount, @@ -31,10 +31,8 @@ pub fn query_config(deps: Deps) -> StdResult { }) } -pub fn query_market(deps: Deps, denom: String) -> StdResult { - MARKETS - .load(deps.storage, &denom) - .map_err(|_| StdError::generic_err(format!("failed to load market for: {denom}"))) +pub fn query_market(deps: Deps, denom: String) -> StdResult> { + MARKETS.may_load(deps.storage, &denom) } pub fn query_markets( From 31e236b1b600bb4788f5c6dca6515e34eac9697a Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Tue, 25 Jul 2023 14:34:04 +0100 Subject: [PATCH 04/27] add `total_deposit` query to mars-params --- Cargo.lock | 2 + contracts/params/Cargo.toml | 2 + contracts/params/src/contract.rs | 14 +++- contracts/params/src/msg.rs | 11 ++- contracts/params/src/query.rs | 84 +++++++++++++++++++++- contracts/params/src/state.rs | 1 + contracts/params/tests/helpers/mock_env.rs | 1 + 7 files changed, 109 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e08f94d9e..56a7d06bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1936,7 +1936,9 @@ dependencies = [ "cw-multi-test 0.16.5", "cw-storage-plus 1.1.0", "cw2 1.1.0", + "mars-interest-rate", "mars-owner", + "mars-red-bank-types 1.1.0", "mars-utils 1.1.0", "schemars", "serde", diff --git a/contracts/params/Cargo.toml b/contracts/params/Cargo.toml index 1f449d6fe..c9c70330c 100644 --- a/contracts/params/Cargo.toml +++ b/contracts/params/Cargo.toml @@ -24,7 +24,9 @@ cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw2 = { workspace = true } cw-storage-plus = { workspace = true } +mars-interest-rate = { workspace = true } mars-owner = { workspace = true } +mars-red-bank-types = { workspace = true } mars-utils = { workspace = true } schemars = { workspace = true } serde = { workspace = true } diff --git a/contracts/params/src/contract.rs b/contracts/params/src/contract.rs index 71ffd2b0c..12eaca325 100644 --- a/contracts/params/src/contract.rs +++ b/contracts/params/src/contract.rs @@ -12,8 +12,10 @@ use crate::{ CmEmergencyUpdate, EmergencyUpdate, ExecuteMsg, InstantiateMsg, QueryMsg, RedBankEmergencyUpdate, }, - query::{query_all_asset_params, query_all_vault_configs, query_vault_config}, - state::{ASSET_PARAMS, OWNER, TARGET_HEALTH_FACTOR}, + query::{ + query_all_asset_params, query_all_vault_configs, query_total_deposit, query_vault_config, + }, + state::{ASSET_PARAMS, OWNER, TARGET_HEALTH_FACTOR, ADDRESS_PROVIDER}, }; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); @@ -36,6 +38,9 @@ pub fn instantiate( }, )?; + let address_provider_addr = deps.api.addr_validate(&msg.address_provider)?; + ADDRESS_PROVIDER.save(deps.storage, &address_provider_addr)?; + assert_thf(msg.target_health_factor)?; TARGET_HEALTH_FACTOR.save(deps.storage, &msg.target_health_factor)?; @@ -72,7 +77,7 @@ pub fn execute( } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _: Env, msg: QueryMsg) -> ContractResult { +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> ContractResult { let res = match msg { QueryMsg::Owner {} => to_binary(&OWNER.query(deps.storage)?), QueryMsg::AssetParams { @@ -90,6 +95,9 @@ pub fn query(deps: Deps, _: Env, msg: QueryMsg) -> ContractResult { limit, } => to_binary(&query_all_vault_configs(deps, start_after, limit)?), QueryMsg::TargetHealthFactor {} => to_binary(&TARGET_HEALTH_FACTOR.load(deps.storage)?), + QueryMsg::TotalDeposit { + denom, + } => to_binary(&query_total_deposit(deps, &env, denom)?), }; res.map_err(Into::into) } diff --git a/contracts/params/src/msg.rs b/contracts/params/src/msg.rs index 836691097..7f192d08d 100644 --- a/contracts/params/src/msg.rs +++ b/contracts/params/src/msg.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::Decimal; +use cosmwasm_std::{Coin, Decimal}; use mars_owner::OwnerUpdate; use crate::types::{asset::AssetParamsUnchecked, vault::VaultConfigUnchecked}; @@ -8,6 +8,8 @@ use crate::types::{asset::AssetParamsUnchecked, vault::VaultConfigUnchecked}; pub struct InstantiateMsg { /// Contract's owner pub owner: String, + /// Address of the address provider contract + pub address_provider: String, /// Determines the ideal HF a position should be left at immediately after the position has been liquidated. pub target_health_factor: Decimal, } @@ -52,6 +54,13 @@ pub enum QueryMsg { #[returns(Decimal)] TargetHealthFactor {}, + + /// Compute the total amount deposited of the given asset across Red Bank + /// and Credit Manager. + #[returns(Coin)] + TotalDeposit { + denom: String, + }, } #[cw_serde] diff --git a/contracts/params/src/query.rs b/contracts/params/src/query.rs index b9ec3b109..d7a5b8256 100644 --- a/contracts/params/src/query.rs +++ b/contracts/params/src/query.rs @@ -1,8 +1,13 @@ -use cosmwasm_std::{Addr, Deps, Order, StdResult}; +use cosmwasm_std::{Addr, Coin, Deps, Env, Order, StdResult, Uint128}; use cw_storage_plus::Bound; +use mars_interest_rate::get_underlying_liquidity_amount; +use mars_red_bank_types::{ + address_provider::{self, MarsAddressType}, + red_bank::{self, Market, UserDebtResponse}, +}; use crate::{ - state::{ASSET_PARAMS, VAULT_CONFIGS}, + state::{ADDRESS_PROVIDER, ASSET_PARAMS, VAULT_CONFIGS}, types::{asset::AssetParams, vault::VaultConfig}, }; @@ -49,3 +54,78 @@ pub fn query_all_vault_configs( .map(|res| Ok(res?.1)) .collect() } + +/// Query and compute the total deposited amount of the given asset across Red +/// Bank (RB) and Credit Manager (CM). +/// +/// Specifically, the amount is defined as: +/// rb_deposit + cm_deposit - cm_debt_owed_to_rb +/// +/// Note: +/// +/// 1. We subtract the amount of debt that CM owes to RB to avoid double- +/// counting. +/// +/// 2. We only consider spot asset holdings, meaning we don't unwrap DEX LP +/// tokens or vault tokens to the underlying assets. After some discussions +/// we have concluded the latter is not feasible. +/// +/// For example, when computing the deposited amount of ATOM, we only include +/// ATOM deposited in RB and CM; we don't include the ATOM-OSMO LP token, or +/// the ATOM-OSMO farming vault. +pub fn query_total_deposit(deps: Deps, env: &Env, denom: String) -> StdResult { + let current_timestamp = env.block.time.seconds(); + + // query contract addresses + let address_provider_addr = ADDRESS_PROVIDER.load(deps.storage)?; + let addresses = address_provider::helpers::query_contract_addrs( + deps, + &address_provider_addr, + vec![MarsAddressType::RedBank, MarsAddressType::CreditManager], + )?; + let credit_manager_addr = &addresses[&MarsAddressType::CreditManager]; + let red_bank_addr = &addresses[&MarsAddressType::RedBank]; + + // amount of this asset deposited into Red Bank + // if the market doesn't exist on RB, we default to zero + let rb_deposit = deps + .querier + .query_wasm_smart::>( + red_bank_addr, + &red_bank::QueryMsg::Market { + denom: denom.clone(), + }, + )? + .map(|market| { + get_underlying_liquidity_amount( + market.collateral_total_scaled, + &market, + current_timestamp, + ) + }) + .transpose()? + .unwrap_or_else(Uint128::zero); + + // amount of debt in this asset the Credit Manager owes to Red Bank + // this query returns zero if no debt is owed + let cm_debt = deps + .querier + .query_wasm_smart::( + red_bank_addr, + &red_bank::QueryMsg::UserDebt { + user: credit_manager_addr.into(), + denom: denom.clone(), + }, + )? + .amount; + + // amount of this asset deposited into Credit Manager + // this is simply the coin balance of the CM contract + // note that this way, we don't include LP tokens or vault positions + let cm_deposit = deps.querier.query_balance(credit_manager_addr, &denom)?.amount; + + Ok(Coin { + denom, + amount: rb_deposit.checked_add(cm_deposit)?.checked_sub(cm_debt)?, + }) +} diff --git a/contracts/params/src/state.rs b/contracts/params/src/state.rs index efa0169a6..0259a0377 100644 --- a/contracts/params/src/state.rs +++ b/contracts/params/src/state.rs @@ -5,6 +5,7 @@ use mars_owner::Owner; use crate::types::{asset::AssetParams, vault::VaultConfig}; pub const OWNER: Owner = Owner::new("owner"); +pub const ADDRESS_PROVIDER: Item = Item::new("address_provider"); pub const ASSET_PARAMS: Map<&str, AssetParams> = Map::new("asset_params"); pub const VAULT_CONFIGS: Map<&Addr, VaultConfig> = Map::new("vault_configs"); pub const TARGET_HEALTH_FACTOR: Item = Item::new("target_health_factor"); diff --git a/contracts/params/tests/helpers/mock_env.rs b/contracts/params/tests/helpers/mock_env.rs index e5b291d2f..729a9d331 100644 --- a/contracts/params/tests/helpers/mock_env.rs +++ b/contracts/params/tests/helpers/mock_env.rs @@ -187,6 +187,7 @@ impl MockEnvBuilder { Addr::unchecked("owner"), &InstantiateMsg { owner: "owner".to_string(), + address_provider: "address_provider".to_string(), target_health_factor: self.get_target_health_factor(), }, &[], From b97bd846f916458a66a2cbac29c9639067ac7ef4 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Wed, 26 Jul 2023 12:05:19 +0100 Subject: [PATCH 05/27] check total deposit when user deposits --- contracts/red-bank/src/deposit.rs | 10 ++++------ contracts/red-bank/src/helpers.rs | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/contracts/red-bank/src/deposit.rs b/contracts/red-bank/src/deposit.rs index bda09cf24..39e9b1c31 100644 --- a/contracts/red-bank/src/deposit.rs +++ b/contracts/red-bank/src/deposit.rs @@ -1,10 +1,10 @@ use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, Uint128}; -use mars_interest_rate::{get_scaled_liquidity_amount, get_underlying_liquidity_amount}; +use mars_interest_rate::get_scaled_liquidity_amount; use mars_red_bank_types::address_provider::{self, MarsAddressType}; use crate::{ error::ContractError, - helpers::query_asset_params, + helpers::{query_asset_params, query_total_deposit}, interest_rates::{apply_accumulated_interests, update_interest_rates}, state::{CONFIG, MARKETS}, user::User, @@ -42,10 +42,8 @@ pub fn deposit( }); } - let total_scaled_deposits = market.collateral_total_scaled; - let total_deposits = - get_underlying_liquidity_amount(total_scaled_deposits, &market, env.block.time.seconds())?; - if total_deposits.checked_add(deposit_amount)? > asset_params.red_bank.deposit_cap { + let total_deposits = query_total_deposit(&deps.querier, params_addr, &denom)?; + if total_deposits.amount.checked_add(deposit_amount)? > asset_params.red_bank.deposit_cap { return Err(ContractError::DepositCapExceeded { denom, }); diff --git a/contracts/red-bank/src/helpers.rs b/contracts/red-bank/src/helpers.rs index b6de4095e..79fa686ff 100644 --- a/contracts/red-bank/src/helpers.rs +++ b/contracts/red-bank/src/helpers.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Decimal, QuerierWrapper, StdResult}; +use cosmwasm_std::{Coin, Decimal, QuerierWrapper, StdResult}; use mars_params::{msg::QueryMsg, types::asset::AssetParams}; pub fn query_asset_params( @@ -20,3 +20,16 @@ pub fn query_target_health_factor( ) -> StdResult { querier.query_wasm_smart(params.into(), &QueryMsg::TargetHealthFactor {}) } + +pub fn query_total_deposit( + querier: &QuerierWrapper, + params: impl Into, + denom: impl Into, +) -> StdResult { + querier.query_wasm_smart( + params.into(), + &QueryMsg::TotalDeposit { + denom: denom.into(), + }, + ) +} From df65481946ddfd5cc0f05fd9dd2831ee688bee61 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Fri, 28 Jul 2023 20:20:47 +0100 Subject: [PATCH 06/27] add debt to mock querier --- packages/testing/src/mars_mock_querier.rs | 10 ++++++++++ packages/testing/src/red_bank_querier.rs | 10 +++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/testing/src/mars_mock_querier.rs b/packages/testing/src/mars_mock_querier.rs index ea89017ea..29805acaa 100644 --- a/packages/testing/src/mars_mock_querier.rs +++ b/packages/testing/src/mars_mock_querier.rs @@ -186,6 +186,16 @@ impl MarsMockQuerier { .insert((user.into(), collateral.denom.clone()), collateral); } + pub fn set_red_bank_user_debt( + &mut self, + user: impl Into, + debt: red_bank::UserDebtResponse, + ) { + self.redbank_querier + .users_denoms_debts + .insert((user.into(), debt.denom.clone()), debt); + } + pub fn set_redbank_user_position( &mut self, user_address: String, diff --git a/packages/testing/src/red_bank_querier.rs b/packages/testing/src/red_bank_querier.rs index b4984d268..fa69e81cb 100644 --- a/packages/testing/src/red_bank_querier.rs +++ b/packages/testing/src/red_bank_querier.rs @@ -2,13 +2,14 @@ use std::collections::HashMap; use cosmwasm_std::{to_binary, Binary, ContractResult, QuerierResult}; use mars_red_bank_types::red_bank::{ - Market, QueryMsg, UserCollateralResponse, UserPositionResponse, + Market, QueryMsg, UserCollateralResponse, UserPositionResponse, UserDebtResponse, }; #[derive(Default)] pub struct RedBankQuerier { pub markets: HashMap, pub users_denoms_collaterals: HashMap<(String, String), UserCollateralResponse>, + pub users_denoms_debts: HashMap<(String, String), UserDebtResponse>, pub users_positions: HashMap, } @@ -28,6 +29,13 @@ impl RedBankQuerier { Some(collateral) => to_binary(&collateral).into(), None => Err(format!("[mock]: could not find the collateral for {user}")).into(), }, + QueryMsg::UserDebt { + user, + denom, + } => match self.users_denoms_debts.get(&(user.clone(), denom)) { + Some(debt) => to_binary(&debt).into(), + None => Err(format!("[mock]: could not find the debt for {user}")).into(), + }, QueryMsg::UserPosition { user, } => match self.users_positions.get(&user) { From 60f646819d8167a8160cbf25973ea86a856db786 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Fri, 28 Jul 2023 20:26:20 +0100 Subject: [PATCH 07/27] mock querier to return `Option` --- packages/testing/src/red_bank_querier.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/testing/src/red_bank_querier.rs b/packages/testing/src/red_bank_querier.rs index fa69e81cb..87ce73ea8 100644 --- a/packages/testing/src/red_bank_querier.rs +++ b/packages/testing/src/red_bank_querier.rs @@ -18,9 +18,9 @@ impl RedBankQuerier { let ret: ContractResult = match query { QueryMsg::Market { denom, - } => match self.markets.get(&denom) { - Some(market) => to_binary(&market).into(), - None => Err(format!("[mock]: could not find the market for {denom}")).into(), + } => { + let maybe_market = self.markets.get(&denom); + to_binary(&maybe_market).into() }, QueryMsg::UserCollateral { user, From e64133a63083c290ce41526e491247f426df23e5 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Fri, 28 Jul 2023 20:49:14 +0100 Subject: [PATCH 08/27] add `update_balances` method to mock querier --- packages/testing/src/mars_mock_querier.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/testing/src/mars_mock_querier.rs b/packages/testing/src/mars_mock_querier.rs index 29805acaa..ae7a96886 100644 --- a/packages/testing/src/mars_mock_querier.rs +++ b/packages/testing/src/mars_mock_querier.rs @@ -77,6 +77,10 @@ impl MarsMockQuerier { self.base.update_balance(contract_addr.to_string(), contract_balances.to_vec()); } + pub fn update_balances(&mut self, addr: impl Into, balance: Vec) { + self.base.update_balance(addr, balance); + } + pub fn set_oracle_price(&mut self, denom: &str, price: Decimal) { self.oracle_querier.prices.insert(denom.to_string(), price); } From 3b54094f5ac8fca303e2dd27b8a56ee189bb1bfd Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Fri, 28 Jul 2023 21:07:34 +0100 Subject: [PATCH 09/27] add test for computing total deposit --- Cargo.lock | 2 + contracts/params/Cargo.toml | 2 + contracts/params/tests/test_deposit_cap.rs | 69 ++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 contracts/params/tests/test_deposit_cap.rs diff --git a/Cargo.lock b/Cargo.lock index e490dcdca..8de795cce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1931,9 +1931,11 @@ dependencies = [ "mars-interest-rate", "mars-owner", "mars-red-bank-types", + "mars-testing", "mars-utils", "schemars", "serde", + "test-case", "thiserror", ] diff --git a/contracts/params/Cargo.toml b/contracts/params/Cargo.toml index c9c70330c..88b963499 100644 --- a/contracts/params/Cargo.toml +++ b/contracts/params/Cargo.toml @@ -35,3 +35,5 @@ thiserror = { workspace = true } [dev-dependencies] anyhow = { workspace = true } cw-multi-test = { workspace = true } +mars-testing = { workspace = true } +test-case = { workspace = true } diff --git a/contracts/params/tests/test_deposit_cap.rs b/contracts/params/tests/test_deposit_cap.rs new file mode 100644 index 000000000..4ee1f3854 --- /dev/null +++ b/contracts/params/tests/test_deposit_cap.rs @@ -0,0 +1,69 @@ +use std::str::FromStr; + +use cosmwasm_std::{coins, Addr, Decimal, Uint128}; +use mars_interest_rate::get_underlying_liquidity_amount; +use mars_params::{query::query_total_deposit, state::ADDRESS_PROVIDER}; +use mars_red_bank_types::red_bank::{Market, UserDebtResponse}; +use mars_testing::{mock_dependencies, mock_env_at_block_time}; +use test_case::test_case; + +const CREDIT_MANAGER: &str = "credit_manager"; +const MOCK_DENOM: &str = "utoken"; +const TIMESTAMP: u64 = 1690573960; + +#[test_case( + Market { + denom: MOCK_DENOM.into(), + collateral_total_scaled: Uint128::zero(), + liquidity_index: Decimal::one(), + indexes_last_updated: TIMESTAMP, + ..Default::default() + }, + UserDebtResponse { + denom: MOCK_DENOM.into(), + amount_scaled: Uint128::zero(), + amount: Uint128::zero(), + uncollateralized: true, + }, + Uint128::zero(); + "zero liquidity, zero debt, zero balance" +)] +#[test_case( + Market { + denom: MOCK_DENOM.into(), + collateral_total_scaled: Uint128::new(6023580722925709342), + liquidity_index: Decimal::from_str("1.010435027113017045").unwrap(), + indexes_last_updated: 1690573862, + ..Default::default() + }, + UserDebtResponse { + denom: MOCK_DENOM.into(), + amount_scaled: Uint128::new(442125932248737808), + amount: Uint128::new(459180188271), + uncollateralized: true, + }, + Uint128::new(1751191642); + "real data queried from mainnet" +)] +fn querying_total_deposit( + rb_market: Market, + rb_debt: UserDebtResponse, + cm_balance: Uint128, +) { + let mut deps = mock_dependencies(&[]); + let env = mock_env_at_block_time(TIMESTAMP); + + // setup + deps.querier.set_redbank_market(rb_market.clone()); + deps.querier.set_red_bank_user_debt(CREDIT_MANAGER, rb_debt.clone()); + deps.querier.update_balances(CREDIT_MANAGER, coins(cm_balance.u128(), MOCK_DENOM)); + ADDRESS_PROVIDER.save(deps.as_mut().storage, &Addr::unchecked("address_provider")).unwrap(); + + // compute the correct, expected total deposit + let rb_deposit = get_underlying_liquidity_amount(rb_market.collateral_total_scaled, &rb_market, TIMESTAMP).unwrap(); + let exp_total_deposit = rb_deposit + cm_balance - rb_debt.amount; + + // query total deposit + let total_deposit = query_total_deposit(deps.as_ref(), &env, MOCK_DENOM.into()).unwrap(); + assert_eq!(total_deposit.amount, exp_total_deposit); +} From 034e78b8352b70dcbb0d433f0cad934907ed0d8e Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Fri, 28 Jul 2023 21:16:48 +0100 Subject: [PATCH 10/27] fix tests --- packages/testing/src/integration/mock_env.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/testing/src/integration/mock_env.rs b/packages/testing/src/integration/mock_env.rs index cc5bd7345..4c9dfba5b 100644 --- a/packages/testing/src/integration/mock_env.rs +++ b/packages/testing/src/integration/mock_env.rs @@ -690,6 +690,7 @@ impl MockEnvBuilder { let red_bank_addr = self.deploy_red_bank(&address_provider_addr); let rewards_collector_addr = self.deploy_rewards_collector_osmosis(&address_provider_addr); let params_addr = self.deploy_params_osmosis(&address_provider_addr); + let credit_manager_addr = Addr::unchecked("credit_manager"); self.update_address_provider( &address_provider_addr, @@ -708,6 +709,7 @@ impl MockEnvBuilder { &rewards_collector_addr, ); self.update_address_provider(&address_provider_addr, MarsAddressType::Params, ¶ms_addr); + self.update_address_provider(&address_provider_addr, MarsAddressType::CreditManager, &credit_manager_addr); MockEnv { app: take(&mut self.app), From 423d68275ee6573a98d10a44a9d445ae81311607 Mon Sep 17 00:00:00 2001 From: Pacman Date: Fri, 21 Jul 2023 13:37:37 +0200 Subject: [PATCH 11/27] Move rewards collector to rewards collector base Move tests from base to osmosis Implement IbcTransferMsg abstraction Implement mars-rewards-collector-neutron Add neutron config to update msg. Disable github pipeline. --- .github/workflows/coverage.yml | 10 +- .gitignore | 2 + Cargo.lock | 257 +++++++++++++----- Cargo.toml | 6 +- .../rewards-collector/{ => base}/Cargo.toml | 2 +- .../{ => base}/examples/schema.rs | 0 .../{ => base}/src/contract.rs | 223 +++++++-------- .../rewards-collector/{ => base}/src/error.rs | 0 .../{ => base}/src/helpers.rs | 0 .../rewards-collector/{ => base}/src/lib.rs | 0 .../rewards-collector/base/src/traits.rs | 61 +++++ .../rewards-collector/neutron/Cargo.toml | 45 +++ .../rewards-collector/neutron/src/lib.rs | 91 +++++++ .../rewards-collector/osmosis/Cargo.toml | 44 +++ .../rewards-collector/osmosis/src/lib.rs | 38 +++ .../{ => osmosis}/tests/helpers.rs | 3 +- .../{ => osmosis}/tests/test_admin.rs | 6 +- .../tests/test_distribute_rewards.rs | 3 +- .../{ => osmosis}/tests/test_swap.rs | 2 +- .../{ => osmosis}/tests/test_update_owner.rs | 3 +- .../{ => osmosis}/tests/test_withdraw.rs | 2 +- contracts/rewards-collector/src/traits.rs | 32 --- integration-tests/Cargo.toml | 2 +- integration-tests/tests/test_oracles.rs | 3 +- .../tests/test_rewards_collector.rs | 5 +- packages/testing/Cargo.toml | 2 +- .../testing/src/integration/mock_contracts.rs | 6 +- packages/testing/src/integration/mock_env.rs | 1 + packages/types/src/rewards_collector.rs | 16 +- schema.Makefile.toml | 2 +- .../mars-address-provider.json | 15 +- schemas/mars-params/mars-params.json | 50 ++++ .../mars-rewards-collector-base.json} | 110 +++++++- scripts/deploy/base/deployer.ts | 2 +- .../MarsAddressProvider.types.ts | 2 +- .../mars-params/MarsParams.client.ts | 9 + .../mars-params/MarsParams.react-query.ts | 25 ++ .../generated/mars-params/MarsParams.types.ts | 6 + .../MarsRewardsCollectorBase.client.ts} | 23 +- .../MarsRewardsCollectorBase.react-query.ts} | 96 +++---- .../MarsRewardsCollectorBase.types.ts} | 14 +- .../bundle.ts | 8 +- scripts/types/msg.ts | 2 +- 43 files changed, 918 insertions(+), 311 deletions(-) rename contracts/rewards-collector/{ => base}/Cargo.toml (95%) rename contracts/rewards-collector/{ => base}/examples/schema.rs (100%) rename contracts/rewards-collector/{ => base}/src/contract.rs (80%) rename contracts/rewards-collector/{ => base}/src/error.rs (100%) rename contracts/rewards-collector/{ => base}/src/helpers.rs (100%) rename contracts/rewards-collector/{ => base}/src/lib.rs (100%) create mode 100644 contracts/rewards-collector/base/src/traits.rs create mode 100644 contracts/rewards-collector/neutron/Cargo.toml create mode 100644 contracts/rewards-collector/neutron/src/lib.rs create mode 100644 contracts/rewards-collector/osmosis/Cargo.toml create mode 100644 contracts/rewards-collector/osmosis/src/lib.rs rename contracts/rewards-collector/{ => osmosis}/tests/helpers.rs (98%) rename contracts/rewards-collector/{ => osmosis}/tests/test_admin.rs (97%) rename contracts/rewards-collector/{ => osmosis}/tests/test_distribute_rewards.rs (95%) rename contracts/rewards-collector/{ => osmosis}/tests/test_swap.rs (98%) rename contracts/rewards-collector/{ => osmosis}/tests/test_update_owner.rs (95%) rename contracts/rewards-collector/{ => osmosis}/tests/test_withdraw.rs (95%) delete mode 100644 contracts/rewards-collector/src/traits.rs rename schemas/{mars-rewards-collector/mars-rewards-collector.json => mars-rewards-collector-base/mars-rewards-collector-base.json} (83%) rename scripts/types/generated/{mars-rewards-collector/MarsRewardsCollector.client.ts => mars-rewards-collector-base/MarsRewardsCollectorBase.client.ts} (91%) rename scripts/types/generated/{mars-rewards-collector/MarsRewardsCollector.react-query.ts => mars-rewards-collector-base/MarsRewardsCollectorBase.react-query.ts} (55%) rename scripts/types/generated/{mars-rewards-collector/MarsRewardsCollector.types.ts => mars-rewards-collector-base/MarsRewardsCollectorBase.types.ts} (88%) rename scripts/types/generated/{mars-rewards-collector => mars-rewards-collector-base}/bundle.ts (53%) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 29be0e235..010df50c6 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -29,14 +29,8 @@ jobs: - name: Compile workspace run: cargo make build - # FIXME: enable it one problem with `no space left fixed` - # - remove `target/release` deps - # - name: Cleanup - # run: | - # rm -rf target/release - - # - name: Run test - # run: cargo make test + - name: Run test + run: cargo make test # disabled because of "no space left" error. # - name: Run test coverage diff --git a/.gitignore b/.gitignore index f59407e03..767427783 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ build/ # IDEs *.iml .idea +.vscode/ # Environment *.env @@ -27,3 +28,4 @@ whitelists/ artifacts/ yarn-error.log + diff --git a/Cargo.lock b/Cargo.lock index 8de795cce..a3c902c20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,7 +114,7 @@ dependencies = [ "cw-storage-plus 0.15.1", "cw2 0.15.1", "itertools", - "protobuf", + "protobuf 2.28.0", "thiserror", ] @@ -131,7 +131,7 @@ dependencies = [ "cw1-whitelist", "cw2 0.15.1", "cw20 0.15.1", - "protobuf", + "protobuf 2.28.0", "thiserror", ] @@ -200,7 +200,7 @@ dependencies = [ "cw2 0.15.1", "cw20 0.15.1", "integer-sqrt", - "protobuf", + "protobuf 2.28.0", "thiserror", ] @@ -246,7 +246,7 @@ dependencies = [ "cw-storage-plus 0.15.1", "cw2 0.15.1", "cw20 0.15.1", - "protobuf", + "protobuf 2.28.0", "thiserror", ] @@ -300,7 +300,7 @@ checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -347,6 +347,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + [[package]] name = "base64ct" version = "1.6.0" @@ -522,6 +528,9 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "cc" @@ -638,7 +647,18 @@ checksum = "20b42021d8488665b1a0d9748f1f81df7235362d194f44481e2e61bf376b77b4" dependencies = [ "prost 0.11.9", "prost-types", - "tendermint-proto", + "tendermint-proto 0.23.9", +] + +[[package]] +name = "cosmos-sdk-proto" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4776e787b24d9568dd61d3237eeb4eb321d622fb881b858c7b82806420e87d4" +dependencies = [ + "prost 0.11.9", + "prost-types", + "tendermint-proto 0.27.0", ] [[package]] @@ -648,7 +668,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3903590099dcf1ea580d9353034c9ba1dbf55d1389a5bd2ade98535c3445d1f9" dependencies = [ "bip32", - "cosmos-sdk-proto", + "cosmos-sdk-proto 0.14.0", "ecdsa", "eyre", "getrandom", @@ -664,9 +684,9 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d076a08ec01ed23c4396aca98ec73a38fa1fee5f310465add52b4108181c7a8" +checksum = "7e272708a9745dad8b591ef8a718571512130f2b39b33e3d7a27c558e3069394" dependencies = [ "digest 0.10.7", "ed25519-zebra", @@ -677,18 +697,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec361f3c09d7b41221948fc17be9b3c96cb58e55a02f82da36f888a651f2584" +checksum = "296db6a3caca5283425ae0cf347f4e46999ba3f6620dbea8939a0e00347831ce" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6b2fb76758ef59cddc77f2e2ae91c22f77da49037e9f182e9c2833f0e959b1" +checksum = "63c337e097a089e5b52b5d914a7ff6613332777f38ea6d9d36e1887cd0baa72e" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -699,9 +719,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfa39422f0d9f1c9a6fd3711573258495314dfa3aae738ea825ecd9964bc659" +checksum = "766cc9e7c1762d8fc9c0265808910fcad755200cd0e624195a491dd885a61169" dependencies = [ "proc-macro2", "quote", @@ -710,11 +730,11 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6dc2ee23313add5ecacc3ccac217b9967ad9d2d11bd56e5da6aa65a9da6138" +checksum = "eb5e05a95fd2a420cca50f4e94eb7e70648dac64db45e90403997ebefeb143bd" dependencies = [ - "base64", + "base64 0.13.1", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -730,9 +750,9 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade8cae79dc08a06bcf119c0854ffaed11bd8cb1013c6b04abfe1f51f36211e" +checksum = "800aaddd70ba915e19bf3d2d992aa3689d8767857727fdd3b414df4fd52d2aa1" dependencies = [ "cosmwasm-std", "serde", @@ -1198,9 +1218,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", @@ -1330,7 +1350,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -1452,7 +1472,7 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ - "base64", + "base64 0.13.1", "bitflags 1.3.2", "bytes", "headers-core", @@ -1745,9 +1765,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "log" @@ -1813,12 +1833,12 @@ dependencies = [ "mars-params", "mars-red-bank", "mars-red-bank-types", - "mars-rewards-collector", + "mars-rewards-collector-osmosis", "mars-swapper-osmosis", "mars-testing", "mars-utils", "osmosis-std 0.16.1", - "osmosis-test-tube 16.0.1", + "osmosis-test-tube 16.1.1", "serde", ] @@ -1974,7 +1994,45 @@ dependencies = [ ] [[package]] -name = "mars-rewards-collector" +name = "mars-rewards-collector-base" +version = "1.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0", + "mars-osmosis", + "mars-owner", + "mars-red-bank-types", + "mars-testing", + "mars-utils", + "osmosis-std 0.16.1", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "mars-rewards-collector-neutron" +version = "1.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0", + "mars-osmosis", + "mars-owner", + "mars-red-bank-types", + "mars-rewards-collector-base", + "mars-testing", + "mars-utils", + "neutron-sdk", + "osmosis-std 0.16.1", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "mars-rewards-collector-osmosis" version = "1.1.0" dependencies = [ "cosmwasm-schema", @@ -1983,6 +2041,7 @@ dependencies = [ "mars-osmosis", "mars-owner", "mars-red-bank-types", + "mars-rewards-collector-base", "mars-testing", "mars-utils", "osmosis-std 0.16.1", @@ -2067,7 +2126,7 @@ dependencies = [ "mars-params", "mars-red-bank", "mars-red-bank-types", - "mars-rewards-collector", + "mars-rewards-collector-osmosis", "mars-swapper-astroport", "osmosis-std 0.16.1", "prost 0.11.9", @@ -2120,6 +2179,27 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "neutron-sdk" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfc6f92cae61b5af9014c09b7bac25ac95b7442be38441a7103377a8edfd37c" +dependencies = [ + "base64 0.21.2", + "bech32", + "cosmos-sdk-proto 0.16.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0", + "prost 0.11.9", + "protobuf 3.2.0", + "schemars", + "serde", + "serde-json-wasm", + "serde_json", + "thiserror", +] + [[package]] name = "nom" version = "7.1.3" @@ -2273,7 +2353,7 @@ name = "osmosis-test-tube" version = "15.1.0" source = "git+https://github.com/apollodao/test-tube.git?rev=800a2af15bdd8270a4d832a2b1b799446fc8e1cf#800a2af15bdd8270a4d832a2b1b799446fc8e1cf" dependencies = [ - "base64", + "base64 0.13.1", "bindgen", "cosmrs", "cosmwasm-std", @@ -2287,11 +2367,11 @@ dependencies = [ [[package]] name = "osmosis-test-tube" -version = "16.0.1" +version = "16.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527375c01396e7e4de4ccc18a0141aeb6b342dc089d30c57282025f3a8753e72" +checksum = "4929047d1dcec5d7d02fd0a00ecdfca78918d3a33bffc193bf57b3eeb4d407ab" dependencies = [ - "base64", + "base64 0.13.1", "bindgen", "cosmrs", "cosmwasm-std", @@ -2365,9 +2445,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5" +checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" dependencies = [ "thiserror", "ucd-trie", @@ -2375,9 +2455,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f94bca7e7a599d89dea5dfa309e217e7906c3c007fb9c3299c40b10d6a315d3" +checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" dependencies = [ "pest", "pest_generator", @@ -2385,22 +2465,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d490fe7e8556575ff6911e45567ab95e71617f43781e5c05490dc8d75c965c" +checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] name = "pest_meta" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674c66ebb4b4d9036012091b537aae5878970d6999f81a265034d85b136b341" +checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" dependencies = [ "once_cell", "pest", @@ -2424,7 +2504,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -2581,6 +2661,27 @@ dependencies = [ "bytes", ] +[[package]] +name = "protobuf" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" +dependencies = [ + "bytes", + "once_cell", + "protobuf-support", + "thiserror", +] + +[[package]] +name = "protobuf-support" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" +dependencies = [ + "thiserror", +] + [[package]] name = "pyth-sdk" version = "0.7.0" @@ -2689,9 +2790,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" dependencies = [ "aho-corasick", "memchr", @@ -2762,7 +2863,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" dependencies = [ - "base64", + "base64 0.13.1", "bitflags 1.3.2", "serde", ] @@ -2808,7 +2909,7 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ - "base64", + "base64 0.13.1", "log", "ring", "sct", @@ -2948,9 +3049,9 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.175" +version = "1.0.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b" +checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed" dependencies = [ "serde_derive", ] @@ -2984,13 +3085,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.175" +version = "1.0.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4" +checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -3006,9 +3107,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -3017,13 +3118,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e168eaaf71e8f9bd6037feb05190485708e019f4fd87d161b3c0a0d37daf85e5" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -3205,9 +3306,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.27" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -3253,7 +3354,7 @@ dependencies = [ "signature", "subtle", "subtle-encoding", - "tendermint-proto", + "tendermint-proto 0.23.9", "time", "zeroize", ] @@ -3290,6 +3391,24 @@ dependencies = [ "time", ] +[[package]] +name = "tendermint-proto" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5895470f28c530f8ae8c4071bf8190304ce00bd131d25e81730453124a3375c" +dependencies = [ + "bytes", + "flex-error", + "num-derive", + "num-traits", + "prost 0.11.9", + "prost-types", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + [[package]] name = "tendermint-rpc" version = "0.23.9" @@ -3313,7 +3432,7 @@ dependencies = [ "subtle-encoding", "tendermint", "tendermint-config", - "tendermint-proto", + "tendermint-proto 0.23.9", "thiserror", "time", "tokio", @@ -3372,7 +3491,7 @@ name = "test-tube" version = "0.1.2" source = "git+https://github.com/apollodao/test-tube.git?rev=800a2af15bdd8270a4d832a2b1b799446fc8e1cf#800a2af15bdd8270a4d832a2b1b799446fc8e1cf" dependencies = [ - "base64", + "base64 0.13.1", "cosmrs", "cosmwasm-std", "osmosis-std 0.16.1", @@ -3388,7 +3507,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a520f35c8ca6665ea5231a81ce9110f49a6901a1cbedb751e72859e83320bfea" dependencies = [ - "base64", + "base64 0.13.1", "cosmrs", "cosmwasm-std", "osmosis-std 0.16.1", @@ -3421,7 +3540,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -3482,7 +3601,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] [[package]] @@ -3686,7 +3805,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", "wasm-bindgen-shared", ] @@ -3708,7 +3827,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3882,5 +4001,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.28", ] diff --git a/Cargo.toml b/Cargo.toml index 13c9404a0..a00f55987 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ "contracts/params", "contracts/swapper/*", "contracts/red-bank", - "contracts/rewards-collector", + "contracts/rewards-collector/*", "packages/chains/*", "packages/health", "packages/interest-rate", @@ -58,6 +58,7 @@ pyth-sdk-cw = "1.2.0" cw-paginate = "0.2.1" astroport = "2.8.0" strum = "0.24.1" +neutron-sdk = "0.6.0" # dev-dependencies cw-multi-test = "0.16.5" @@ -83,7 +84,8 @@ mars-oracle-base = { version = "1.0.0", path = "./contracts/oracle mars-oracle-osmosis = { version = "1.0.0", path = "./contracts/oracle/osmosis" } mars-oracle-wasm = { version = "1.0.0", path = "./contracts/oracle/wasm" } mars-red-bank = { version = "1.0.0", path = "./contracts/red-bank" } -mars-rewards-collector = { version = "1.0.0", path = "./contracts/rewards-collector" } +mars-rewards-collector-base = { version = "1.0.0", path = "./contracts/rewards-collector/base" } +mars-rewards-collector-osmosis = { version = "1.0.0", path = "./contracts/rewards-collector/osmosis" } mars-swapper-base = { version = "1.0.0", path = "./contracts/swapper/base" } mars-swapper-astroport = { version = "1.0.0", path = "./contracts/swapper/astroport" } mars-swapper-osmosis = { version = "1.0.0", path = "./contracts/swapper/osmosis" } diff --git a/contracts/rewards-collector/Cargo.toml b/contracts/rewards-collector/base/Cargo.toml similarity index 95% rename from contracts/rewards-collector/Cargo.toml rename to contracts/rewards-collector/base/Cargo.toml index eb4444101..7054631c7 100644 --- a/contracts/rewards-collector/Cargo.toml +++ b/contracts/rewards-collector/base/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "mars-rewards-collector" +name = "mars-rewards-collector-base" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } diff --git a/contracts/rewards-collector/examples/schema.rs b/contracts/rewards-collector/base/examples/schema.rs similarity index 100% rename from contracts/rewards-collector/examples/schema.rs rename to contracts/rewards-collector/base/examples/schema.rs diff --git a/contracts/rewards-collector/src/contract.rs b/contracts/rewards-collector/base/src/contract.rs similarity index 80% rename from contracts/rewards-collector/src/contract.rs rename to contracts/rewards-collector/base/src/contract.rs index d58da81e3..a3ef2a5c4 100644 --- a/contracts/rewards-collector/src/contract.rs +++ b/contracts/rewards-collector/base/src/contract.rs @@ -1,52 +1,132 @@ use cosmwasm_std::{ - coin, to_binary, Addr, Coin, CosmosMsg, Deps, DepsMut, Empty, Env, IbcMsg, IbcTimeout, + coin, to_binary, Addr, Binary, Coin, CosmosMsg, CustomMsg, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, Uint128, WasmMsg, }; use cw_storage_plus::Item; -use mars_owner::{Owner, OwnerUpdate}; +use mars_owner::{Owner, OwnerInit::SetInitialOwner, OwnerUpdate}; use mars_red_bank_types::{ address_provider::{self, AddressResponseItem, MarsAddressType}, incentives, red_bank, - rewards_collector::{Config, ConfigResponse, UpdateConfig}, + rewards_collector::{ + Config, ConfigResponse, ExecuteMsg, InstantiateMsg, QueryMsg, UpdateConfig, + }, }; use mars_utils::helpers::option_string_to_addr; use crate::{ helpers::{stringify_option_amount, unwrap_option_amount}, - ContractError, ContractResult, + ContractError, ContractResult, IbcTransferMsg, }; -pub struct Collector<'a> { +pub struct Collector<'a, M: CustomMsg, I: IbcTransferMsg> { /// Contract's owner pub owner: Owner<'a>, /// The contract's configurations pub config: Item<'a, Config>, + /// Phantomdata for custom msg + pub custom_msg: std::marker::PhantomData, + /// Phantomdata for IBC transfer msg + pub ibc_transfer_msg: std::marker::PhantomData, } -impl<'a> Default for Collector<'a> { +impl<'a, M: CustomMsg, I: IbcTransferMsg> Default for Collector<'a, M, I> { fn default() -> Self { Self { owner: Owner::new("owner"), config: Item::new("config"), + custom_msg: std::marker::PhantomData, + ibc_transfer_msg: std::marker::PhantomData, } } } -impl<'a> Collector<'a> { - fn update_owner( - self, +impl<'a, M, I> Collector<'a, M, I> +where + M: CustomMsg, + I: IbcTransferMsg, +{ + pub fn instantiate( + &self, + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, + ) -> ContractResult { + let owner = msg.owner.clone(); + + let cfg = Config::checked(deps.api, msg)?; + cfg.validate()?; + + self.owner.initialize( + deps.storage, + deps.api, + SetInitialOwner { + owner, + }, + )?; + + self.config.save(deps.storage, &cfg)?; + + Ok(Response::default()) + } + + pub fn execute( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, + ) -> ContractResult> { + match msg { + ExecuteMsg::UpdateOwner(update) => self.update_owner(deps, info, update), + ExecuteMsg::UpdateConfig { + new_cfg, + } => self.update_config(deps, info.sender, new_cfg), + ExecuteMsg::WithdrawFromRedBank { + denom, + amount, + } => self.withdraw_from_red_bank(deps, denom, amount), + ExecuteMsg::DistributeRewards { + denom, + amount, + } => self.distribute_rewards(deps, env, denom, amount), + ExecuteMsg::SwapAsset { + denom, + amount, + } => self.swap_asset(deps, env, denom, amount), + ExecuteMsg::ClaimIncentiveRewards { + start_after_collateral_denom, + start_after_incentive_denom, + limit, + } => self.claim_incentive_rewards( + deps, + start_after_collateral_denom, + start_after_incentive_denom, + limit, + ), + } + } + + pub fn query(&self, deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Config {} => to_binary(&self.query_config(deps)?), + } + } + + pub fn update_owner( + &self, deps: DepsMut, info: MessageInfo, update: OwnerUpdate, - ) -> ContractResult { + ) -> ContractResult> { Ok(self.owner.update(deps, info, update)?) } - fn update_config( + pub fn update_config( &self, deps: DepsMut, sender: Addr, new_cfg: UpdateConfig, - ) -> ContractResult { + ) -> ContractResult> { self.owner.assert_owner(deps.storage, &sender)?; let mut cfg = self.config.load(deps.storage)?; @@ -59,6 +139,7 @@ impl<'a> Collector<'a> { channel_id, timeout_seconds, slippage_tolerance, + neutron_ibc_config, } = new_cfg; cfg.address_provider = @@ -69,6 +150,10 @@ impl<'a> Collector<'a> { cfg.channel_id = channel_id.unwrap_or(cfg.channel_id); cfg.timeout_seconds = timeout_seconds.unwrap_or(cfg.timeout_seconds); cfg.slippage_tolerance = slippage_tolerance.unwrap_or(cfg.slippage_tolerance); + if neutron_ibc_config.is_some() { + // override current config, otherwise leave previous one + cfg.neutron_ibc_config = neutron_ibc_config; + } cfg.validate()?; @@ -77,12 +162,12 @@ impl<'a> Collector<'a> { Ok(Response::new().add_attribute("action", "mars/rewards-collector/update_config")) } - fn withdraw_from_red_bank( + pub fn withdraw_from_red_bank( &self, deps: DepsMut, denom: String, amount: Option, - ) -> ContractResult { + ) -> ContractResult> { let cfg = self.config.load(deps.storage)?; let red_bank_addr = address_provider::helpers::query_contract_addr( @@ -108,13 +193,13 @@ impl<'a> Collector<'a> { .add_attribute("amount", stringify_option_amount(amount))) } - fn claim_incentive_rewards( + pub fn claim_incentive_rewards( &self, deps: DepsMut, start_after_collateral_denom: Option, start_after_incentive_denom: Option, limit: Option, - ) -> ContractResult { + ) -> ContractResult> { let cfg = self.config.load(deps.storage)?; let incentives_addr = address_provider::helpers::query_contract_addr( @@ -138,13 +223,13 @@ impl<'a> Collector<'a> { .add_attribute("action", "claim_incentive_rewards")) } - fn swap_asset( + pub fn swap_asset( &self, deps: DepsMut, env: Env, denom: String, amount: Option, - ) -> ContractResult { + ) -> ContractResult> { let cfg = self.config.load(deps.storage)?; let swapper_addr = deps @@ -203,13 +288,13 @@ impl<'a> Collector<'a> { .add_attribute("slippage_tolerance", cfg.slippage_tolerance.to_string())) } - fn distribute_rewards( + pub fn distribute_rewards( &self, deps: DepsMut, env: Env, denom: String, amount: Option, - ) -> ContractResult { + ) -> ContractResult> { let cfg = self.config.load(deps.storage)?; let to_address = if denom == cfg.safety_fund_denom { @@ -233,15 +318,15 @@ impl<'a> Collector<'a> { let amount_to_distribute = unwrap_option_amount(&deps.querier, &env.contract.address, &denom, amount)?; - let transfer_msg = CosmosMsg::Ibc(IbcMsg::Transfer { - channel_id: cfg.channel_id, - to_address: to_address.to_string(), - amount: Coin { + let transfer_msg = I::ibc_transfer_msg( + env, + to_address.clone(), + Coin { denom: denom.clone(), amount: amount_to_distribute, }, - timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(cfg.timeout_seconds)), - }); + cfg, + )?; Ok(Response::new() .add_message(transfer_msg) @@ -251,7 +336,7 @@ impl<'a> Collector<'a> { .add_attribute("to", to_address)) } - fn query_config(&self, deps: Deps) -> StdResult { + pub fn query_config(&self, deps: Deps) -> StdResult { let owner_state = self.owner.query(deps.storage)?; let cfg = self.config.load(deps.storage)?; Ok(ConfigResponse { @@ -267,87 +352,3 @@ impl<'a> Collector<'a> { }) } } - -#[cfg(not(feature = "library"))] -pub mod entry { - use cosmwasm_std::{ - entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, - }; - use mars_owner::OwnerInit::SetInitialOwner; - use mars_red_bank_types::rewards_collector::{Config, ExecuteMsg, InstantiateMsg, QueryMsg}; - - use super::Collector; - use crate::ContractResult; - - #[entry_point] - pub fn instantiate( - deps: DepsMut, - _env: Env, - _info: MessageInfo, - msg: InstantiateMsg, - ) -> ContractResult { - let collector = Collector::default(); - let owner = msg.owner.clone(); - - let cfg = Config::checked(deps.api, msg)?; - cfg.validate()?; - - collector.owner.initialize( - deps.storage, - deps.api, - SetInitialOwner { - owner, - }, - )?; - - collector.config.save(deps.storage, &cfg)?; - - Ok(Response::default()) - } - - #[entry_point] - pub fn execute( - deps: DepsMut, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, - ) -> ContractResult { - let collector = Collector::default(); - match msg { - ExecuteMsg::UpdateOwner(update) => collector.update_owner(deps, info, update), - ExecuteMsg::UpdateConfig { - new_cfg, - } => collector.update_config(deps, info.sender, new_cfg), - ExecuteMsg::WithdrawFromRedBank { - denom, - amount, - } => collector.withdraw_from_red_bank(deps, denom, amount), - ExecuteMsg::DistributeRewards { - denom, - amount, - } => collector.distribute_rewards(deps, env, denom, amount), - ExecuteMsg::SwapAsset { - denom, - amount, - } => collector.swap_asset(deps, env, denom, amount), - ExecuteMsg::ClaimIncentiveRewards { - start_after_collateral_denom, - start_after_incentive_denom, - limit, - } => collector.claim_incentive_rewards( - deps, - start_after_collateral_denom, - start_after_incentive_denom, - limit, - ), - } - } - - #[entry_point] - pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { - let collector = Collector::default(); - match msg { - QueryMsg::Config {} => to_binary(&collector.query_config(deps)?), - } - } -} diff --git a/contracts/rewards-collector/src/error.rs b/contracts/rewards-collector/base/src/error.rs similarity index 100% rename from contracts/rewards-collector/src/error.rs rename to contracts/rewards-collector/base/src/error.rs diff --git a/contracts/rewards-collector/src/helpers.rs b/contracts/rewards-collector/base/src/helpers.rs similarity index 100% rename from contracts/rewards-collector/src/helpers.rs rename to contracts/rewards-collector/base/src/helpers.rs diff --git a/contracts/rewards-collector/src/lib.rs b/contracts/rewards-collector/base/src/lib.rs similarity index 100% rename from contracts/rewards-collector/src/lib.rs rename to contracts/rewards-collector/base/src/lib.rs diff --git a/contracts/rewards-collector/base/src/traits.rs b/contracts/rewards-collector/base/src/traits.rs new file mode 100644 index 000000000..f909dcf41 --- /dev/null +++ b/contracts/rewards-collector/base/src/traits.rs @@ -0,0 +1,61 @@ +use std::fmt::{Debug, Display}; + +use cosmwasm_std::{ + Coin, CosmosMsg, CustomMsg, CustomQuery, Decimal, Empty, Env, IbcMsg, IbcTimeout, + QuerierWrapper, Uint128, +}; +use mars_red_bank_types::rewards_collector::Config; +use schemars::JsonSchema; +use serde::{de::DeserializeOwned, Serialize}; + +use crate::ContractResult; + +pub trait Route: + Serialize + DeserializeOwned + Clone + Debug + Display + PartialEq + JsonSchema +where + M: CustomMsg, + Q: CustomQuery, +{ + /// Determine whether the route is valid, given a pair of input and output denoms + fn validate( + &self, + querier: &QuerierWrapper, + denom_in: &str, + denom_out: &str, + ) -> ContractResult<()>; + + /// Build a message for executing the trade, given an input denom and amount + fn build_swap_msg( + &self, + env: &Env, + querier: &QuerierWrapper, + denom_in: &str, + amount: Uint128, + slippage_tolerance: Decimal, + ) -> ContractResult>; +} + +pub trait IbcTransferMsg { + fn ibc_transfer_msg( + env: Env, + to_address: String, + amount: Coin, + cfg: Config, + ) -> ContractResult>; +} + +impl IbcTransferMsg for Empty { + fn ibc_transfer_msg( + env: Env, + to_address: String, + amount: Coin, + cfg: Config, + ) -> ContractResult> { + Ok(CosmosMsg::Ibc(IbcMsg::Transfer { + channel_id: cfg.channel_id, + to_address, + amount, + timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(cfg.timeout_seconds)), + })) + } +} diff --git a/contracts/rewards-collector/neutron/Cargo.toml b/contracts/rewards-collector/neutron/Cargo.toml new file mode 100644 index 000000000..4768109ec --- /dev/null +++ b/contracts/rewards-collector/neutron/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "mars-rewards-collector-neutron" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +documentation = { workspace = true } +keywords = { workspace = true } + +[lib] +crate-type = [ + "cdylib", + "rlib", +] +doctest = false + +[profile.release] +overflow-checks = true + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = [ + "cosmwasm-std/backtraces", +] + +[dependencies] +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true, features = ["stargate"] } +cw-storage-plus = { workspace = true } +mars-owner = { workspace = true } +mars-red-bank-types = { workspace = true } +mars-rewards-collector-base = { workspace = true } +mars-utils = { workspace = true } +schemars = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } +neutron-sdk = { workspace = true } + + +[dev-dependencies] +mars-osmosis = { workspace = true } +mars-testing = { workspace = true } +osmosis-std = { workspace = true } diff --git a/contracts/rewards-collector/neutron/src/lib.rs b/contracts/rewards-collector/neutron/src/lib.rs new file mode 100644 index 000000000..f2360ecce --- /dev/null +++ b/contracts/rewards-collector/neutron/src/lib.rs @@ -0,0 +1,91 @@ +use std::vec; + +use cosmwasm_std::{coin, Coin, CosmosMsg, Env, StdError}; +use mars_rewards_collector_base::{ + contract::Collector, ContractError, ContractResult, IbcTransferMsg, +}; +use neutron_sdk::{ + bindings::msg::{IbcFee, NeutronMsg}, + sudo::msg::RequestPacketTimeoutHeight, +}; + +pub struct NeutronIbcMsgFactory {} + +impl IbcTransferMsg for NeutronIbcMsgFactory { + fn ibc_transfer_msg( + env: Env, + to_address: String, + amount: Coin, + cfg: mars_red_bank_types::rewards_collector::Config, + ) -> ContractResult> { + let neutron_config = cfg.neutron_ibc_config.ok_or(ContractError::Std( + StdError::generic_err("source_port must be provided for neutron"), + ))?; + Ok(NeutronMsg::IbcTransfer { + source_port: neutron_config.source_port, + source_channel: cfg.channel_id, + token: amount, + sender: env.contract.address.to_string(), + receiver: to_address, + timeout_height: RequestPacketTimeoutHeight { + revision_number: None, + revision_height: None, + }, + timeout_timestamp: env.block.time.nanos() + cfg.timeout_seconds * 1_000_000_000, + memo: "".to_string(), + fee: IbcFee { + recv_fee: vec![coin(0u128, "untrn")], + ack_fee: neutron_config.acc_fee, + timeout_fee: neutron_config.timeout_fee, + }, + } + .into()) + } +} + +pub type NeutronCollector<'a> = Collector<'a, NeutronMsg, NeutronIbcMsgFactory>; + +#[cfg(not(feature = "library"))] +pub mod entry { + use cosmwasm_std::{ + entry_point, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, + }; + use mars_red_bank_types::rewards_collector::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use mars_rewards_collector_base::ContractResult; + use neutron_sdk::bindings::msg::NeutronMsg; + + use crate::NeutronCollector; + + #[entry_point] + pub fn instantiate( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: InstantiateMsg, + ) -> ContractResult { + let collector = NeutronCollector::default(); + collector.instantiate(deps, env, info, msg) + } + + #[entry_point] + pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, + ) -> ContractResult> { + let collector = NeutronCollector::default(); + collector.execute(deps, env, info, msg) + } + + #[entry_point] + pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + let collector = NeutronCollector::default(); + collector.query(deps, env, msg) + } + + #[entry_point] + pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> StdResult { + Ok(Response::default()) + } +} diff --git a/contracts/rewards-collector/osmosis/Cargo.toml b/contracts/rewards-collector/osmosis/Cargo.toml new file mode 100644 index 000000000..f35b4d951 --- /dev/null +++ b/contracts/rewards-collector/osmosis/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "mars-rewards-collector-osmosis" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +documentation = { workspace = true } +keywords = { workspace = true } + +[lib] +crate-type = [ + "cdylib", + "rlib", +] +doctest = false + +[profile.release] +overflow-checks = true + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = [ + "cosmwasm-std/backtraces", +] + +[dependencies] +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true, features = ["stargate"] } +cw-storage-plus = { workspace = true } +mars-owner = { workspace = true } +mars-red-bank-types = { workspace = true } +mars-rewards-collector-base = { workspace = true } +mars-utils = { workspace = true } +schemars = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } + + +[dev-dependencies] +mars-osmosis = { workspace = true } +mars-testing = { workspace = true } +osmosis-std = { workspace = true } diff --git a/contracts/rewards-collector/osmosis/src/lib.rs b/contracts/rewards-collector/osmosis/src/lib.rs new file mode 100644 index 000000000..c22b450d8 --- /dev/null +++ b/contracts/rewards-collector/osmosis/src/lib.rs @@ -0,0 +1,38 @@ +#[cfg(not(feature = "library"))] +pub mod entry { + use cosmwasm_std::{ + entry_point, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, + }; + use mars_red_bank_types::rewards_collector::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use mars_rewards_collector_base::{contract::Collector, ContractResult}; + + pub type OsmosisCollector<'a> = Collector<'a, Empty, Empty>; + + #[entry_point] + pub fn instantiate( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: InstantiateMsg, + ) -> ContractResult { + let collector = OsmosisCollector::default(); + collector.instantiate(deps, env, info, msg) + } + + #[entry_point] + pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, + ) -> ContractResult { + let collector = OsmosisCollector::default(); + collector.execute(deps, env, info, msg) + } + + #[entry_point] + pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + let collector = OsmosisCollector::default(); + collector.query(deps, env, msg) + } +} diff --git a/contracts/rewards-collector/tests/helpers.rs b/contracts/rewards-collector/osmosis/tests/helpers.rs similarity index 98% rename from contracts/rewards-collector/tests/helpers.rs rename to contracts/rewards-collector/osmosis/tests/helpers.rs index a0eabb768..a7872d802 100644 --- a/contracts/rewards-collector/tests/helpers.rs +++ b/contracts/rewards-collector/osmosis/tests/helpers.rs @@ -7,7 +7,7 @@ use cosmwasm_std::{ }; use mars_osmosis::helpers::{Pool, QueryPoolResponse}; use mars_red_bank_types::rewards_collector::{Config, InstantiateMsg, QueryMsg}; -use mars_rewards_collector::contract::entry; +use mars_rewards_collector_osmosis::entry; use mars_testing::{mock_info, MarsMockQuerier}; use osmosis_std::types::osmosis::gamm::v1beta1::PoolAsset; @@ -21,6 +21,7 @@ pub fn mock_instantiate_msg() -> InstantiateMsg { channel_id: "channel-69".to_string(), timeout_seconds: 300, slippage_tolerance: Decimal::percent(3), + neutron_ibc_config: None, } } diff --git a/contracts/rewards-collector/tests/test_admin.rs b/contracts/rewards-collector/osmosis/tests/test_admin.rs similarity index 97% rename from contracts/rewards-collector/tests/test_admin.rs rename to contracts/rewards-collector/osmosis/tests/test_admin.rs index bc193fffc..e25cd06c8 100644 --- a/contracts/rewards-collector/tests/test_admin.rs +++ b/contracts/rewards-collector/osmosis/tests/test_admin.rs @@ -1,10 +1,8 @@ use cosmwasm_std::{testing::mock_env, Decimal}; use mars_owner::OwnerError::NotOwner; use mars_red_bank_types::rewards_collector::{ConfigResponse, ExecuteMsg, QueryMsg, UpdateConfig}; -use mars_rewards_collector::{ - contract::entry::{execute, instantiate}, - ContractError, -}; +use mars_rewards_collector_base::ContractError; +use mars_rewards_collector_osmosis::entry::{execute, instantiate}; use mars_testing::mock_info; use mars_utils::error::ValidationError; diff --git a/contracts/rewards-collector/tests/test_distribute_rewards.rs b/contracts/rewards-collector/osmosis/tests/test_distribute_rewards.rs similarity index 95% rename from contracts/rewards-collector/tests/test_distribute_rewards.rs rename to contracts/rewards-collector/osmosis/tests/test_distribute_rewards.rs index 124208d86..0334c8ea3 100644 --- a/contracts/rewards-collector/tests/test_distribute_rewards.rs +++ b/contracts/rewards-collector/osmosis/tests/test_distribute_rewards.rs @@ -2,7 +2,8 @@ use cosmwasm_std::{ coin, testing::mock_env, CosmosMsg, IbcMsg, IbcTimeout, SubMsg, Timestamp, Uint128, }; use mars_red_bank_types::rewards_collector::ExecuteMsg; -use mars_rewards_collector::{contract::entry::execute, ContractError}; +use mars_rewards_collector_base::ContractError; +use mars_rewards_collector_osmosis::entry::execute; use mars_testing::{mock_env as mock_env_at_height_and_time, mock_info, MockEnvParams}; mod helpers; diff --git a/contracts/rewards-collector/tests/test_swap.rs b/contracts/rewards-collector/osmosis/tests/test_swap.rs similarity index 98% rename from contracts/rewards-collector/tests/test_swap.rs rename to contracts/rewards-collector/osmosis/tests/test_swap.rs index f574466f7..331d67ab8 100644 --- a/contracts/rewards-collector/tests/test_swap.rs +++ b/contracts/rewards-collector/osmosis/tests/test_swap.rs @@ -5,7 +5,7 @@ use mars_red_bank_types::{ rewards_collector::{ConfigResponse, ExecuteMsg, QueryMsg}, swapper, }; -use mars_rewards_collector::contract::entry::execute; +use mars_rewards_collector_osmosis::entry::execute; use mars_testing::mock_info; use osmosis_std::types::osmosis::twap::v1beta1::ArithmeticTwapToNowResponse; diff --git a/contracts/rewards-collector/tests/test_update_owner.rs b/contracts/rewards-collector/osmosis/tests/test_update_owner.rs similarity index 95% rename from contracts/rewards-collector/tests/test_update_owner.rs rename to contracts/rewards-collector/osmosis/tests/test_update_owner.rs index 099ac9926..7b41394c5 100644 --- a/contracts/rewards-collector/tests/test_update_owner.rs +++ b/contracts/rewards-collector/osmosis/tests/test_update_owner.rs @@ -1,7 +1,8 @@ use cosmwasm_std::testing::{mock_env, mock_info}; use mars_owner::{OwnerError::NotOwner, OwnerUpdate}; use mars_red_bank_types::rewards_collector::{ConfigResponse, ExecuteMsg, QueryMsg}; -use mars_rewards_collector::{contract::entry::execute, ContractError}; +use mars_rewards_collector_base::ContractError; +use mars_rewards_collector_osmosis::entry::execute; use crate::helpers::{query, setup_test}; diff --git a/contracts/rewards-collector/tests/test_withdraw.rs b/contracts/rewards-collector/osmosis/tests/test_withdraw.rs similarity index 95% rename from contracts/rewards-collector/tests/test_withdraw.rs rename to contracts/rewards-collector/osmosis/tests/test_withdraw.rs index 88ab1661e..788c71444 100644 --- a/contracts/rewards-collector/tests/test_withdraw.rs +++ b/contracts/rewards-collector/osmosis/tests/test_withdraw.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{testing::mock_env, to_binary, CosmosMsg, SubMsg, Uint128, WasmMsg}; use mars_red_bank_types::rewards_collector::ExecuteMsg; -use mars_rewards_collector::contract::entry::execute; +use mars_rewards_collector_osmosis::entry::execute; use mars_testing::mock_info; mod helpers; diff --git a/contracts/rewards-collector/src/traits.rs b/contracts/rewards-collector/src/traits.rs deleted file mode 100644 index dfdc8cfc6..000000000 --- a/contracts/rewards-collector/src/traits.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::fmt::{Debug, Display}; - -use cosmwasm_std::{CosmosMsg, CustomMsg, CustomQuery, Decimal, Env, QuerierWrapper, Uint128}; -use schemars::JsonSchema; -use serde::{de::DeserializeOwned, Serialize}; - -use crate::ContractResult; - -pub trait Route: - Serialize + DeserializeOwned + Clone + Debug + Display + PartialEq + JsonSchema -where - M: CustomMsg, - Q: CustomQuery, -{ - /// Determine whether the route is valid, given a pair of input and output denoms - fn validate( - &self, - querier: &QuerierWrapper, - denom_in: &str, - denom_out: &str, - ) -> ContractResult<()>; - - /// Build a message for executing the trade, given an input denom and amount - fn build_swap_msg( - &self, - env: &Env, - querier: &QuerierWrapper, - denom_in: &str, - amount: Uint128, - slippage_tolerance: Decimal, - ) -> ContractResult>; -} diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 3524937c8..876610864 100755 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -33,7 +33,7 @@ mars-osmosis = { workspace = true } mars-params = { workspace = true } mars-red-bank = { workspace = true } mars-red-bank-types = { workspace = true } -mars-rewards-collector = { workspace = true } +mars-rewards-collector-osmosis = { workspace = true } mars-testing = { workspace = true } mars-utils = { workspace = true } osmosis-std = { workspace = true } diff --git a/integration-tests/tests/test_oracles.rs b/integration-tests/tests/test_oracles.rs index 207f5907f..6820b771f 100644 --- a/integration-tests/tests/test_oracles.rs +++ b/integration-tests/tests/test_oracles.rs @@ -35,7 +35,7 @@ mod helpers; const OSMOSIS_ORACLE_CONTRACT_NAME: &str = "mars-oracle-osmosis"; const OSMOSIS_RED_BANK_CONTRACT_NAME: &str = "mars-red-bank"; const OSMOSIS_ADDR_PROVIDER_CONTRACT_NAME: &str = "mars-address-provider"; -const OSMOSIS_REWARDS_CONTRACT_NAME: &str = "mars-rewards-collector"; +const OSMOSIS_REWARDS_CONTRACT_NAME: &str = "mars-rewards-collector-osmosis"; const OSMOSIS_INCENTIVES_CONTRACT_NAME: &str = "mars-incentives"; const OSMOSIS_PARAMS_CONTRACT_NAME: &str = "mars-params"; @@ -1078,6 +1078,7 @@ fn setup_redbank(wasm: &Wasm, signer: &SigningAccount) -> (Strin channel_id: "channel-1".to_string(), timeout_seconds: 60, slippage_tolerance: Decimal::new(Uint128::from(1u128)), + neutron_ibc_config: None, }, ); diff --git a/integration-tests/tests/test_rewards_collector.rs b/integration-tests/tests/test_rewards_collector.rs index 16b4972f2..cdaa615d2 100644 --- a/integration-tests/tests/test_rewards_collector.rs +++ b/integration-tests/tests/test_rewards_collector.rs @@ -20,7 +20,7 @@ mod cosmos_bank; mod helpers; const OSMOSIS_ADDR_PROVIDER_CONTRACT_NAME: &str = "mars-address-provider"; -const OSMOSIS_REWARDS_CONTRACT_NAME: &str = "mars-rewards-collector"; +const OSMOSIS_REWARDS_CONTRACT_NAME: &str = "mars-rewards-collector-osmosis"; const OSMOSIS_SWAPPER_CONTRACT_NAME: &str = "mars-swapper-osmosis"; #[test] @@ -67,6 +67,7 @@ fn swapping_rewards() { channel_id: "channel-1".to_string(), timeout_seconds: 60, slippage_tolerance: Decimal::percent(1), + neutron_ibc_config: None, }, ); @@ -325,6 +326,7 @@ fn distribute_rewards_if_ibc_channel_invalid() { channel_id: "".to_string(), timeout_seconds: 60, slippage_tolerance: Decimal::percent(1), + neutron_ibc_config: None, }, ); @@ -365,6 +367,7 @@ fn distribute_rewards_if_ibc_channel_invalid() { channel_id: Some("channel-1".to_string()), timeout_seconds: None, slippage_tolerance: None, + neutron_ibc_config: None, }, }, &[], diff --git a/packages/testing/Cargo.toml b/packages/testing/Cargo.toml index 7cd04417f..146b1c350 100644 --- a/packages/testing/Cargo.toml +++ b/packages/testing/Cargo.toml @@ -35,7 +35,7 @@ mars-owner = { workspace = true } mars-params = { workspace = true } mars-red-bank = { workspace = true } mars-red-bank-types = { workspace = true } -mars-rewards-collector = { workspace = true } +mars-rewards-collector-osmosis = { workspace = true } mars-swapper-astroport = { workspace = true } prost = { workspace = true } pyth-sdk-cw = { workspace = true } diff --git a/packages/testing/src/integration/mock_contracts.rs b/packages/testing/src/integration/mock_contracts.rs index e68096233..f18e124d9 100644 --- a/packages/testing/src/integration/mock_contracts.rs +++ b/packages/testing/src/integration/mock_contracts.rs @@ -43,9 +43,9 @@ pub fn mock_red_bank_contract() -> Box> { pub fn mock_rewards_collector_osmosis_contract() -> Box> { let contract = ContractWrapper::new( - mars_rewards_collector::contract::entry::execute, - mars_rewards_collector::contract::entry::instantiate, - mars_rewards_collector::contract::entry::query, + mars_rewards_collector_osmosis::entry::execute, + mars_rewards_collector_osmosis::entry::instantiate, + mars_rewards_collector_osmosis::entry::query, ); Box::new(contract) } diff --git a/packages/testing/src/integration/mock_env.rs b/packages/testing/src/integration/mock_env.rs index 4c9dfba5b..f4da4f848 100644 --- a/packages/testing/src/integration/mock_env.rs +++ b/packages/testing/src/integration/mock_env.rs @@ -828,6 +828,7 @@ impl MockEnvBuilder { channel_id: "0".to_string(), timeout_seconds: 900, slippage_tolerance: self.slippage_tolerance, + neutron_ibc_config: None, }, &[], "rewards-collector", diff --git a/packages/types/src/rewards_collector.rs b/packages/types/src/rewards_collector.rs index d63387f8a..ee11c8f58 100644 --- a/packages/types/src/rewards_collector.rs +++ b/packages/types/src/rewards_collector.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, Api, Decimal, StdResult, Uint128}; +use cosmwasm_std::{Addr, Api, Coin, Decimal, StdResult, Uint128}; use mars_owner::OwnerUpdate; use mars_utils::{ error::ValidationError, @@ -26,6 +26,8 @@ pub struct InstantiateMsg { pub timeout_seconds: u64, /// Maximum percentage of price movement (minimum amount you accept to receive during swap) pub slippage_tolerance: Decimal, + /// Neutron Ibc config + pub neutron_ibc_config: Option, } #[cw_serde] @@ -44,6 +46,15 @@ pub struct Config { pub timeout_seconds: u64, /// Maximum percentage of price movement (minimum amount you accept to receive during swap) pub slippage_tolerance: Decimal, + /// Neutron IBC config + pub neutron_ibc_config: Option, +} + +#[cw_serde] +pub struct NeutronIbcConfig { + pub source_port: String, + pub acc_fee: Vec, + pub timeout_fee: Vec, } impl Config { @@ -77,6 +88,7 @@ impl Config { channel_id: msg.channel_id, timeout_seconds: msg.timeout_seconds, slippage_tolerance: msg.slippage_tolerance, + neutron_ibc_config: msg.neutron_ibc_config, }) } } @@ -98,6 +110,8 @@ pub struct UpdateConfig { pub timeout_seconds: Option, /// Maximum percentage of price movement (minimum amount you accept to receive during swap) pub slippage_tolerance: Option, + /// Neutron Ibc config + pub neutron_ibc_config: Option, } #[cw_serde] diff --git a/schema.Makefile.toml b/schema.Makefile.toml index 35a3ed621..ae6f07438 100644 --- a/schema.Makefile.toml +++ b/schema.Makefile.toml @@ -14,7 +14,7 @@ fn main() -> std::io::Result<()> { "mars-address-provider", "mars-incentives", "mars-red-bank", - "mars-rewards-collector", + "mars-rewards-collector-base", "mars-params", "mars-swapper-osmosis", "mars-swapper-astroport", diff --git a/schemas/mars-address-provider/mars-address-provider.json b/schemas/mars-address-provider/mars-address-provider.json index ea4a34e9c..3d9aaad94 100644 --- a/schemas/mars-address-provider/mars-address-provider.json +++ b/schemas/mars-address-provider/mars-address-provider.json @@ -77,7 +77,8 @@ "oracle", "red_bank", "rewards_collector", - "params" + "params", + "credit_manager" ] }, { @@ -280,7 +281,8 @@ "oracle", "red_bank", "rewards_collector", - "params" + "params", + "credit_manager" ] }, { @@ -351,7 +353,8 @@ "oracle", "red_bank", "rewards_collector", - "params" + "params", + "credit_manager" ] }, { @@ -425,7 +428,8 @@ "oracle", "red_bank", "rewards_collector", - "params" + "params", + "credit_manager" ] }, { @@ -499,7 +503,8 @@ "oracle", "red_bank", "rewards_collector", - "params" + "params", + "credit_manager" ] }, { diff --git a/schemas/mars-params/mars-params.json b/schemas/mars-params/mars-params.json index 873370b7f..48c010b14 100644 --- a/schemas/mars-params/mars-params.json +++ b/schemas/mars-params/mars-params.json @@ -7,10 +7,15 @@ "title": "InstantiateMsg", "type": "object", "required": [ + "address_provider", "owner", "target_health_factor" ], "properties": { + "address_provider": { + "description": "Address of the address provider contract", + "type": "string" + }, "owner": { "description": "Contract's owner", "type": "string" @@ -694,6 +699,28 @@ } }, "additionalProperties": false + }, + { + "description": "Compute the total amount deposited of the given asset across Red Bank and Credit Manager.", + "type": "object", + "required": [ + "total_deposit" + ], + "properties": { + "total_deposit": { + "type": "object", + "required": [ + "denom" + ], + "properties": { + "denom": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } ] }, @@ -1312,6 +1339,29 @@ "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", "type": "string" }, + "total_deposit": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, "vault_config": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "VaultConfigBase_for_Addr", diff --git a/schemas/mars-rewards-collector/mars-rewards-collector.json b/schemas/mars-rewards-collector-base/mars-rewards-collector-base.json similarity index 83% rename from schemas/mars-rewards-collector/mars-rewards-collector.json rename to schemas/mars-rewards-collector-base/mars-rewards-collector-base.json index e29b465b3..8d5f29cf4 100644 --- a/schemas/mars-rewards-collector/mars-rewards-collector.json +++ b/schemas/mars-rewards-collector-base/mars-rewards-collector-base.json @@ -1,5 +1,5 @@ { - "contract_name": "mars-rewards-collector", + "contract_name": "mars-rewards-collector-base", "contract_version": "1.1.0", "idl_version": "1.0.0", "instantiate": { @@ -29,6 +29,17 @@ "description": "The asset to which the fee collector share is converted", "type": "string" }, + "neutron_ibc_config": { + "description": "Neutron Ibc config", + "anyOf": [ + { + "$ref": "#/definitions/NeutronIbcConfig" + }, + { + "type": "null" + } + ] + }, "owner": { "description": "The contract's owner", "type": "string" @@ -62,9 +73,54 @@ }, "additionalProperties": false, "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", "type": "string" + }, + "NeutronIbcConfig": { + "type": "object", + "required": [ + "acc_fee", + "source_port", + "timeout_fee" + ], + "properties": { + "acc_fee": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "source_port": { + "type": "string" + }, + "timeout_fee": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" } } }, @@ -244,10 +300,51 @@ } ], "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", "type": "string" }, + "NeutronIbcConfig": { + "type": "object", + "required": [ + "acc_fee", + "source_port", + "timeout_fee" + ], + "properties": { + "acc_fee": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "source_port": { + "type": "string" + }, + "timeout_fee": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + }, + "additionalProperties": false + }, "OwnerUpdate": { "oneOf": [ { @@ -352,6 +449,17 @@ "null" ] }, + "neutron_ibc_config": { + "description": "Neutron Ibc config", + "anyOf": [ + { + "$ref": "#/definitions/NeutronIbcConfig" + }, + { + "type": "null" + } + ] + }, "safety_fund_denom": { "description": "The asset to which the safety fund share is converted", "type": [ diff --git a/scripts/deploy/base/deployer.ts b/scripts/deploy/base/deployer.ts index 7ff2db106..bc4dd6230 100644 --- a/scripts/deploy/base/deployer.ts +++ b/scripts/deploy/base/deployer.ts @@ -25,7 +25,7 @@ import { } from '../../types/generated/mars-red-bank/MarsRedBank.types' import { InstantiateMsg as AddressProviderInstantiateMsg } from '../../types/generated/mars-address-provider/MarsAddressProvider.types' import { InstantiateMsg as IncentivesInstantiateMsg } from '../../types/generated/mars-incentives/MarsIncentives.types' -import { InstantiateMsg as RewardsInstantiateMsg } from '../../types/generated/mars-rewards-collector/MarsRewardsCollector.types' +import { InstantiateMsg as RewardsInstantiateMsg } from '../../types/generated/mars-rewards-collector-base/MarsRewardsCollectorBase.types' import { WasmOracleCustomInitParams, InstantiateMsg as WasmOracleInstantiateMsg, diff --git a/scripts/types/generated/mars-address-provider/MarsAddressProvider.types.ts b/scripts/types/generated/mars-address-provider/MarsAddressProvider.types.ts index e568853d7..695663ba9 100644 --- a/scripts/types/generated/mars-address-provider/MarsAddressProvider.types.ts +++ b/scripts/types/generated/mars-address-provider/MarsAddressProvider.types.ts @@ -20,7 +20,7 @@ export type ExecuteMsg = update_owner: OwnerUpdate } export type MarsAddressType = - | ('incentives' | 'oracle' | 'red_bank' | 'rewards_collector' | 'params') + | ('incentives' | 'oracle' | 'red_bank' | 'rewards_collector' | 'params' | 'credit_manager') | 'protocol_admin' | 'fee_collector' | 'safety_fund' diff --git a/scripts/types/generated/mars-params/MarsParams.client.ts b/scripts/types/generated/mars-params/MarsParams.client.ts index ef6a19f05..610c71efe 100644 --- a/scripts/types/generated/mars-params/MarsParams.client.ts +++ b/scripts/types/generated/mars-params/MarsParams.client.ts @@ -57,6 +57,7 @@ export interface MarsParamsReadOnlyInterface { startAfter?: string }) => Promise targetHealthFactor: () => Promise + totalDeposit: ({ denom }: { denom: string }) => Promise } export class MarsParamsQueryClient implements MarsParamsReadOnlyInterface { client: CosmWasmClient @@ -71,6 +72,7 @@ export class MarsParamsQueryClient implements MarsParamsReadOnlyInterface { this.vaultConfig = this.vaultConfig.bind(this) this.allVaultConfigs = this.allVaultConfigs.bind(this) this.targetHealthFactor = this.targetHealthFactor.bind(this) + this.totalDeposit = this.totalDeposit.bind(this) } owner = async (): Promise => { @@ -125,6 +127,13 @@ export class MarsParamsQueryClient implements MarsParamsReadOnlyInterface { target_health_factor: {}, }) } + totalDeposit = async ({ denom }: { denom: string }): Promise => { + return this.client.queryContractSmart(this.contractAddress, { + total_deposit: { + denom, + }, + }) + } } export interface MarsParamsInterface extends MarsParamsReadOnlyInterface { contractAddress: string diff --git a/scripts/types/generated/mars-params/MarsParams.react-query.ts b/scripts/types/generated/mars-params/MarsParams.react-query.ts index 9b4c1121e..beae94e83 100644 --- a/scripts/types/generated/mars-params/MarsParams.react-query.ts +++ b/scripts/types/generated/mars-params/MarsParams.react-query.ts @@ -65,6 +65,10 @@ export const marsParamsQueryKeys = { [ { ...marsParamsQueryKeys.address(contractAddress)[0], method: 'target_health_factor', args }, ] as const, + totalDeposit: (contractAddress: string | undefined, args?: Record) => + [ + { ...marsParamsQueryKeys.address(contractAddress)[0], method: 'total_deposit', args }, + ] as const, } export interface MarsParamsReactQuery { client: MarsParamsQueryClient | undefined @@ -75,6 +79,27 @@ export interface MarsParamsReactQuery { initialData?: undefined } } +export interface MarsParamsTotalDepositQuery extends MarsParamsReactQuery { + args: { + denom: string + } +} +export function useMarsParamsTotalDepositQuery({ + client, + args, + options, +}: MarsParamsTotalDepositQuery) { + return useQuery( + marsParamsQueryKeys.totalDeposit(client?.contractAddress, args), + () => + client + ? client.totalDeposit({ + denom: args.denom, + }) + : Promise.reject(new Error('Invalid client')), + { ...options, enabled: !!client && (options?.enabled != undefined ? options.enabled : true) }, + ) +} export interface MarsParamsTargetHealthFactorQuery extends MarsParamsReactQuery {} export function useMarsParamsTargetHealthFactorQuery({ diff --git a/scripts/types/generated/mars-params/MarsParams.types.ts b/scripts/types/generated/mars-params/MarsParams.types.ts index 6c2e27625..3a4c7a3a5 100644 --- a/scripts/types/generated/mars-params/MarsParams.types.ts +++ b/scripts/types/generated/mars-params/MarsParams.types.ts @@ -7,6 +7,7 @@ export type Decimal = string export interface InstantiateMsg { + address_provider: string owner: string target_health_factor: Decimal } @@ -154,6 +155,11 @@ export type QueryMsg = | { target_health_factor: {} } + | { + total_deposit: { + denom: string + } + } export type HlsAssetTypeForAddr = | { coin: { diff --git a/scripts/types/generated/mars-rewards-collector/MarsRewardsCollector.client.ts b/scripts/types/generated/mars-rewards-collector-base/MarsRewardsCollectorBase.client.ts similarity index 91% rename from scripts/types/generated/mars-rewards-collector/MarsRewardsCollector.client.ts rename to scripts/types/generated/mars-rewards-collector-base/MarsRewardsCollectorBase.client.ts index 7d8b9fb44..44808d0c6 100644 --- a/scripts/types/generated/mars-rewards-collector/MarsRewardsCollector.client.ts +++ b/scripts/types/generated/mars-rewards-collector-base/MarsRewardsCollectorBase.client.ts @@ -6,22 +6,26 @@ */ import { CosmWasmClient, SigningCosmWasmClient, ExecuteResult } from '@cosmjs/cosmwasm-stargate' -import { Coin, StdFee } from '@cosmjs/amino' +import { StdFee } from '@cosmjs/amino' import { + Uint128, Decimal, InstantiateMsg, + NeutronIbcConfig, + Coin, ExecuteMsg, OwnerUpdate, - Uint128, UpdateConfig, QueryMsg, ConfigResponse, -} from './MarsRewardsCollector.types' -export interface MarsRewardsCollectorReadOnlyInterface { +} from './MarsRewardsCollectorBase.types' +export interface MarsRewardsCollectorBaseReadOnlyInterface { contractAddress: string config: () => Promise } -export class MarsRewardsCollectorQueryClient implements MarsRewardsCollectorReadOnlyInterface { +export class MarsRewardsCollectorBaseQueryClient + implements MarsRewardsCollectorBaseReadOnlyInterface +{ client: CosmWasmClient contractAddress: string @@ -37,7 +41,8 @@ export class MarsRewardsCollectorQueryClient implements MarsRewardsCollectorRead }) } } -export interface MarsRewardsCollectorInterface extends MarsRewardsCollectorReadOnlyInterface { +export interface MarsRewardsCollectorBaseInterface + extends MarsRewardsCollectorBaseReadOnlyInterface { contractAddress: string sender: string updateOwner: ( @@ -107,9 +112,9 @@ export interface MarsRewardsCollectorInterface extends MarsRewardsCollectorReadO _funds?: Coin[], ) => Promise } -export class MarsRewardsCollectorClient - extends MarsRewardsCollectorQueryClient - implements MarsRewardsCollectorInterface +export class MarsRewardsCollectorBaseClient + extends MarsRewardsCollectorBaseQueryClient + implements MarsRewardsCollectorBaseInterface { client: SigningCosmWasmClient sender: string diff --git a/scripts/types/generated/mars-rewards-collector/MarsRewardsCollector.react-query.ts b/scripts/types/generated/mars-rewards-collector-base/MarsRewardsCollectorBase.react-query.ts similarity index 55% rename from scripts/types/generated/mars-rewards-collector/MarsRewardsCollector.react-query.ts rename to scripts/types/generated/mars-rewards-collector-base/MarsRewardsCollectorBase.react-query.ts index 6e749847a..5db72cd7f 100644 --- a/scripts/types/generated/mars-rewards-collector/MarsRewardsCollector.react-query.ts +++ b/scripts/types/generated/mars-rewards-collector-base/MarsRewardsCollectorBase.react-query.ts @@ -7,36 +7,38 @@ import { UseQueryOptions, useQuery, useMutation, UseMutationOptions } from '@tanstack/react-query' import { ExecuteResult } from '@cosmjs/cosmwasm-stargate' -import { StdFee, Coin } from '@cosmjs/amino' +import { StdFee } from '@cosmjs/amino' import { + Uint128, Decimal, InstantiateMsg, + NeutronIbcConfig, + Coin, ExecuteMsg, OwnerUpdate, - Uint128, UpdateConfig, QueryMsg, ConfigResponse, -} from './MarsRewardsCollector.types' +} from './MarsRewardsCollectorBase.types' import { - MarsRewardsCollectorQueryClient, - MarsRewardsCollectorClient, -} from './MarsRewardsCollector.client' -export const marsRewardsCollectorQueryKeys = { + MarsRewardsCollectorBaseQueryClient, + MarsRewardsCollectorBaseClient, +} from './MarsRewardsCollectorBase.client' +export const marsRewardsCollectorBaseQueryKeys = { contract: [ { - contract: 'marsRewardsCollector', + contract: 'marsRewardsCollectorBase', }, ] as const, address: (contractAddress: string | undefined) => - [{ ...marsRewardsCollectorQueryKeys.contract[0], address: contractAddress }] as const, + [{ ...marsRewardsCollectorBaseQueryKeys.contract[0], address: contractAddress }] as const, config: (contractAddress: string | undefined, args?: Record) => [ - { ...marsRewardsCollectorQueryKeys.address(contractAddress)[0], method: 'config', args }, + { ...marsRewardsCollectorBaseQueryKeys.address(contractAddress)[0], method: 'config', args }, ] as const, } -export interface MarsRewardsCollectorReactQuery { - client: MarsRewardsCollectorQueryClient | undefined +export interface MarsRewardsCollectorBaseReactQuery { + client: MarsRewardsCollectorBaseQueryClient | undefined options?: Omit< UseQueryOptions, "'queryKey' | 'queryFn' | 'initialData'" @@ -44,20 +46,20 @@ export interface MarsRewardsCollectorReactQuery { initialData?: undefined } } -export interface MarsRewardsCollectorConfigQuery - extends MarsRewardsCollectorReactQuery {} -export function useMarsRewardsCollectorConfigQuery({ +export interface MarsRewardsCollectorBaseConfigQuery + extends MarsRewardsCollectorBaseReactQuery {} +export function useMarsRewardsCollectorBaseConfigQuery({ client, options, -}: MarsRewardsCollectorConfigQuery) { +}: MarsRewardsCollectorBaseConfigQuery) { return useQuery( - marsRewardsCollectorQueryKeys.config(client?.contractAddress), + marsRewardsCollectorBaseQueryKeys.config(client?.contractAddress), () => (client ? client.config() : Promise.reject(new Error('Invalid client'))), { ...options, enabled: !!client && (options?.enabled != undefined ? options.enabled : true) }, ) } -export interface MarsRewardsCollectorClaimIncentiveRewardsMutation { - client: MarsRewardsCollectorClient +export interface MarsRewardsCollectorBaseClaimIncentiveRewardsMutation { + client: MarsRewardsCollectorBaseClient msg: { limit?: number startAfterCollateralDenom?: string @@ -69,20 +71,20 @@ export interface MarsRewardsCollectorClaimIncentiveRewardsMutation { funds?: Coin[] } } -export function useMarsRewardsCollectorClaimIncentiveRewardsMutation( +export function useMarsRewardsCollectorBaseClaimIncentiveRewardsMutation( options?: Omit< - UseMutationOptions, + UseMutationOptions, 'mutationFn' >, ) { - return useMutation( + return useMutation( ({ client, msg, args: { fee, memo, funds } = {} }) => client.claimIncentiveRewards(msg, fee, memo, funds), options, ) } -export interface MarsRewardsCollectorSwapAssetMutation { - client: MarsRewardsCollectorClient +export interface MarsRewardsCollectorBaseSwapAssetMutation { + client: MarsRewardsCollectorBaseClient msg: { amount?: Uint128 denom: string @@ -93,19 +95,19 @@ export interface MarsRewardsCollectorSwapAssetMutation { funds?: Coin[] } } -export function useMarsRewardsCollectorSwapAssetMutation( +export function useMarsRewardsCollectorBaseSwapAssetMutation( options?: Omit< - UseMutationOptions, + UseMutationOptions, 'mutationFn' >, ) { - return useMutation( + return useMutation( ({ client, msg, args: { fee, memo, funds } = {} }) => client.swapAsset(msg, fee, memo, funds), options, ) } -export interface MarsRewardsCollectorDistributeRewardsMutation { - client: MarsRewardsCollectorClient +export interface MarsRewardsCollectorBaseDistributeRewardsMutation { + client: MarsRewardsCollectorBaseClient msg: { amount?: Uint128 denom: string @@ -116,20 +118,20 @@ export interface MarsRewardsCollectorDistributeRewardsMutation { funds?: Coin[] } } -export function useMarsRewardsCollectorDistributeRewardsMutation( +export function useMarsRewardsCollectorBaseDistributeRewardsMutation( options?: Omit< - UseMutationOptions, + UseMutationOptions, 'mutationFn' >, ) { - return useMutation( + return useMutation( ({ client, msg, args: { fee, memo, funds } = {} }) => client.distributeRewards(msg, fee, memo, funds), options, ) } -export interface MarsRewardsCollectorWithdrawFromRedBankMutation { - client: MarsRewardsCollectorClient +export interface MarsRewardsCollectorBaseWithdrawFromRedBankMutation { + client: MarsRewardsCollectorBaseClient msg: { amount?: Uint128 denom: string @@ -140,20 +142,20 @@ export interface MarsRewardsCollectorWithdrawFromRedBankMutation { funds?: Coin[] } } -export function useMarsRewardsCollectorWithdrawFromRedBankMutation( +export function useMarsRewardsCollectorBaseWithdrawFromRedBankMutation( options?: Omit< - UseMutationOptions, + UseMutationOptions, 'mutationFn' >, ) { - return useMutation( + return useMutation( ({ client, msg, args: { fee, memo, funds } = {} }) => client.withdrawFromRedBank(msg, fee, memo, funds), options, ) } -export interface MarsRewardsCollectorUpdateConfigMutation { - client: MarsRewardsCollectorClient +export interface MarsRewardsCollectorBaseUpdateConfigMutation { + client: MarsRewardsCollectorBaseClient msg: { newCfg: UpdateConfig } @@ -163,20 +165,20 @@ export interface MarsRewardsCollectorUpdateConfigMutation { funds?: Coin[] } } -export function useMarsRewardsCollectorUpdateConfigMutation( +export function useMarsRewardsCollectorBaseUpdateConfigMutation( options?: Omit< - UseMutationOptions, + UseMutationOptions, 'mutationFn' >, ) { - return useMutation( + return useMutation( ({ client, msg, args: { fee, memo, funds } = {} }) => client.updateConfig(msg, fee, memo, funds), options, ) } -export interface MarsRewardsCollectorUpdateOwnerMutation { - client: MarsRewardsCollectorClient +export interface MarsRewardsCollectorBaseUpdateOwnerMutation { + client: MarsRewardsCollectorBaseClient msg: OwnerUpdate args?: { fee?: number | StdFee | 'auto' @@ -184,13 +186,13 @@ export interface MarsRewardsCollectorUpdateOwnerMutation { funds?: Coin[] } } -export function useMarsRewardsCollectorUpdateOwnerMutation( +export function useMarsRewardsCollectorBaseUpdateOwnerMutation( options?: Omit< - UseMutationOptions, + UseMutationOptions, 'mutationFn' >, ) { - return useMutation( + return useMutation( ({ client, msg, args: { fee, memo, funds } = {} }) => client.updateOwner(msg, fee, memo, funds), options, ) diff --git a/scripts/types/generated/mars-rewards-collector/MarsRewardsCollector.types.ts b/scripts/types/generated/mars-rewards-collector-base/MarsRewardsCollectorBase.types.ts similarity index 88% rename from scripts/types/generated/mars-rewards-collector/MarsRewardsCollector.types.ts rename to scripts/types/generated/mars-rewards-collector-base/MarsRewardsCollectorBase.types.ts index d5bc432c2..a1148e582 100644 --- a/scripts/types/generated/mars-rewards-collector/MarsRewardsCollector.types.ts +++ b/scripts/types/generated/mars-rewards-collector-base/MarsRewardsCollectorBase.types.ts @@ -5,17 +5,29 @@ * and run the @cosmwasm/ts-codegen generate command to regenerate this file. */ +export type Uint128 = string export type Decimal = string export interface InstantiateMsg { address_provider: string channel_id: string fee_collector_denom: string + neutron_ibc_config?: NeutronIbcConfig | null owner: string safety_fund_denom: string safety_tax_rate: Decimal slippage_tolerance: Decimal timeout_seconds: number } +export interface NeutronIbcConfig { + acc_fee: Coin[] + source_port: string + timeout_fee: Coin[] +} +export interface Coin { + amount: Uint128 + denom: string + [k: string]: unknown +} export type ExecuteMsg = | { update_owner: OwnerUpdate @@ -65,11 +77,11 @@ export type OwnerUpdate = } } | 'clear_emergency_owner' -export type Uint128 = string export interface UpdateConfig { address_provider?: string | null channel_id?: string | null fee_collector_denom?: string | null + neutron_ibc_config?: NeutronIbcConfig | null safety_fund_denom?: string | null safety_tax_rate?: Decimal | null slippage_tolerance?: Decimal | null diff --git a/scripts/types/generated/mars-rewards-collector/bundle.ts b/scripts/types/generated/mars-rewards-collector-base/bundle.ts similarity index 53% rename from scripts/types/generated/mars-rewards-collector/bundle.ts rename to scripts/types/generated/mars-rewards-collector-base/bundle.ts index e234a6e87..61bb087c8 100644 --- a/scripts/types/generated/mars-rewards-collector/bundle.ts +++ b/scripts/types/generated/mars-rewards-collector-base/bundle.ts @@ -5,9 +5,9 @@ * and run the @cosmwasm/ts-codegen generate command to regenerate this file. */ -import * as _18 from './MarsRewardsCollector.types' -import * as _19 from './MarsRewardsCollector.client' -import * as _20 from './MarsRewardsCollector.react-query' +import * as _18 from './MarsRewardsCollectorBase.types' +import * as _19 from './MarsRewardsCollectorBase.client' +import * as _20 from './MarsRewardsCollectorBase.react-query' export namespace contracts { - export const MarsRewardsCollector = { ..._18, ..._19, ..._20 } + export const MarsRewardsCollectorBase = { ..._18, ..._19, ..._20 } } diff --git a/scripts/types/msg.ts b/scripts/types/msg.ts index da8eed8e8..f3ca45fd6 100644 --- a/scripts/types/msg.ts +++ b/scripts/types/msg.ts @@ -3,7 +3,7 @@ import { InstantiateMsg as AstroportSwapperInstantiateMsg } from './generated/ma import { InstantiateMsg as RedBankInstantiateMsg } from './generated/mars-red-bank/MarsRedBank.types' import { InstantiateMsg as AddressProviderInstantiateMsg } from './generated/mars-address-provider/MarsAddressProvider.types' import { InstantiateMsg as IncentivesInstantiateMsg } from './generated/mars-incentives/MarsIncentives.types' -import { InstantiateMsg as RewardsInstantiateMsg } from './generated/mars-rewards-collector/MarsRewardsCollector.types' +import { InstantiateMsg as RewardsInstantiateMsg } from './generated/mars-rewards-collector-base/MarsRewardsCollectorBase.types' import { InstantiateMsg as WasmOracleInstantiateMsg } from './generated/mars-oracle-wasm/MarsOracleWasm.types' import { InstantiateMsg as OsmosisSwapperInstantiateMsg } from './generated/mars-swapper-osmosis/MarsSwapperOsmosis.types' import { InstantiateMsg as OsmosisOracleInstantiateMsg } from './generated/mars-oracle-osmosis/MarsOracleOsmosis.types' From 3d3a10571b2398a2914a1c0b9149cd505c2a3faf Mon Sep 17 00:00:00 2001 From: Pacman Date: Thu, 20 Jul 2023 17:21:01 +0200 Subject: [PATCH 12/27] Bump version. Bump deps. --- Cargo.lock | 352 ++++++------------ Cargo.toml | 44 +-- contracts/oracle/osmosis/src/migrations.rs | 4 +- .../mars-address-provider.json | 2 +- schemas/mars-incentives/mars-incentives.json | 2 +- .../mars-oracle-osmosis.json | 2 +- .../mars-oracle-wasm/mars-oracle-wasm.json | 2 +- schemas/mars-red-bank/mars-red-bank.json | 2 +- .../mars-rewards-collector-base.json | 2 +- .../mars-swapper-astroport.json | 2 +- .../mars-swapper-osmosis.json | 2 +- 11 files changed, 152 insertions(+), 264 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3c902c20..de78cd5de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,55 +50,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] -name = "astro-satellite-package" -version = "0.1.0" -source = "git+https://github.com/astroport-fi/astroport_ibc?branch=main#f9f4def037d117275de31fffef36ddda388baf48" +name = "apollo-cw-multi-test" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1c7e04dcaeab52c3390867822b3019f81e239b8bbd3146fb91a83b10fb1f27b" dependencies = [ - "astroport-governance 1.2.0 (git+https://github.com/astroport-fi/astroport-governance?branch=feat/merge_hidden_2023_05_22)", - "cosmwasm-schema", + "anyhow", "cosmwasm-std", + "cw-storage-plus 1.1.0", + "cw-utils 1.0.1", + "derivative", + "itertools", + "k256", + "osmosis-std", + "prost 0.9.0", + "regex", + "schemars", + "serde", + "thiserror", ] [[package]] -name = "astroport" -version = "2.8.0" +name = "astro-satellite-package" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcea351626899d205aab091c891fc878fc9b3c930585fd3ef6222de028d8a7a" +checksum = "5cf737cf762c341a9575ee8fc6da47cc0c4ec11725c9fd0fc5fbb2c31ce0d61a" dependencies = [ + "astroport-governance", "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 0.15.1", - "cw-utils 0.15.1", - "cw20 0.15.1", - "itertools", - "uint", ] [[package]] name = "astroport" version = "2.8.0" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcea351626899d205aab091c891fc878fc9b3c930585fd3ef6222de028d8a7a" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", "cw-utils 0.15.1", - "cw20 0.15.1", - "itertools", - "uint", -] - -[[package]] -name = "astroport" -version = "2.10.0" -source = "git+https://github.com/astroport-fi/astroport-core?branch=feat/merge_hidden_2023_05_22#11e7a81d4b18a40bed916177061a549633e02b1b" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.15.1", - "cw-utils 1.0.1", - "cw20 0.15.1", - "cw3", + "cw20", "itertools", "uint", ] @@ -106,9 +99,10 @@ dependencies = [ [[package]] name = "astroport-factory" version = "1.5.1" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ecf768e2d3153bebfbe0c502ffa4199a52598e9b6e89fca54339615b2de77eb" dependencies = [ - "astroport 2.8.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", @@ -121,16 +115,17 @@ dependencies = [ [[package]] name = "astroport-generator" version = "2.3.0" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6144780ac014665b07616de0cfb35ca6a9411ed821e20c21e02f4f428c8ed51f" dependencies = [ - "astroport 2.8.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0)", - "astroport-governance 1.2.0 (git+https://github.com/astroport-fi/astroport-governance?branch=main)", + "astroport", + "astroport-governance", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", "cw1-whitelist", "cw2 0.15.1", - "cw20 0.15.1", + "cw20", "protobuf 2.28.0", "thiserror", ] @@ -138,48 +133,39 @@ dependencies = [ [[package]] name = "astroport-governance" version = "1.2.0" -source = "git+https://github.com/astroport-fi/astroport-governance?branch=feat/merge_hidden_2023_05_22#1e865abe55093d249b69b538e2d54472b643d6c7" -dependencies = [ - "astroport 2.10.0", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.15.1", - "cw20 0.15.1", -] - -[[package]] -name = "astroport-governance" -version = "1.2.0" -source = "git+https://github.com/astroport-fi/astroport-governance?branch=main#f0ef7c6dde76fc77ce360262923366a5cde3c3f8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72806ace350e81c4e1cab7e275ef91f05bad830275d697d67ad1bd4acc6f016d" dependencies = [ - "astroport 2.10.0", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", - "cw20 0.15.1", + "cw20", ] [[package]] name = "astroport-maker" version = "1.3.1" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92403e5d00e3c77d13d9616661ea9d9308d493fff6bec5e6e5e7bd7b7e0ff6af" dependencies = [ "astro-satellite-package", - "astroport 2.8.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", "cw2 0.15.1", - "cw20 0.15.1", + "cw20", "thiserror", ] [[package]] name = "astroport-native-coin-registry" version = "1.0.1" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "648ed6827a8f11012c0377fb60329204e8511fe46c86db3220113e70bdc57826" dependencies = [ - "astroport 2.8.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", @@ -191,14 +177,15 @@ dependencies = [ [[package]] name = "astroport-pair" version = "1.3.1" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2287463c922ef2a73f03fe6b16b37123f3f26da5a76998e6ddf828856068f7" dependencies = [ - "astroport 2.8.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", "cw2 0.15.1", - "cw20 0.15.1", + "cw20", "integer-sqrt", "protobuf 2.28.0", "thiserror", @@ -207,15 +194,16 @@ dependencies = [ [[package]] name = "astroport-pair-stable" version = "2.1.2" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a262f2b6916e2a83808b246ff16cb2306a416e88b15a47ddbea5f8b666b1a4" dependencies = [ - "astroport 2.8.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", "cw-utils 1.0.1", "cw2 0.15.1", - "cw20 0.15.1", + "cw20", "itertools", "thiserror", ] @@ -223,14 +211,15 @@ dependencies = [ [[package]] name = "astroport-router" version = "1.1.1" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bbb33c00370bd194cf3a166f1e3f4029a2add2bea01a5eb61e886aefbc85b" dependencies = [ - "astroport 2.8.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", "cw2 0.15.1", - "cw20 0.15.1", + "cw20", "integer-sqrt", "thiserror", ] @@ -238,14 +227,15 @@ dependencies = [ [[package]] name = "astroport-staking" version = "1.1.0" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67adbc4240794e886ca1edbc7d46bc8a54c7aca7217d73ddcfbc90e1dbb030e7" dependencies = [ - "astroport 2.8.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", "cw2 0.15.1", - "cw20 0.15.1", + "cw20", "protobuf 2.28.0", "thiserror", ] @@ -253,13 +243,14 @@ dependencies = [ [[package]] name = "astroport-token" version = "1.1.1" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3360383a2e585211da9a455ad57eb100578253b5d18a387f025cadd666604d99" dependencies = [ - "astroport 2.8.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw2 0.15.1", - "cw20 0.15.1", + "cw20", "cw20-base", "snafu", ] @@ -267,24 +258,26 @@ dependencies = [ [[package]] name = "astroport-vesting" version = "1.3.1" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffce7cf86bf4d4f177ef941145352499e802abc4b898032af7808d16cca6371" dependencies = [ - "astroport 2.8.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 0.15.1", "cw-utils 0.15.1", "cw2 0.15.1", - "cw20 0.15.1", + "cw20", "thiserror", ] [[package]] name = "astroport-whitelist" version = "1.0.1" -source = "git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0#3b44a4044b823a145730f66ffaf7ae4205b2cd35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44156757bfab3d4bd208d9b86b890d1478f45d07c8f8d3d1c3e3da91081cb54d" dependencies = [ - "astroport 2.8.0 (git+https://github.com/astroport-fi/astroport-core.git?tag=v2.8.0)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw1-whitelist", @@ -820,10 +813,12 @@ dependencies = [ [[package]] name = "cw-it" version = "0.1.0" -source = "git+https://github.com/apollodao/cw-it.git?rev=af23474183cc56746a0b9785328a0009427caa9a#af23474183cc56746a0b9785328a0009427caa9a" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3120d46b30b900c4b9ab0f996ce527c1187095c75ffa61d9ea5aa6d1e93b41" dependencies = [ "anyhow", - "astroport 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "apollo-cw-multi-test", + "astroport", "astroport-factory", "astroport-generator", "astroport-maker", @@ -839,37 +834,16 @@ dependencies = [ "cosmrs", "cosmwasm-schema", "cosmwasm-std", - "cw-multi-test 0.16.2", - "cw20 0.15.1", - "osmosis-std 0.16.1", - "osmosis-test-tube 15.1.0", + "cw20", + "osmosis-std", + "osmosis-test-tube", "paste", "prost 0.11.9", "regex", "serde", "serde_json", "strum", - "test-tube 0.1.2", - "thiserror", -] - -[[package]] -name = "cw-multi-test" -version = "0.16.2" -source = "git+https://github.com/pacmanifold/cw-multi-test.git?rev=a0a28465f3ffee47831c2f20cc20c15c599e70c5#a0a28465f3ffee47831c2f20cc20c15c599e70c5" -dependencies = [ - "anyhow", - "cosmwasm-std", - "cw-storage-plus 1.1.0", - "cw-utils 1.0.1", - "derivative", - "itertools", - "k256", - "osmosis-std 0.15.3", - "prost 0.9.0", - "regex", - "schemars", - "serde", + "test-tube", "thiserror", ] @@ -1024,19 +998,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cw20" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011c45920f8200bd5d32d4fe52502506f64f2f75651ab408054d4cfc75ca3a9b" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 1.0.1", - "schemars", - "serde", -] - [[package]] name = "cw20-base" version = "0.15.1" @@ -1048,28 +1009,13 @@ dependencies = [ "cw-storage-plus 0.15.1", "cw-utils 0.15.1", "cw2 0.15.1", - "cw20 0.15.1", + "cw20", "schemars", "semver", "serde", "thiserror", ] -[[package]] -name = "cw3" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171af3d9127de6805a7dd819fb070c7d2f6c3ea85f4193f42cef259f0a7f33d5" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 1.0.1", - "cw20 1.1.0", - "schemars", - "serde", - "thiserror", -] - [[package]] name = "der" version = "0.6.1" @@ -1777,7 +1723,7 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "mars-address-provider" -version = "1.1.0" +version = "1.2.0" dependencies = [ "bech32", "cosmwasm-schema", @@ -1792,7 +1738,7 @@ dependencies = [ [[package]] name = "mars-health" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-std", "mars-params", @@ -1803,7 +1749,7 @@ dependencies = [ [[package]] name = "mars-incentives" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1814,19 +1760,19 @@ dependencies = [ "mars-red-bank-types", "mars-testing", "mars-utils", - "osmosis-std 0.16.1", + "osmosis-std", "test-case", "thiserror", ] [[package]] name = "mars-integration-tests" -version = "1.1.0" +version = "1.2.0" dependencies = [ "anyhow", "cosmwasm-std", "cw-it", - "cw-multi-test 0.16.5", + "cw-multi-test", "mars-oracle-base", "mars-oracle-osmosis", "mars-osmosis", @@ -1837,14 +1783,14 @@ dependencies = [ "mars-swapper-osmosis", "mars-testing", "mars-utils", - "osmosis-std 0.16.1", - "osmosis-test-tube 16.1.1", + "osmosis-std", + "osmosis-test-tube", "serde", ] [[package]] name = "mars-interest-rate" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1864,7 +1810,7 @@ dependencies = [ [[package]] name = "mars-oracle-base" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-std", "cw-storage-plus 1.1.0", @@ -1879,7 +1825,7 @@ dependencies = [ [[package]] name = "mars-oracle-osmosis" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1891,7 +1837,7 @@ dependencies = [ "mars-red-bank-types", "mars-testing", "mars-utils", - "osmosis-std 0.16.1", + "osmosis-std", "pyth-sdk-cw", "schemars", "serde", @@ -1899,9 +1845,9 @@ dependencies = [ [[package]] name = "mars-oracle-wasm" -version = "1.1.0" +version = "1.2.0" dependencies = [ - "astroport 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw-it", @@ -1918,10 +1864,10 @@ dependencies = [ [[package]] name = "mars-osmosis" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-std", - "osmosis-std 0.16.1", + "osmosis-std", "serde", ] @@ -1945,7 +1891,7 @@ dependencies = [ "anyhow", "cosmwasm-schema", "cosmwasm-std", - "cw-multi-test 0.16.5", + "cw-multi-test", "cw-storage-plus 1.1.0", "cw2 1.1.0", "mars-interest-rate", @@ -1961,12 +1907,12 @@ dependencies = [ [[package]] name = "mars-red-bank" -version = "1.1.0" +version = "1.2.0" dependencies = [ "anyhow", "cosmwasm-schema", "cosmwasm-std", - "cw-multi-test 0.16.5", + "cw-multi-test", "cw-storage-plus 1.1.0", "cw-utils 1.0.1", "cw2 1.1.0", @@ -1983,7 +1929,7 @@ dependencies = [ [[package]] name = "mars-red-bank-types" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1995,7 +1941,7 @@ dependencies = [ [[package]] name = "mars-rewards-collector-base" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -2005,7 +1951,7 @@ dependencies = [ "mars-red-bank-types", "mars-testing", "mars-utils", - "osmosis-std 0.16.1", + "osmosis-std", "schemars", "serde", "thiserror", @@ -2013,7 +1959,7 @@ dependencies = [ [[package]] name = "mars-rewards-collector-neutron" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -2025,7 +1971,7 @@ dependencies = [ "mars-testing", "mars-utils", "neutron-sdk", - "osmosis-std 0.16.1", + "osmosis-std", "schemars", "serde", "thiserror", @@ -2033,7 +1979,7 @@ dependencies = [ [[package]] name = "mars-rewards-collector-osmosis" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -2044,7 +1990,7 @@ dependencies = [ "mars-rewards-collector-base", "mars-testing", "mars-utils", - "osmosis-std 0.16.1", + "osmosis-std", "schemars", "serde", "thiserror", @@ -2052,10 +1998,10 @@ dependencies = [ [[package]] name = "mars-swapper-astroport" -version = "1.1.0" +version = "1.2.0" dependencies = [ "anyhow", - "astroport 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "astroport", "cosmwasm-schema", "cosmwasm-std", "cw-it", @@ -2069,7 +2015,7 @@ dependencies = [ [[package]] name = "mars-swapper-base" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -2084,17 +2030,17 @@ dependencies = [ [[package]] name = "mars-swapper-mock" -version = "1.1.0" +version = "1.2.0" dependencies = [ "anyhow", "cosmwasm-std", - "cw-multi-test 0.16.5", + "cw-multi-test", "mars-red-bank-types", ] [[package]] name = "mars-swapper-osmosis" -version = "1.1.0" +version = "1.2.0" dependencies = [ "anyhow", "cosmwasm-schema", @@ -2105,18 +2051,18 @@ dependencies = [ "mars-owner", "mars-red-bank-types", "mars-swapper-base", - "osmosis-std 0.16.1", + "osmosis-std", ] [[package]] name = "mars-testing" -version = "1.1.0" +version = "1.2.0" dependencies = [ "anyhow", - "astroport 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "astroport", "cosmwasm-std", "cw-it", - "cw-multi-test 0.16.5", + "cw-multi-test", "mars-address-provider", "mars-incentives", "mars-oracle-osmosis", @@ -2128,14 +2074,14 @@ dependencies = [ "mars-red-bank-types", "mars-rewards-collector-osmosis", "mars-swapper-astroport", - "osmosis-std 0.16.1", + "osmosis-std", "prost 0.11.9", "pyth-sdk-cw", ] [[package]] name = "mars-utils" -version = "1.1.0" +version = "1.2.0" dependencies = [ "cosmwasm-std", "thiserror", @@ -2293,21 +2239,6 @@ version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" -[[package]] -name = "osmosis-std" -version = "0.15.3" -source = "git+https://github.com/apollodao/osmosis-rust.git?rev=cd3e84201b08d168ee848acbebc85b1da7cab3fa#cd3e84201b08d168ee848acbebc85b1da7cab3fa" -dependencies = [ - "chrono", - "cosmwasm-std", - "osmosis-std-derive 0.15.3", - "prost 0.11.9", - "prost-types", - "schemars", - "serde", - "serde-cw-value", -] - [[package]] name = "osmosis-std" version = "0.16.1" @@ -2316,7 +2247,7 @@ checksum = "2fa46d2ad5ae738572887974e000934374ce3546b820505c0ee19ca708e49622" dependencies = [ "chrono", "cosmwasm-std", - "osmosis-std-derive 0.16.1", + "osmosis-std-derive", "prost 0.11.9", "prost-types", "schemars", @@ -2324,17 +2255,6 @@ dependencies = [ "serde-cw-value", ] -[[package]] -name = "osmosis-std-derive" -version = "0.15.3" -source = "git+https://github.com/apollodao/osmosis-rust.git?rev=cd3e84201b08d168ee848acbebc85b1da7cab3fa#cd3e84201b08d168ee848acbebc85b1da7cab3fa" -dependencies = [ - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "osmosis-std-derive" version = "0.16.1" @@ -2348,23 +2268,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "osmosis-test-tube" -version = "15.1.0" -source = "git+https://github.com/apollodao/test-tube.git?rev=800a2af15bdd8270a4d832a2b1b799446fc8e1cf#800a2af15bdd8270a4d832a2b1b799446fc8e1cf" -dependencies = [ - "base64 0.13.1", - "bindgen", - "cosmrs", - "cosmwasm-std", - "osmosis-std 0.16.1", - "prost 0.11.9", - "serde", - "serde_json", - "test-tube 0.1.2", - "thiserror", -] - [[package]] name = "osmosis-test-tube" version = "16.1.1" @@ -2375,11 +2278,11 @@ dependencies = [ "bindgen", "cosmrs", "cosmwasm-std", - "osmosis-std 0.16.1", + "osmosis-std", "prost 0.11.9", "serde", "serde_json", - "test-tube 0.1.5", + "test-tube", "thiserror", ] @@ -3486,21 +3389,6 @@ dependencies = [ "test-case-core", ] -[[package]] -name = "test-tube" -version = "0.1.2" -source = "git+https://github.com/apollodao/test-tube.git?rev=800a2af15bdd8270a4d832a2b1b799446fc8e1cf#800a2af15bdd8270a4d832a2b1b799446fc8e1cf" -dependencies = [ - "base64 0.13.1", - "cosmrs", - "cosmwasm-std", - "osmosis-std 0.16.1", - "prost 0.11.9", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "test-tube" version = "0.1.5" @@ -3510,7 +3398,7 @@ dependencies = [ "base64 0.13.1", "cosmrs", "cosmwasm-std", - "osmosis-std 0.16.1", + "osmosis-std", "prost 0.11.9", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index a00f55987..b1729a4f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = [ resolver = "2" [workspace.package] -version = "1.1.0" +version = "1.2.0" authors = [ "Gabe R. ", "Larry Engineer ", @@ -49,7 +49,7 @@ cw2 = "1.1.0" cw-storage-plus = "1.0.1" cw-utils = "1.0.1" mars-owner = { version = "1.2.0", features = ["emergency-owner"] } -osmosis-std = "0.16.0" +osmosis-std = "0.16.1" prost = { version = "0.11.5", default-features = false, features = ["prost-derive"] } schemars = "0.8.12" serde = { version = "1.0.163", default-features = false, features = ["derive"] } @@ -62,33 +62,33 @@ neutron-sdk = "0.6.0" # dev-dependencies cw-multi-test = "0.16.5" -cw-it = { git = "https://github.com/apollodao/cw-it.git", rev = "af23474183cc56746a0b9785328a0009427caa9a" } +cw-it = "0.1.0" osmosis-test-tube = "16.0.0" test-case = "3.0.0" proptest = "1.1.0" # packages -mars-health = { version = "1.0.0", path = "./packages/health" } -mars-interest-rate = { version = "1.0.0", path = "./packages/interest-rate" } -mars-liquidation = { version = "1.0.0", path = "./packages/liquidation" } -mars-osmosis = { version = "1.0.0", path = "./packages/chains/osmosis" } -mars-params = { version = "1.0.7", path = "./contracts/params" } -mars-red-bank-types = { version = "1.0.0", path = "./packages/types" } -mars-testing = { version = "1.0.0", path = "./packages/testing" } -mars-utils = { version = "1.0.0", path = "./packages/utils" } +mars-health = { path = "./packages/health" } +mars-interest-rate = { path = "./packages/interest-rate" } +mars-liquidation = { path = "./packages/liquidation" } +mars-osmosis = { path = "./packages/chains/osmosis" } +mars-red-bank-types = { path = "./packages/types" } +mars-testing = { path = "./packages/testing" } +mars-utils = { path = "./packages/utils" } # contracts -mars-address-provider = { version = "1.0.0", path = "./contracts/address-provider" } -mars-incentives = { version = "1.0.0", path = "./contracts/incentives" } -mars-oracle-base = { version = "1.0.0", path = "./contracts/oracle/base" } -mars-oracle-osmosis = { version = "1.0.0", path = "./contracts/oracle/osmosis" } -mars-oracle-wasm = { version = "1.0.0", path = "./contracts/oracle/wasm" } -mars-red-bank = { version = "1.0.0", path = "./contracts/red-bank" } -mars-rewards-collector-base = { version = "1.0.0", path = "./contracts/rewards-collector/base" } -mars-rewards-collector-osmosis = { version = "1.0.0", path = "./contracts/rewards-collector/osmosis" } -mars-swapper-base = { version = "1.0.0", path = "./contracts/swapper/base" } -mars-swapper-astroport = { version = "1.0.0", path = "./contracts/swapper/astroport" } -mars-swapper-osmosis = { version = "1.0.0", path = "./contracts/swapper/osmosis" } +mars-address-provider = { path = "./contracts/address-provider" } +mars-incentives = { path = "./contracts/incentives" } +mars-oracle-base = { path = "./contracts/oracle/base" } +mars-oracle-osmosis = { path = "./contracts/oracle/osmosis" } +mars-oracle-wasm = { path = "./contracts/oracle/wasm" } +mars-params = { path = "./contracts/params" } +mars-red-bank = { path = "./contracts/red-bank" } +mars-rewards-collector-base = { path = "./contracts/rewards-collector/base" } +mars-rewards-collector-osmosis = { path = "./contracts/rewards-collector/osmosis" } +mars-swapper-base = { path = "./contracts/swapper/base" } +mars-swapper-astroport = { path = "./contracts/swapper/astroport" } +mars-swapper-osmosis = { path = "./contracts/swapper/osmosis" } [profile.release] codegen-units = 1 diff --git a/contracts/oracle/osmosis/src/migrations.rs b/contracts/oracle/osmosis/src/migrations.rs index 67ecd6176..a10f865b3 100644 --- a/contracts/oracle/osmosis/src/migrations.rs +++ b/contracts/oracle/osmosis/src/migrations.rs @@ -101,7 +101,7 @@ pub mod v1_0_1 { vec![ attr("action", "migrate"), attr("from_version", "1.0.1"), - attr("to_version", "1.1.0") + attr("to_version", "1.2.0") ] ); @@ -131,7 +131,7 @@ pub mod v1_0_1 { vec![ attr("action", "migrate"), attr("from_version", "1.0.1"), - attr("to_version", "1.1.0") + attr("to_version", "1.2.0") ] ); diff --git a/schemas/mars-address-provider/mars-address-provider.json b/schemas/mars-address-provider/mars-address-provider.json index 3d9aaad94..34ce77c5b 100644 --- a/schemas/mars-address-provider/mars-address-provider.json +++ b/schemas/mars-address-provider/mars-address-provider.json @@ -1,6 +1,6 @@ { "contract_name": "mars-address-provider", - "contract_version": "1.1.0", + "contract_version": "1.2.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/schemas/mars-incentives/mars-incentives.json b/schemas/mars-incentives/mars-incentives.json index 7bbb22178..7ef20e6f0 100644 --- a/schemas/mars-incentives/mars-incentives.json +++ b/schemas/mars-incentives/mars-incentives.json @@ -1,6 +1,6 @@ { "contract_name": "mars-incentives", - "contract_version": "1.1.0", + "contract_version": "1.2.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/schemas/mars-oracle-osmosis/mars-oracle-osmosis.json b/schemas/mars-oracle-osmosis/mars-oracle-osmosis.json index f8921e423..1a1ef011e 100644 --- a/schemas/mars-oracle-osmosis/mars-oracle-osmosis.json +++ b/schemas/mars-oracle-osmosis/mars-oracle-osmosis.json @@ -1,6 +1,6 @@ { "contract_name": "mars-oracle-osmosis", - "contract_version": "1.1.0", + "contract_version": "1.2.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/schemas/mars-oracle-wasm/mars-oracle-wasm.json b/schemas/mars-oracle-wasm/mars-oracle-wasm.json index 3578337a7..19d235a88 100644 --- a/schemas/mars-oracle-wasm/mars-oracle-wasm.json +++ b/schemas/mars-oracle-wasm/mars-oracle-wasm.json @@ -1,6 +1,6 @@ { "contract_name": "mars-oracle-wasm", - "contract_version": "1.1.0", + "contract_version": "1.2.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/schemas/mars-red-bank/mars-red-bank.json b/schemas/mars-red-bank/mars-red-bank.json index a42ba09b7..5454bc91b 100644 --- a/schemas/mars-red-bank/mars-red-bank.json +++ b/schemas/mars-red-bank/mars-red-bank.json @@ -1,6 +1,6 @@ { "contract_name": "mars-red-bank", - "contract_version": "1.1.0", + "contract_version": "1.2.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/schemas/mars-rewards-collector-base/mars-rewards-collector-base.json b/schemas/mars-rewards-collector-base/mars-rewards-collector-base.json index 8d5f29cf4..2be4ffe4f 100644 --- a/schemas/mars-rewards-collector-base/mars-rewards-collector-base.json +++ b/schemas/mars-rewards-collector-base/mars-rewards-collector-base.json @@ -1,6 +1,6 @@ { "contract_name": "mars-rewards-collector-base", - "contract_version": "1.1.0", + "contract_version": "1.2.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/schemas/mars-swapper-astroport/mars-swapper-astroport.json b/schemas/mars-swapper-astroport/mars-swapper-astroport.json index 76e63bb85..93ef8f7c2 100644 --- a/schemas/mars-swapper-astroport/mars-swapper-astroport.json +++ b/schemas/mars-swapper-astroport/mars-swapper-astroport.json @@ -1,6 +1,6 @@ { "contract_name": "mars-swapper-astroport", - "contract_version": "1.1.0", + "contract_version": "1.2.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/schemas/mars-swapper-osmosis/mars-swapper-osmosis.json b/schemas/mars-swapper-osmosis/mars-swapper-osmosis.json index 8e270e411..5fd1ac9ee 100644 --- a/schemas/mars-swapper-osmosis/mars-swapper-osmosis.json +++ b/schemas/mars-swapper-osmosis/mars-swapper-osmosis.json @@ -1,6 +1,6 @@ { "contract_name": "mars-swapper-osmosis", - "contract_version": "1.1.0", + "contract_version": "1.2.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", From d9ceff0fc2519ef7d8dcdebe1269fcc4a4054714 Mon Sep 17 00:00:00 2001 From: dancreee Date: Wed, 26 Jul 2023 00:52:49 +0800 Subject: [PATCH 13/27] Create readme for setting up Neutron multisig --- scripts/multisig/neutron/README.md | 82 ++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 scripts/multisig/neutron/README.md diff --git a/scripts/multisig/neutron/README.md b/scripts/multisig/neutron/README.md new file mode 100644 index 000000000..baa04d376 --- /dev/null +++ b/scripts/multisig/neutron/README.md @@ -0,0 +1,82 @@ +# Neutron Multisig Overview + +The multisig on Neutron is set to have 5 multisig holders with a threshold of 3, meaning that 3 signatures are needed for any transaction to pass. + +## Set up Neutrond + +Neutrond is the daemon for the neutron blockchain. To install, follow [this documentation](https://docs.neutron.org/neutron/build-and-run/neutron-build). + +## Set up individual account as a multisig signer on your local network + +1. Create the account - to use a consistent naming, we will use [name]\_ntrn e.g. dane_ntrn (similarly on other chains e.g. dane_osmo, dane_mars). It is up to the signer if they wish to use a Ledger or other hardware wallet - or not. + +The benefit is that you will be more secure of a signer +The downsides are that: +a. Some Ledgers are not able to sign large messages such as contract uploads +b. If you are travelling a lot it's best to leave your hardware wallet at home in a secure place, and so if this is the case it might actually be more secure to have a hot wallet as hardware wallets are easily recognisable in airport security etc. + +```bash +neutrond keys add [name] +``` + +2. Note down the mnemonic - it is important that you are able to recover this account as a multisig signer. + +3. Send a small amount of funds to the address to register it. In testnet you can do this by visiting the facuet [here](https://t.me/+SyhWrlnwfCw2NGM6) + +## Set up the multisig on your local network + +_Steps 2-4 must be completed by ALL multisig holders to properly set up their local keyring in their machine._ + +1. Generate the public keys of each of the 5 multisig holder's wallets. In order to generate a public key, the wallet must be active and have made at least one transaction on the specified network to return a public key. + +To do a send transaction of 1 NTRN to anoter account you can use the command: + +```bash +neutrond tx bank send [name]_ntrn [to_address] 1000000untrn --node=[rpc node] --chain-id=[chain id] +``` + +Note for testnet node you can use https://testnet-neutron-rpc.marsprotocol.io:443 and chain-id pion-1 + +Query the public key: + +```bash +neutrond query account [address] --node=[node_URL] +``` + +2. Add each public key to the keys list in your local network. + + ```bash + neutrond keys add [name] --pubkey=[pubkey] + ``` + + Note: The pubkey must be entered with the same syntax as shown in Step 1. + +3. Generate the multisig. + + ```bash + neutrond keys add neutron_multisig \ + --multisig=[name1],[name2],[name3],[name4],[name5] \ + --multisig-threshold=3 + ``` + +4. Assert that it was completed correctly. + + ```bash + neutrond keys show neutron_multisig + ``` + +5. Update the config with the new mutlisig address in `red-bank/scripts/deploy/neutron/config`, which will set the owner and admin of the smart contracts to the multisig upon deployment. + +## Signing a TX with the multisig - Testnet Migrate Msg Example + +**Every multisig holder is responsible for verifying the contract's newly uploaded code for every migrate msg.** + +Refer to the osmosis readme, examples are the same but replacing osmosisd with neutrond + +## Signing a TX with the multisig - Testnet Execute Msg Example + +Refer to the osmosis readme, examples are the same but replacing osmosisd with neutrond + +## Examples of Execute Args + +Refer to the osmosis readme, examples are the same but replacing osmosisd with neutrond From 812364b3221a61c2d1404bebe7fe9c153d26b9af Mon Sep 17 00:00:00 2001 From: piobab Date: Wed, 26 Jul 2023 16:30:40 +0200 Subject: [PATCH 14/27] MP-2615 rover rewards (#273) * Add account_id for rover deposit rewards. * Use user addr and account id as compound key. * Apply comments. --- Cargo.lock | 1 + contracts/incentives/src/contract.rs | 37 ++- contracts/incentives/src/helpers.rs | 10 +- contracts/incentives/src/state.rs | 12 +- .../incentives/tests/test_balance_change.rs | 192 ++++++++++++--- .../incentives/tests/test_claim_rewards.rs | 41 ++-- contracts/incentives/tests/test_whitelist.rs | 3 + contracts/red-bank/src/collateral.rs | 13 +- contracts/red-bank/src/contract.rs | 16 +- contracts/red-bank/src/deposit.rs | 2 + contracts/red-bank/src/health.rs | 17 +- contracts/red-bank/src/interest_rates.rs | 1 + contracts/red-bank/src/liquidate.rs | 5 +- contracts/red-bank/src/query.rs | 10 +- contracts/red-bank/src/state.rs | 3 +- contracts/red-bank/src/user.rs | 28 ++- contracts/red-bank/src/withdraw.rs | 4 +- contracts/red-bank/tests/helpers.rs | 8 +- contracts/red-bank/tests/test_admin.rs | 2 +- contracts/red-bank/tests/test_deposit.rs | 96 +++----- contracts/red-bank/tests/test_liquidate.rs | 21 +- contracts/red-bank/tests/test_payment.rs | 1 + contracts/red-bank/tests/test_query.rs | 5 +- contracts/red-bank/tests/test_withdraw.rs | 24 +- .../rewards-collector/base/src/contract.rs | 2 + .../osmosis/tests/test_withdraw.rs | 3 +- integration-tests/Cargo.toml | 1 + integration-tests/tests/helpers.rs | 15 +- integration-tests/tests/test_incentives.rs | 230 ++++++++++++++++-- integration-tests/tests/test_oracles.rs | 20 +- integration-tests/tests/test_rover_flow.rs | 10 +- integration-tests/tests/test_user_flow.rs | 4 +- packages/testing/src/incentives_querier.rs | 1 + packages/testing/src/integration/mock_env.rs | 104 ++++++-- packages/testing/src/red_bank_querier.rs | 1 + packages/types/src/address_provider.rs | 5 +- packages/types/src/incentives.rs | 6 + packages/types/src/red_bank/msg.rs | 9 +- schemas/mars-incentives/mars-incentives.json | 21 ++ schemas/mars-red-bank/mars-red-bank.json | 28 +++ .../mars-incentives/MarsIncentives.client.ts | 15 ++ .../MarsIncentives.react-query.ts | 4 + .../mars-incentives/MarsIncentives.types.ts | 3 + .../mars-red-bank/MarsRedBank.client.ts | 29 ++- .../mars-red-bank/MarsRedBank.react-query.ts | 10 +- .../mars-red-bank/MarsRedBank.types.ts | 7 +- 46 files changed, 851 insertions(+), 229 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index de78cd5de..09f1e4e84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1773,6 +1773,7 @@ dependencies = [ "cosmwasm-std", "cw-it", "cw-multi-test", + "mars-incentives", "mars-oracle-base", "mars-oracle-osmosis", "mars-osmosis", diff --git a/contracts/incentives/src/contract.rs b/contracts/incentives/src/contract.rs index 78969d0f9..a877040f5 100644 --- a/contracts/incentives/src/contract.rs +++ b/contracts/incentives/src/contract.rs @@ -104,6 +104,7 @@ pub fn execute( ), ExecuteMsg::BalanceChange { user_addr, + account_id, denom, user_amount_scaled_before, total_amount_scaled_before, @@ -112,11 +113,13 @@ pub fn execute( env, info, user_addr, + account_id, denom, user_amount_scaled_before, total_amount_scaled_before, ), ExecuteMsg::ClaimRewards { + account_id, start_after_collateral_denom, start_after_incentive_denom, limit, @@ -124,6 +127,7 @@ pub fn execute( deps, env, info, + account_id, start_after_collateral_denom, start_after_incentive_denom, limit, @@ -339,6 +343,7 @@ pub fn execute_balance_change( env: Env, info: MessageInfo, user_addr: Addr, + account_id: Option, collateral_denom: String, user_amount_scaled_before: Uint128, total_amount_scaled_before: Uint128, @@ -349,10 +354,17 @@ pub fn execute_balance_change( return Err(MarsError::Unauthorized {}.into()); } + let acc_id = account_id.clone().unwrap_or("".to_string()); + let base_event = Event::new("mars/incentives/balance_change") .add_attribute("action", "balance_change") .add_attribute("denom", collateral_denom.clone()) .add_attribute("user", user_addr.to_string()); + let base_event = if account_id.is_some() { + base_event.add_attribute("account_id", &acc_id) + } else { + base_event + }; let mut events = vec![base_event]; let incentive_states = INCENTIVE_STATES @@ -371,7 +383,7 @@ pub fn execute_balance_change( // Check if user has accumulated uncomputed rewards (which means index is not up to date) let user_asset_index_key = - USER_ASSET_INDICES.key((&user_addr, &collateral_denom, &incentive_denom)); + USER_ASSET_INDICES.key(((&user_addr, &acc_id), &collateral_denom, &incentive_denom)); let user_asset_index = user_asset_index_key.may_load(deps.storage)?.unwrap_or_else(Decimal::zero); @@ -391,6 +403,7 @@ pub fn execute_balance_change( state::increase_unclaimed_rewards( deps.storage, &user_addr, + &acc_id, &collateral_denom, &incentive_denom, accrued_rewards, @@ -415,17 +428,25 @@ pub fn execute_claim_rewards( mut deps: DepsMut, env: Env, info: MessageInfo, + account_id: Option, start_after_collateral_denom: Option, start_after_incentive_denom: Option, limit: Option, ) -> Result { - let red_bank_addr = query_red_bank_address(deps.as_ref())?; let user_addr = info.sender; + let acc_id = account_id.clone().unwrap_or("".to_string()); + + let red_bank_addr = query_red_bank_address(deps.as_ref())?; let mut response = Response::new(); let base_event = Event::new("mars/incentives/claim_rewards") .add_attribute("action", "claim_rewards") .add_attribute("user", user_addr.to_string()); + let base_event = if account_id.is_some() { + base_event.add_attribute("account_id", &acc_id) + } else { + base_event + }; let mut events = vec![base_event]; let asset_incentives = state::paginate_incentive_states( @@ -445,6 +466,7 @@ pub fn execute_claim_rewards( &env.block, &red_bank_addr, &user_addr, + &account_id, &collateral_denom, &incentive_denom, )?; @@ -452,7 +474,7 @@ pub fn execute_claim_rewards( // clear unclaimed rewards USER_UNCLAIMED_REWARDS.save( deps.storage, - (&user_addr, &collateral_denom, &incentive_denom), + ((&user_addr, &acc_id), &collateral_denom, &incentive_denom), &Uint128::zero(), )?; @@ -532,6 +554,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { )?), QueryMsg::UserUnclaimedRewards { user, + account_id, start_after_collateral_denom, start_after_incentive_denom, limit, @@ -539,6 +562,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { deps, env, user, + account_id, start_after_collateral_denom, start_after_incentive_denom, limit, @@ -635,13 +659,15 @@ pub fn query_user_unclaimed_rewards( deps: Deps, env: Env, user: String, + account_id: Option, start_after_collateral_denom: Option, start_after_incentive_denom: Option, limit: Option, -) -> StdResult> { - let red_bank_addr = query_red_bank_address(deps)?; +) -> Result, ContractError> { let user_addr = deps.api.addr_validate(&user)?; + let red_bank_addr = query_red_bank_address(deps)?; + let incentive_states = state::paginate_incentive_states( deps.storage, start_after_collateral_denom, @@ -658,6 +684,7 @@ pub fn query_user_unclaimed_rewards( &env.block, &red_bank_addr, &user_addr, + &account_id, &collateral_denom, &incentive_denom, )?; diff --git a/contracts/incentives/src/helpers.rs b/contracts/incentives/src/helpers.rs index 7183496ea..5201bd946 100644 --- a/contracts/incentives/src/helpers.rs +++ b/contracts/incentives/src/helpers.rs @@ -254,11 +254,14 @@ pub fn compute_user_unclaimed_rewards( block: &BlockInfo, red_bank_addr: &Addr, user_addr: &Addr, + account_id: &Option, collateral_denom: &str, incentive_denom: &str, ) -> StdResult { + let acc_id = account_id.clone().unwrap_or("".to_string()); + let mut unclaimed_rewards = USER_UNCLAIMED_REWARDS - .may_load(storage.to_storage(), (user_addr, collateral_denom, incentive_denom))? + .may_load(storage.to_storage(), ((user_addr, &acc_id), collateral_denom, incentive_denom))? .unwrap_or_else(Uint128::zero); // Get asset user balances and total supply @@ -266,6 +269,7 @@ pub fn compute_user_unclaimed_rewards( red_bank_addr, &red_bank::QueryMsg::UserCollateral { user: user_addr.to_string(), + account_id: account_id.clone(), denom: collateral_denom.to_string(), }, )?; @@ -292,7 +296,7 @@ pub fn compute_user_unclaimed_rewards( )?; let user_asset_index = USER_ASSET_INDICES - .may_load(storage.to_storage(), (user_addr, collateral_denom, incentive_denom))? + .may_load(storage.to_storage(), ((user_addr, &acc_id), collateral_denom, incentive_denom))? .unwrap_or_else(Decimal::zero); if user_asset_index != incentive_state.index { @@ -310,7 +314,7 @@ pub fn compute_user_unclaimed_rewards( if user_asset_index != incentive_state.index { USER_ASSET_INDICES.save( *storage, - (user_addr, collateral_denom, incentive_denom), + ((user_addr, &acc_id), collateral_denom, incentive_denom), &incentive_state.index, )? } diff --git a/contracts/incentives/src/state.rs b/contracts/incentives/src/state.rs index b8f9ede94..16d2342f9 100644 --- a/contracts/incentives/src/state.rs +++ b/contracts/incentives/src/state.rs @@ -32,12 +32,13 @@ pub const INCENTIVE_STATES: Map<(&str, &str), IncentiveState> = Map::new("incent pub const EMISSIONS: Map<(&str, &str, u64), Uint128> = Map::new("emissions"); /// A map containing the incentive index for a given user, collateral denom and incentive denom. -/// The key is (user address, collateral denom, incentive denom). -pub const USER_ASSET_INDICES: Map<(&Addr, &str, &str), Decimal> = Map::new("indices"); +/// The key is (user address with optional account id, collateral denom, incentive denom). +pub const USER_ASSET_INDICES: Map<((&Addr, &str), &str, &str), Decimal> = Map::new("indices"); /// A map containing the amount of unclaimed incentives for a given user and incentive denom. -/// The key is (user address, collateral denom, incentive denom). -pub const USER_UNCLAIMED_REWARDS: Map<(&Addr, &str, &str), Uint128> = Map::new("unclaimed_rewards"); +/// The key is (user address with optional account id, collateral denom, incentive denom). +pub const USER_UNCLAIMED_REWARDS: Map<((&Addr, &str), &str, &str), Uint128> = + Map::new("unclaimed_rewards"); /// The default limit for pagination pub const DEFAULT_LIMIT: u32 = 5; @@ -50,13 +51,14 @@ pub const MAX_LIMIT: u32 = 10; pub fn increase_unclaimed_rewards( storage: &mut dyn Storage, user_addr: &Addr, + acc_id: &str, collateral_denom: &str, incentive_denom: &str, accrued_rewards: Uint128, ) -> StdResult<()> { USER_UNCLAIMED_REWARDS.update( storage, - (user_addr, collateral_denom, incentive_denom), + ((user_addr, acc_id), collateral_denom, incentive_denom), |ur: Option| -> StdResult { Ok(ur.map_or_else(|| accrued_rewards, |r| r + accrued_rewards)) }, diff --git a/contracts/incentives/tests/test_balance_change.rs b/contracts/incentives/tests/test_balance_change.rs index 96216f174..07b49c8e8 100644 --- a/contracts/incentives/tests/test_balance_change.rs +++ b/contracts/incentives/tests/test_balance_change.rs @@ -30,6 +30,7 @@ fn balance_change_unauthorized() { mock_info("jake", &[]), // not Red Bank ExecuteMsg::BalanceChange { user_addr: Addr::unchecked("user"), + account_id: None, denom: "uosmo".to_string(), user_amount_scaled_before: Uint128::new(100000), total_amount_scaled_before: Uint128::new(100000), @@ -47,6 +48,7 @@ fn execute_balance_change_noops() { let info = mock_info("red_bank", &[]); let msg = ExecuteMsg::BalanceChange { user_addr: Addr::unchecked("user"), + account_id: None, denom: "uosmo".to_string(), user_amount_scaled_before: Uint128::new(100000), total_amount_scaled_before: Uint128::new(100000), @@ -93,6 +95,7 @@ fn balance_change_zero_emission() { }); let msg = ExecuteMsg::BalanceChange { user_addr: Addr::unchecked("user"), + account_id: None, denom: "uosmo".to_string(), user_amount_scaled_before: Uint128::new(100_000), total_amount_scaled_before: Uint128::new(100_000), @@ -106,7 +109,7 @@ fn balance_change_zero_emission() { assert_eq!( res.events[0].attributes, - vec![attr("action", "balance_change"), attr("denom", denom), attr("user", "user"),] + vec![attr("action", "balance_change"), attr("denom", denom), attr("user", "user")] ); assert_eq!( res.events[1].attributes, @@ -124,12 +127,13 @@ fn balance_change_zero_emission() { // user index is set to asset's index let user_asset_index = - USER_ASSET_INDICES.load(deps.as_ref().storage, (&user_addr, denom, "umars")).unwrap(); + USER_ASSET_INDICES.load(deps.as_ref().storage, ((&user_addr, ""), denom, "umars")).unwrap(); assert_eq!(user_asset_index, asset_incentive_index); // rewards get updated - let user_unclaimed_rewards = - USER_UNCLAIMED_REWARDS.load(deps.as_ref().storage, (&user_addr, denom, "umars")).unwrap(); + let user_unclaimed_rewards = USER_UNCLAIMED_REWARDS + .load(deps.as_ref().storage, ((&user_addr, ""), denom, "umars")) + .unwrap(); assert_eq!(user_unclaimed_rewards, expected_accrued_rewards) } @@ -167,6 +171,7 @@ fn balance_change_user_with_zero_balance() { }); let msg = ExecuteMsg::BalanceChange { user_addr: user_addr.clone(), + account_id: None, denom: "uosmo".to_string(), user_amount_scaled_before: Uint128::zero(), total_amount_scaled_before: total_supply, @@ -185,7 +190,7 @@ fn balance_change_user_with_zero_balance() { assert_eq!( res.events[0].attributes, - vec![attr("action", "balance_change"), attr("denom", denom), attr("user", "user"),] + vec![attr("action", "balance_change"), attr("denom", denom), attr("user", "user")] ); assert_eq!( res.events[1].attributes, @@ -203,12 +208,12 @@ fn balance_change_user_with_zero_balance() { // user index is set to asset's index let user_asset_index = - USER_ASSET_INDICES.load(deps.as_ref().storage, (&user_addr, denom, "umars")).unwrap(); + USER_ASSET_INDICES.load(deps.as_ref().storage, ((&user_addr, ""), denom, "umars")).unwrap(); assert_eq!(user_asset_index, expected_index); // no new rewards let user_unclaimed_rewards = USER_UNCLAIMED_REWARDS - .may_load(deps.as_ref().storage, (&user_addr, denom, "umars")) + .may_load(deps.as_ref().storage, ((&user_addr, ""), denom, "umars")) .unwrap(); assert_eq!(user_unclaimed_rewards, None) } @@ -247,6 +252,7 @@ fn with_zero_previous_balance_and_asset_with_zero_index_accumulates_rewards() { }); let msg = ExecuteMsg::BalanceChange { user_addr: user_addr.clone(), + account_id: None, denom: "uosmo".to_string(), user_amount_scaled_before: Uint128::zero(), total_amount_scaled_before: Uint128::zero(), @@ -278,9 +284,16 @@ fn with_zero_previous_balance_and_asset_with_zero_index_accumulates_rewards() { block_time: Timestamp::from_seconds(time_contract_call + 1000), ..Default::default() }); - let rewards_query = - query_user_unclaimed_rewards(deps.as_ref(), env, "user".to_string(), None, None, None) - .unwrap(); + let rewards_query = query_user_unclaimed_rewards( + deps.as_ref(), + env, + "user".to_string(), + None, + None, + None, + None, + ) + .unwrap(); // Rewards that are accrued when no one had deposit in Red Bank are distributed to the first depositor assert_eq!( vec![coin( @@ -351,9 +364,16 @@ fn set_new_asset_incentive_user_non_zero_balance() { ..Default::default() }); - let unclaimed_rewards = - query_user_unclaimed_rewards(deps.as_ref(), env, "user".to_string(), None, None, None) - .unwrap(); + let unclaimed_rewards = query_user_unclaimed_rewards( + deps.as_ref(), + env, + "user".to_string(), + None, + None, + None, + None, + ) + .unwrap(); // 100_000 s * 100 MARS/s * 1/10th of total deposit let expected_unclaimed_rewards = vec![coin(1_000_000, "umars")]; assert_eq!(unclaimed_rewards, expected_unclaimed_rewards); @@ -386,6 +406,7 @@ fn set_new_asset_incentive_user_non_zero_balance() { env, info, user_addr, + None, denom.to_string(), Uint128::new(10_000), total_supply, @@ -402,9 +423,16 @@ fn set_new_asset_incentive_user_non_zero_balance() { ..Default::default() }); - let unclaimed_rewards = - query_user_unclaimed_rewards(deps.as_ref(), env, "user".to_string(), None, None, None) - .unwrap(); + let unclaimed_rewards = query_user_unclaimed_rewards( + deps.as_ref(), + env, + "user".to_string(), + None, + None, + None, + None, + ) + .unwrap(); let expected_unclaimed_rewards = vec![coin( // 200_000 s * 100 MARS/s * 1/10th of total deposit + 2_000_000 + @@ -461,6 +489,7 @@ fn balance_change_user_non_zero_balance() { }); let msg = ExecuteMsg::BalanceChange { user_addr: user_addr.clone(), + account_id: None, denom: "uosmo".to_string(), user_amount_scaled_before: user_balance, total_amount_scaled_before: total_supply, @@ -484,7 +513,7 @@ fn balance_change_user_non_zero_balance() { .unwrap(); assert_eq!( res.events[0].attributes, - vec![attr("action", "balance_change"), attr("denom", denom), attr("user", "user"),] + vec![attr("action", "balance_change"), attr("denom", denom), attr("user", "user")] ); assert_eq!( res.events[1].attributes, @@ -504,13 +533,14 @@ fn balance_change_user_non_zero_balance() { assert_eq!(asset_incentive.last_updated, expected_time_last_updated); // user index is set to asset's index - let user_asset_index = - USER_ASSET_INDICES.load(deps.as_ref().storage, (&user_addr, denom, "umars")).unwrap(); + let user_asset_index = USER_ASSET_INDICES + .load(deps.as_ref().storage, ((&user_addr, ""), denom, "umars")) + .unwrap(); assert_eq!(user_asset_index, expected_asset_incentive_index); // user gets new rewards let user_unclaimed_rewards = USER_UNCLAIMED_REWARDS - .load(deps.as_ref().storage, (&user_addr, denom, "umars")) + .load(deps.as_ref().storage, ((&user_addr, ""), denom, "umars")) .unwrap(); expected_accumulated_rewards += expected_accrued_rewards; assert_eq!(user_unclaimed_rewards, expected_accumulated_rewards) @@ -527,6 +557,7 @@ fn balance_change_user_non_zero_balance() { }); let msg = ExecuteMsg::BalanceChange { user_addr: user_addr.clone(), + account_id: None, denom: "uosmo".to_string(), user_amount_scaled_before: user_balance, total_amount_scaled_before: total_supply, @@ -551,7 +582,7 @@ fn balance_change_user_non_zero_balance() { .unwrap(); assert_eq!( res.events[0].attributes, - vec![attr("action", "balance_change"), attr("denom", denom), attr("user", "user"),] + vec![attr("action", "balance_change"), attr("denom", denom), attr("user", "user")] ); assert_eq!( res.events[1].attributes, @@ -571,13 +602,14 @@ fn balance_change_user_non_zero_balance() { assert_eq!(asset_incentive.last_updated, expected_time_last_updated); // user index is set to asset's index - let user_asset_index = - USER_ASSET_INDICES.load(deps.as_ref().storage, (&user_addr, denom, "umars")).unwrap(); + let user_asset_index = USER_ASSET_INDICES + .load(deps.as_ref().storage, ((&user_addr, ""), denom, "umars")) + .unwrap(); assert_eq!(user_asset_index, expected_asset_incentive_index); // user gets new rewards let user_unclaimed_rewards = USER_UNCLAIMED_REWARDS - .load(deps.as_ref().storage, (&user_addr, denom, "umars")) + .load(deps.as_ref().storage, ((&user_addr, ""), denom, "umars")) .unwrap(); expected_accumulated_rewards += expected_accrued_rewards; assert_eq!(user_unclaimed_rewards, expected_accumulated_rewards) @@ -594,6 +626,7 @@ fn balance_change_user_non_zero_balance() { }); let msg = ExecuteMsg::BalanceChange { user_addr: user_addr.clone(), + account_id: None, denom: "uosmo".to_string(), user_amount_scaled_before: user_balance, total_amount_scaled_before: total_supply, @@ -602,7 +635,7 @@ fn balance_change_user_non_zero_balance() { assert_eq!( res.events[0].attributes, - vec![attr("action", "balance_change"), attr("denom", denom), attr("user", "user"),] + vec![attr("action", "balance_change"), attr("denom", denom), attr("user", "user")] ); assert_eq!( res.events[1].attributes, @@ -620,14 +653,117 @@ fn balance_change_user_non_zero_balance() { assert_eq!(asset_incentive.last_updated, expected_time_last_updated); // user index is still the same - let user_asset_index = - USER_ASSET_INDICES.load(deps.as_ref().storage, (&user_addr, denom, "umars")).unwrap(); + let user_asset_index = USER_ASSET_INDICES + .load(deps.as_ref().storage, ((&user_addr, ""), denom, "umars")) + .unwrap(); assert_eq!(user_asset_index, expected_asset_incentive_index); // user gets no new rewards let user_unclaimed_rewards = USER_UNCLAIMED_REWARDS - .load(deps.as_ref().storage, (&user_addr, denom, "umars")) + .load(deps.as_ref().storage, ((&user_addr, ""), denom, "umars")) .unwrap(); assert_eq!(user_unclaimed_rewards, expected_accumulated_rewards) } } + +#[test] +fn balance_change_for_credit_account_id_with_non_zero_balance() { + let env = mock_env(); + let mut deps = ths_setup_with_epoch_duration(env, 8640000); + let denom = "uosmo"; + let user_addr = Addr::unchecked("credit_manager"); + let account_id = "random_account_id"; + + let emission_per_second = Uint128::new(100); + let total_supply = Uint128::new(100_000); + + let mut expected_asset_incentive_index = Decimal::from_ratio(1_u128, 2_u128); + let mut expected_time_last_updated = 500_000_u64; + let mut expected_accumulated_rewards = Uint128::zero(); + + INCENTIVE_STATES + .save( + deps.as_mut().storage, + (denom, "umars"), + &IncentiveState { + index: expected_asset_incentive_index, + last_updated: expected_time_last_updated, + }, + ) + .unwrap(); + EMISSIONS + .save( + deps.as_mut().storage, + (denom, "umars", expected_time_last_updated), + &emission_per_second, + ) + .unwrap(); + + let info = mock_info("red_bank", &[]); + + let time_contract_call = 600_000_u64; + let user_balance = Uint128::new(10_000); + + let env = mars_testing::mock_env(MockEnvParams { + block_time: Timestamp::from_seconds(time_contract_call), + ..Default::default() + }); + let msg = ExecuteMsg::BalanceChange { + user_addr: user_addr.clone(), + account_id: Some(account_id.to_string()), + denom: "uosmo".to_string(), + user_amount_scaled_before: user_balance, + total_amount_scaled_before: total_supply, + }; + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + + expected_asset_incentive_index = compute_incentive_index( + expected_asset_incentive_index, + emission_per_second, + total_supply, + expected_time_last_updated, + time_contract_call, + ) + .unwrap(); + + let expected_accrued_rewards = + compute_user_accrued_rewards(user_balance, Decimal::zero(), expected_asset_incentive_index) + .unwrap(); + assert_eq!( + res.events[0].attributes, + vec![ + attr("action", "balance_change"), + attr("denom", denom), + attr("user", "credit_manager"), + attr("account_id", account_id) + ] + ); + assert_eq!( + res.events[1].attributes, + vec![ + attr("incentive_denom", "umars"), + attr("rewards_accrued", expected_accrued_rewards), + attr("asset_index", expected_asset_incentive_index.to_string()) + ] + ); + + // asset incentive gets updated + expected_time_last_updated = time_contract_call; + + let asset_incentive = INCENTIVE_STATES.load(deps.as_ref().storage, (denom, "umars")).unwrap(); + assert_eq!(asset_incentive.index, expected_asset_incentive_index); + assert_eq!(asset_incentive.last_updated, expected_time_last_updated); + + // user index is set to asset's index + let user_asset_index = USER_ASSET_INDICES + .load(deps.as_ref().storage, ((&user_addr, account_id), denom, "umars")) + .unwrap(); + assert_eq!(user_asset_index, expected_asset_incentive_index); + + // user gets new rewards + let user_unclaimed_rewards = USER_UNCLAIMED_REWARDS + .load(deps.as_ref().storage, ((&user_addr, account_id), denom, "umars")) + .unwrap(); + expected_accumulated_rewards += expected_accrued_rewards; + assert_eq!(user_unclaimed_rewards, expected_accumulated_rewards) +} diff --git a/contracts/incentives/tests/test_claim_rewards.rs b/contracts/incentives/tests/test_claim_rewards.rs index 66c887556..8320d81ec 100644 --- a/contracts/incentives/tests/test_claim_rewards.rs +++ b/contracts/incentives/tests/test_claim_rewards.rs @@ -133,13 +133,13 @@ fn execute_claim_rewards() { // user indices USER_ASSET_INDICES - .save(deps.as_mut().storage, (&user_addr, asset_denom, "umars"), &Decimal::one()) + .save(deps.as_mut().storage, ((&user_addr, ""), asset_denom, "umars"), &Decimal::one()) .unwrap(); USER_ASSET_INDICES .save( deps.as_mut().storage, - (&user_addr, zero_denom, "umars"), + ((&user_addr, ""), zero_denom, "umars"), &Decimal::from_ratio(1_u128, 2_u128), ) .unwrap(); @@ -148,7 +148,7 @@ fn execute_claim_rewards() { USER_UNCLAIMED_REWARDS .save( deps.as_mut().storage, - (&user_addr, asset_denom, "umars"), + ((&user_addr, ""), asset_denom, "umars"), &previous_unclaimed_rewards, ) .unwrap(); @@ -186,6 +186,7 @@ fn execute_claim_rewards() { ..Default::default() }); let msg = ExecuteMsg::ClaimRewards { + account_id: None, start_after_collateral_denom: None, start_after_incentive_denom: None, limit: None, @@ -203,6 +204,7 @@ fn execute_claim_rewards() { None, None, None, + None, ) .unwrap(); assert!(rewards_query_before.len() == 1); @@ -216,6 +218,7 @@ fn execute_claim_rewards() { None, None, None, + None, ) .unwrap(); assert_eq!(rewards_query[0].amount, expected_accrued_rewards); @@ -223,9 +226,16 @@ fn execute_claim_rewards() { let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); // query after execution gives 0 rewards - let rewards_query_after = - query_user_unclaimed_rewards(deps.as_ref(), env, String::from("user"), None, None, None) - .unwrap(); + let rewards_query_after = query_user_unclaimed_rewards( + deps.as_ref(), + env, + String::from("user"), + None, + None, + None, + None, + ) + .unwrap(); assert_eq!(rewards_query_after[0].amount, Uint128::zero()); // ASSERT @@ -240,7 +250,7 @@ fn execute_claim_rewards() { assert_eq!( res.events[0].attributes, - vec![attr("action", "claim_rewards"), attr("user", "user"),] + vec![attr("action", "claim_rewards"), attr("user", "user")] ); assert_eq!( res.events[1].attributes, @@ -263,23 +273,25 @@ fn execute_claim_rewards() { assert_eq!(no_user_incentive.last_updated, time_start); // user's asset and zero indices are updated - let user_asset_index = - USER_ASSET_INDICES.load(deps.as_ref().storage, (&user_addr, asset_denom, "umars")).unwrap(); + let user_asset_index = USER_ASSET_INDICES + .load(deps.as_ref().storage, ((&user_addr, ""), asset_denom, "umars")) + .unwrap(); assert_eq!(user_asset_index, expected_asset_incentive_index); - let user_zero_index = - USER_ASSET_INDICES.load(deps.as_ref().storage, (&user_addr, zero_denom, "umars")).unwrap(); + let user_zero_index = USER_ASSET_INDICES + .load(deps.as_ref().storage, ((&user_addr, ""), zero_denom, "umars")) + .unwrap(); assert_eq!(user_zero_index, Decimal::one()); // user's no_user does not get updated let user_no_user_index = USER_ASSET_INDICES - .may_load(deps.as_ref().storage, (&user_addr, no_user_denom, "umars")) + .may_load(deps.as_ref().storage, ((&user_addr, ""), no_user_denom, "umars")) .unwrap(); assert_eq!(user_no_user_index, None); // user rewards are cleared let user_unclaimed_rewards = USER_UNCLAIMED_REWARDS - .load(deps.as_ref().storage, (&user_addr, asset_denom, "umars")) + .load(deps.as_ref().storage, ((&user_addr, ""), asset_denom, "umars")) .unwrap(); assert_eq!(user_unclaimed_rewards, Uint128::zero()) } @@ -291,6 +303,7 @@ fn claim_zero_rewards() { let info = mock_info("user", &[]); let msg = ExecuteMsg::ClaimRewards { + account_id: None, start_after_collateral_denom: None, start_after_incentive_denom: None, limit: None, @@ -300,6 +313,6 @@ fn claim_zero_rewards() { assert_eq!(res.messages.len(), 0); assert_eq!( res.events[0].attributes, - vec![attr("action", "claim_rewards"), attr("user", "user"),] + vec![attr("action", "claim_rewards"), attr("user", "user")] ); } diff --git a/contracts/incentives/tests/test_whitelist.rs b/contracts/incentives/tests/test_whitelist.rs index 6424c5ab6..959e1d09b 100644 --- a/contracts/incentives/tests/test_whitelist.rs +++ b/contracts/incentives/tests/test_whitelist.rs @@ -210,6 +210,7 @@ fn incentives_updated_and_removed_when_removing_from_whitelist() { env.clone(), mock_info("red_bank", &[]), user_addr.clone(), + None, "uosmo".to_string(), Uint128::zero(), Uint128::zero(), @@ -236,6 +237,7 @@ fn incentives_updated_and_removed_when_removing_from_whitelist() { env.clone(), QueryMsg::UserUnclaimedRewards { user: user_addr.to_string(), + account_id: None, start_after_collateral_denom: None, start_after_incentive_denom: None, limit: None, @@ -255,6 +257,7 @@ fn incentives_updated_and_removed_when_removing_from_whitelist() { env, QueryMsg::UserUnclaimedRewards { user: user_addr.to_string(), + account_id: None, start_after_collateral_denom: None, start_after_incentive_denom: None, limit: None, diff --git a/contracts/red-bank/src/collateral.rs b/contracts/red-bank/src/collateral.rs index b9aa62dfe..f2ef6c7f2 100644 --- a/contracts/red-bank/src/collateral.rs +++ b/contracts/red-bank/src/collateral.rs @@ -21,18 +21,17 @@ pub fn update_asset_collateral_status( ) -> Result { let user = User(&info.sender); - let mut collateral = - COLLATERALS.may_load(deps.storage, (user.address(), &denom))?.ok_or_else(|| { - ContractError::UserNoCollateralBalance { - user: user.into(), - denom: denom.clone(), - } + let mut collateral = COLLATERALS + .may_load(deps.storage, (user.address(), "", &denom))? + .ok_or_else(|| ContractError::UserNoCollateralBalance { + user: user.into(), + denom: denom.clone(), })?; let previously_enabled = collateral.enabled; collateral.enabled = enable; - COLLATERALS.save(deps.storage, (user.address(), &denom), &collateral)?; + COLLATERALS.save(deps.storage, (user.address(), "", &denom), &collateral)?; // if the collateral was previously enabled, but is not disabled, it is necessary to ensure the // user is not liquidatable after disabling diff --git a/contracts/red-bank/src/contract.rs b/contracts/red-bank/src/contract.rs index 6d9154049..052e96c05 100644 --- a/contracts/red-bank/src/contract.rs +++ b/contracts/red-bank/src/contract.rs @@ -46,17 +46,20 @@ pub fn execute( deps, info, user_addr, denom, new_limit, ) } - ExecuteMsg::Deposit {} => { + ExecuteMsg::Deposit { + account_id, + } => { let sent_coin = cw_utils::one_coin(&info)?; - deposit::deposit(deps, env, info, sent_coin.denom, sent_coin.amount) + deposit::deposit(deps, env, info, sent_coin.denom, sent_coin.amount, account_id) } ExecuteMsg::Withdraw { denom, amount, recipient, + account_id, } => { cw_utils::nonpayable(&info)?; - withdraw::withdraw(deps, env, info, denom, amount, recipient) + withdraw::withdraw(deps, env, info, denom, amount, recipient, account_id) } ExecuteMsg::Borrow { denom, @@ -148,13 +151,17 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { let user_addr = deps.api.addr_validate(&user)?; - to_binary(&query::query_user_collateral(deps, &env.block, user_addr, denom)?) + to_binary(&query::query_user_collateral( + deps, &env.block, user_addr, account_id, denom, + )?) } QueryMsg::UserCollaterals { user, + account_id, start_after, limit, } => { @@ -163,6 +170,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result, ) -> Result { let mut market = MARKETS.load(deps.storage, &denom)?; @@ -73,6 +74,7 @@ pub fn deposit( deposit_amount_scaled, incentives_addr, response, + account_id, )?; market.increase_collateral(deposit_amount_scaled)?; diff --git a/contracts/red-bank/src/health.rs b/contracts/red-bank/src/health.rs index 352629040..ae8b3b775 100644 --- a/contracts/red-bank/src/health.rs +++ b/contracts/red-bank/src/health.rs @@ -117,7 +117,7 @@ pub fn get_user_positions_map( // Find all denoms that the user has a collateral or debt position in let collateral_denoms = COLLATERALS - .prefix(user_addr) + .prefix((user_addr, "")) .keys(deps.storage, None, None, Order::Ascending) .collect::>>()?; let debt_denoms = DEBTS @@ -138,13 +138,14 @@ pub fn get_user_positions_map( let market = MARKETS.load(deps.storage, &denom)?; let params = query_asset_params(&deps.querier, params_addr, &denom)?; - let collateral_amount = match COLLATERALS.may_load(deps.storage, (user_addr, &denom))? { - Some(collateral) if collateral.enabled => { - let amount_scaled = collateral.amount_scaled; - get_underlying_liquidity_amount(amount_scaled, &market, block_time)? - } - _ => Uint128::zero(), - }; + let collateral_amount = + match COLLATERALS.may_load(deps.storage, (user_addr, "", &denom))? { + Some(collateral) if collateral.enabled => { + let amount_scaled = collateral.amount_scaled; + get_underlying_liquidity_amount(amount_scaled, &market, block_time)? + } + _ => Uint128::zero(), + }; let (debt_amount, uncollateralized_debt) = match DEBTS.may_load(deps.storage, (user_addr, &denom))? { diff --git a/contracts/red-bank/src/interest_rates.rs b/contracts/red-bank/src/interest_rates.rs index 980fd9a29..d25f83236 100644 --- a/contracts/red-bank/src/interest_rates.rs +++ b/contracts/red-bank/src/interest_rates.rs @@ -86,6 +86,7 @@ pub fn apply_accumulated_interests( reward_amount_scaled, incentives_addr, response, + None, )?; market.increase_collateral(reward_amount_scaled)?; } diff --git a/contracts/red-bank/src/liquidate.rs b/contracts/red-bank/src/liquidate.rs index 15d867aa2..5d4a8a523 100644 --- a/contracts/red-bank/src/liquidate.rs +++ b/contracts/red-bank/src/liquidate.rs @@ -50,7 +50,7 @@ pub fn liquidate( // check if the user has enabled the collateral asset as collateral let user_collateral = COLLATERALS - .may_load(deps.storage, (&liquidatee_addr, &collateral_denom))? + .may_load(deps.storage, (&liquidatee_addr, "", &collateral_denom))? .ok_or(ContractError::CannotLiquidateWhenNoCollateralBalance {})?; if !user_collateral.enabled { return Err(ContractError::CannotLiquidateWhenCollateralUnset { @@ -160,6 +160,7 @@ pub fn liquidate( collateral_amount_to_liquidate_scaled, incentives_addr, response, + None, )?; response = recipient.increase_collateral( deps.storage, @@ -167,6 +168,7 @@ pub fn liquidate( collateral_amount_received_by_liquidator_scaled, incentives_addr, response, + None, )?; if !protocol_fee.is_zero() { response = User(rewards_collector_addr).increase_collateral( @@ -175,6 +177,7 @@ pub fn liquidate( protocol_fee_scaled, incentives_addr, response, + None, )?; } diff --git a/contracts/red-bank/src/query.rs b/contracts/red-bank/src/query.rs index 7e79000b1..e9a74ab16 100644 --- a/contracts/red-bank/src/query.rs +++ b/contracts/red-bank/src/query.rs @@ -149,12 +149,15 @@ pub fn query_user_collateral( deps: Deps, block: &BlockInfo, user_addr: Addr, + account_id: Option, denom: String, ) -> StdResult { + let acc_id = account_id.unwrap_or("".to_string()); + let Collateral { amount_scaled, enabled, - } = COLLATERALS.may_load(deps.storage, (&user_addr, &denom))?.unwrap_or_default(); + } = COLLATERALS.may_load(deps.storage, (&user_addr, &acc_id, &denom))?.unwrap_or_default(); let block_time = block.time.seconds(); let market = MARKETS.load(deps.storage, &denom)?; @@ -172,6 +175,7 @@ pub fn query_user_collaterals( deps: Deps, block: &BlockInfo, user_addr: Addr, + account_id: Option, start_after: Option, limit: Option, ) -> StdResult> { @@ -180,8 +184,10 @@ pub fn query_user_collaterals( let start = start_after.map(|denom| Bound::ExclusiveRaw(denom.into_bytes())); let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; + let acc_id = account_id.unwrap_or("".to_string()); + COLLATERALS - .prefix(&user_addr) + .prefix((&user_addr, &acc_id)) .range(deps.storage, start, None, Order::Ascending) .take(limit) .map(|item| { diff --git a/contracts/red-bank/src/state.rs b/contracts/red-bank/src/state.rs index 4eaf0be3b..075eb7c01 100644 --- a/contracts/red-bank/src/state.rs +++ b/contracts/red-bank/src/state.rs @@ -6,6 +6,7 @@ use mars_red_bank_types::red_bank::{Collateral, Config, Debt, Market}; pub const OWNER: Owner = Owner::new("owner"); pub const CONFIG: Item> = Item::new("config"); pub const MARKETS: Map<&str, Market> = Map::new("markets"); -pub const COLLATERALS: Map<(&Addr, &str), Collateral> = Map::new("collaterals"); +/// The key is: user address, account id (if any), collateral denom +pub const COLLATERALS: Map<(&Addr, &str, &str), Collateral> = Map::new("collaterals"); pub const DEBTS: Map<(&Addr, &str), Debt> = Map::new("debts"); pub const UNCOLLATERALIZED_LOAN_LIMITS: Map<(&Addr, &str), Uint128> = Map::new("limits"); diff --git a/contracts/red-bank/src/user.rs b/contracts/red-bank/src/user.rs index b8e7dd4b1..dbee6f8df 100644 --- a/contracts/red-bank/src/user.rs +++ b/contracts/red-bank/src/user.rs @@ -48,8 +48,14 @@ impl<'a> User<'a> { } /// Load the user's collateral - pub fn collateral(&self, store: &dyn Storage, denom: &str) -> StdResult { - COLLATERALS.load(store, (self.0, denom)) + pub fn collateral( + &self, + store: &dyn Storage, + denom: &str, + account_id: Option, + ) -> StdResult { + let acc_id = account_id.unwrap_or("".to_string()); + COLLATERALS.load(store, (self.0, &acc_id, denom)) } /// Load the user's debt @@ -102,10 +108,13 @@ impl<'a> User<'a> { amount_scaled: Uint128, incentives_addr: &Addr, response: Response, + account_id: Option, ) -> StdResult { + let acc_id = account_id.clone().unwrap_or("".to_string()); + let mut amount_scaled_before = Uint128::zero(); - COLLATERALS.update(store, (self.0, &market.denom), |opt| -> StdResult<_> { + COLLATERALS.update(store, (self.0, &acc_id, &market.denom), |opt| -> StdResult<_> { match opt { Some(mut col) => { amount_scaled_before = col.amount_scaled; @@ -123,6 +132,7 @@ impl<'a> User<'a> { incentives_addr, market, amount_scaled_before, + account_id, )?; Ok(response.add_message(msg)) @@ -141,22 +151,26 @@ impl<'a> User<'a> { amount_scaled: Uint128, incentives_addr: &Addr, response: Response, + account_id: Option, ) -> StdResult { - let mut collateral = COLLATERALS.load(store, (self.0, &market.denom))?; + let acc_id = account_id.clone().unwrap_or("".to_string()); + + let mut collateral = COLLATERALS.load(store, (self.0, &acc_id, &market.denom))?; let amount_scaled_before = collateral.amount_scaled; collateral.amount_scaled = collateral.amount_scaled.checked_sub(amount_scaled)?; if collateral.amount_scaled.is_zero() { - COLLATERALS.remove(store, (self.0, &market.denom)); + COLLATERALS.remove(store, (self.0, &acc_id, &market.denom)); } else { - COLLATERALS.save(store, (self.0, &market.denom), &collateral)?; + COLLATERALS.save(store, (self.0, &acc_id, &market.denom), &collateral)?; } let msg = self.build_incentives_balance_changed_msg( incentives_addr, market, amount_scaled_before, + account_id, )?; Ok(response.add_message(msg)) @@ -171,11 +185,13 @@ impl<'a> User<'a> { incentives_addr: &Addr, market: &Market, user_amount_scaled_before: Uint128, + account_id: Option, ) -> StdResult { Ok(WasmMsg::Execute { contract_addr: incentives_addr.into(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: self.address().clone(), + account_id, denom: market.denom.clone(), user_amount_scaled_before, total_amount_scaled_before: market.collateral_total_scaled, diff --git a/contracts/red-bank/src/withdraw.rs b/contracts/red-bank/src/withdraw.rs index 08ce458d9..a9da5ce72 100644 --- a/contracts/red-bank/src/withdraw.rs +++ b/contracts/red-bank/src/withdraw.rs @@ -18,12 +18,13 @@ pub fn withdraw( denom: String, amount: Option, recipient: Option, + account_id: Option, ) -> Result { let withdrawer = User(&info.sender); let mut market = MARKETS.load(deps.storage, &denom)?; - let collateral = withdrawer.collateral(deps.storage, &denom)?; + let collateral = withdrawer.collateral(deps.storage, &denom, account_id.clone())?; let withdrawer_balance_scaled_before = collateral.amount_scaled; if withdrawer_balance_scaled_before.is_zero() { @@ -111,6 +112,7 @@ pub fn withdraw( withdraw_amount_scaled, incentives_addr, response, + account_id, )?; market.decrease_collateral(withdraw_amount_scaled)?; diff --git a/contracts/red-bank/tests/helpers.rs b/contracts/red-bank/tests/helpers.rs index 670ab5b4e..72190cdd1 100644 --- a/contracts/red-bank/tests/helpers.rs +++ b/contracts/red-bank/tests/helpers.rs @@ -37,11 +37,11 @@ pub fn set_collateral( amount_scaled, enabled, }; - COLLATERALS.save(deps.storage, (user_addr, denom), &collateral).unwrap(); + COLLATERALS.save(deps.storage, (user_addr, "", denom), &collateral).unwrap(); } pub fn unset_collateral(deps: DepsMut, user_addr: &Addr, denom: &str) { - COLLATERALS.remove(deps.storage, (user_addr, denom)); + COLLATERALS.remove(deps.storage, (user_addr, "", denom)); } pub fn set_debt( @@ -65,13 +65,13 @@ pub fn has_debt_position(deps: Deps, user_addr: &Addr, denom: &str) -> bool { /// Find if a user has a collateral position in the specified asset, regardless of whether enabled pub fn has_collateral_position(deps: Deps, user_addr: &Addr, denom: &str) -> bool { - COLLATERALS.may_load(deps.storage, (user_addr, denom)).unwrap().is_some() + COLLATERALS.may_load(deps.storage, (user_addr, "", denom)).unwrap().is_some() } /// Find whether a user has a collateral position AND has it enabled in the specified asset pub fn has_collateral_enabled(deps: Deps, user_addr: &Addr, denom: &str) -> bool { COLLATERALS - .may_load(deps.storage, (user_addr, denom)) + .may_load(deps.storage, (user_addr, "", denom)) .unwrap() .map(|collateral| collateral.enabled) .unwrap_or(false) diff --git a/contracts/red-bank/tests/test_admin.rs b/contracts/red-bank/tests/test_admin.rs index 68a495798..ae6a9b162 100644 --- a/contracts/red-bank/tests/test_admin.rs +++ b/contracts/red-bank/tests/test_admin.rs @@ -653,7 +653,7 @@ fn update_asset_new_reserve_factor_accrues_interest_rate() { let collateral = COLLATERALS .load( deps.as_ref().storage, - (&Addr::unchecked(MarsAddressType::RewardsCollector.to_string()), "somecoin"), + (&Addr::unchecked(MarsAddressType::RewardsCollector.to_string()), "", "somecoin"), ) .unwrap(); assert_eq!(collateral.amount_scaled, expected_rewards_scaled); diff --git a/contracts/red-bank/tests/test_deposit.rs b/contracts/red-bank/tests/test_deposit.rs index 67a02a20e..29dbebb97 100644 --- a/contracts/red-bank/tests/test_deposit.rs +++ b/contracts/red-bank/tests/test_deposit.rs @@ -100,7 +100,9 @@ fn depositing_with_no_coin_sent() { deps.as_mut(), mock_env(), mock_info(depositor_addr.as_str(), &[]), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ) .unwrap_err(); assert_eq!(err, PaymentError::NoFunds {}.into()); @@ -120,7 +122,9 @@ fn depositing_with_multiple_coins_sent() { deps.as_mut(), mock_env(), mock_info(depositor_addr.as_str(), &sent_coins), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ) .unwrap_err(); assert_eq!(err, PaymentError::MultipleDenoms {}.into()); @@ -141,7 +145,9 @@ fn depositing_to_non_existent_market() { deps.as_mut(), mock_env(), mock_info(depositor_addr.as_str(), &coins(123, false_denom)), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ) .unwrap_err(); assert_eq!(err, StdError::not_found(type_name::()).into()); @@ -177,7 +183,9 @@ fn depositing_to_disabled_market() { deps.as_mut(), mock_env(), mock_info(depositor_addr.as_str(), &coins(123, denom)), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ) .unwrap_err(); assert_eq!( @@ -226,7 +234,9 @@ fn depositing_above_cap() { deps.as_mut(), mock_env_at_block_time(10000100), mock_info(depositor_addr.as_str(), &coins(1_000_001, denom)), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ) .unwrap_err(); assert_eq!( @@ -241,7 +251,9 @@ fn depositing_above_cap() { deps.as_mut(), mock_env_at_block_time(10000100), mock_info(depositor_addr.as_str(), &coins(123, denom)), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ); assert!(result.is_ok()); } @@ -272,7 +284,9 @@ fn depositing_without_existing_position() { deps.as_mut(), mock_env_at_block_time(block_time), mock_info(depositor_addr.as_str(), &coins(deposit_amount, denom)), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ) .unwrap(); @@ -285,6 +299,7 @@ fn depositing_without_existing_position() { contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: depositor_addr.clone(), + account_id: None, denom: initial_market.denom.clone(), user_amount_scaled_before: Uint128::zero(), // NOTE: Protocol rewards accrued is zero, so here it's initial total supply @@ -319,7 +334,7 @@ fn depositing_without_existing_position() { // the depositor previously did not have a collateral position // a position should have been created with the correct scaled amount, and enabled by default - let collateral = COLLATERALS.load(deps.as_ref().storage, (&depositor_addr, denom)).unwrap(); + let collateral = COLLATERALS.load(deps.as_ref().storage, (&depositor_addr, "", denom)).unwrap(); assert_eq!( collateral, Collateral { @@ -359,7 +374,9 @@ fn depositing_with_existing_position() { deps.as_mut(), mock_env_at_block_time(block_time), mock_info(depositor_addr.as_str(), &coins(deposit_amount, denom)), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ) .unwrap(); @@ -372,6 +389,7 @@ fn depositing_with_existing_position() { contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: depositor_addr.clone(), + account_id: None, denom: initial_market.denom.clone(), user_amount_scaled_before: collateral_amount_scaled, // NOTE: Protocol rewards accrued is zero, so here it's initial total supply @@ -384,7 +402,7 @@ fn depositing_with_existing_position() { // the depositor's scaled collateral amount should have been increased // however, the `enabled` status should not been affected - let collateral = COLLATERALS.load(deps.as_ref().storage, (&depositor_addr, denom)).unwrap(); + let collateral = COLLATERALS.load(deps.as_ref().storage, (&depositor_addr, "", denom)).unwrap(); let expected = collateral_amount_scaled + expected_mint_amount; assert_eq!( collateral, @@ -394,61 +412,3 @@ fn depositing_with_existing_position() { } ); } - -#[test] -fn depositing_on_behalf_of_cannot_enable_collateral() { - let TestSuite { - mut deps, - denom, - depositor_addr, - .. - } = setup_test(); - - deps.querier.set_oracle_price(denom, Decimal::one()); - - let on_behalf_of_addr = Addr::unchecked("jake"); - - let block_time = 10000300; - - // 'on_behalf_of_addr' deposit funds to their own account - execute( - deps.as_mut(), - mock_env_at_block_time(block_time), - mock_info(on_behalf_of_addr.as_str(), &coins(1u128, denom)), - ExecuteMsg::Deposit {}, - ) - .unwrap(); - - // 'on_behalf_of_addr' should have collateral enabled - let collateral = COLLATERALS.load(deps.as_ref().storage, (&on_behalf_of_addr, denom)).unwrap(); - assert!(collateral.enabled); - - // 'on_behalf_of_addr' disables asset as collateral - execute( - deps.as_mut(), - mock_env_at_block_time(block_time), - mock_info(on_behalf_of_addr.as_str(), &[]), - ExecuteMsg::UpdateAssetCollateralStatus { - denom: denom.to_string(), - enable: false, - }, - ) - .unwrap(); - - // verify asset is disabled as collateral for 'on_behalf_of_addr' - let collateral = COLLATERALS.load(deps.as_ref().storage, (&on_behalf_of_addr, denom)).unwrap(); - assert!(!collateral.enabled); - - // 'depositor_addr' deposits a small amount of funds to 'on_behalf_of_addr' to enable his asset as collateral - execute( - deps.as_mut(), - mock_env_at_block_time(block_time), - mock_info(depositor_addr.as_str(), &coins(1u128, denom)), - ExecuteMsg::Deposit {}, - ) - .unwrap(); - - // 'on_behalf_of_addr' doesn't have the asset enabled as collateral - let collateral = COLLATERALS.load(deps.as_ref().storage, (&on_behalf_of_addr, denom)).unwrap(); - assert!(!collateral.enabled); -} diff --git a/contracts/red-bank/tests/test_liquidate.rs b/contracts/red-bank/tests/test_liquidate.rs index 06e268562..ed9efcdf6 100644 --- a/contracts/red-bank/tests/test_liquidate.rs +++ b/contracts/red-bank/tests/test_liquidate.rs @@ -801,14 +801,18 @@ fn response_verification() { deps.as_mut(), env.clone(), mock_info(provider.as_str(), &[coin(1000000, "uusdc")]), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ) .unwrap(); execute( deps.as_mut(), env.clone(), mock_info(provider.as_str(), &[coin(1000000, "untrn")]), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ) .unwrap(); @@ -817,14 +821,18 @@ fn response_verification() { deps.as_mut(), env.clone(), mock_info(liquidatee.as_str(), &[coin(10000, "uosmo")]), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ) .unwrap(); execute( deps.as_mut(), env.clone(), mock_info(liquidatee.as_str(), &[coin(900, "uatom")]), - ExecuteMsg::Deposit {}, + ExecuteMsg::Deposit { + account_id: None, + }, ) .unwrap(); execute( @@ -869,6 +877,7 @@ fn response_verification() { deps.as_ref(), QueryMsg::UserCollateral { user: liquidatee.to_string(), + account_id: None, denom: "uosmo".to_string(), }, ); @@ -980,6 +989,7 @@ fn expected_messages( contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: user_addr.clone(), + account_id: None, denom: collateral_market.denom.clone(), user_amount_scaled_before: user_collateral_scaled, total_amount_scaled_before: collateral_market.collateral_total_scaled, @@ -991,6 +1001,7 @@ fn expected_messages( contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: recipient_addr.clone(), + account_id: None, denom: collateral_market.denom.clone(), user_amount_scaled_before: recipient_collateral_scaled, total_amount_scaled_before: collateral_market.collateral_total_scaled, @@ -1002,6 +1013,7 @@ fn expected_messages( contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: Addr::unchecked(MarsAddressType::RewardsCollector.to_string()), + account_id: None, denom: collateral_market.denom.clone(), user_amount_scaled_before: Uint128::zero(), total_amount_scaled_before: collateral_market.collateral_total_scaled, @@ -1013,6 +1025,7 @@ fn expected_messages( contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: Addr::unchecked(MarsAddressType::RewardsCollector.to_string()), + account_id: None, denom: debt_market.denom.clone(), user_amount_scaled_before: Uint128::zero(), total_amount_scaled_before: debt_market.collateral_total_scaled, diff --git a/contracts/red-bank/tests/test_payment.rs b/contracts/red-bank/tests/test_payment.rs index 588997c7a..b3ef8dde7 100644 --- a/contracts/red-bank/tests/test_payment.rs +++ b/contracts/red-bank/tests/test_payment.rs @@ -30,6 +30,7 @@ fn rejecting_unexpected_payments() { denom: "".into(), amount: None, recipient: None, + account_id: None, }, ) .unwrap_err(); diff --git a/contracts/red-bank/tests/test_query.rs b/contracts/red-bank/tests/test_query.rs index 82ac958e1..6547db7c2 100644 --- a/contracts/red-bank/tests/test_query.rs +++ b/contracts/red-bank/tests/test_query.rs @@ -31,7 +31,8 @@ fn query_collateral() { // Assert markets correctly return collateral status let collaterals = - query_user_collaterals(deps.as_ref(), &env.block, user_addr.clone(), None, None).unwrap(); + query_user_collaterals(deps.as_ref(), &env.block, user_addr.clone(), None, None, None) + .unwrap(); assert_eq!( collaterals, vec![UserCollateralResponse { @@ -47,7 +48,7 @@ fn query_collateral() { // Assert markets correctly return collateral status let collaterals = - query_user_collaterals(deps.as_ref(), &env.block, user_addr, None, None).unwrap(); + query_user_collaterals(deps.as_ref(), &env.block, user_addr, None, None, None).unwrap(); assert_eq!( collaterals, vec![ diff --git a/contracts/red-bank/tests/test_withdraw.rs b/contracts/red-bank/tests/test_withdraw.rs index d7dab0655..235fea0ea 100644 --- a/contracts/red-bank/tests/test_withdraw.rs +++ b/contracts/red-bank/tests/test_withdraw.rs @@ -85,6 +85,7 @@ fn withdrawing_more_than_balance() { denom: denom.to_string(), amount: Some(Uint128::from(2000u128)), recipient: None, + account_id: None, }, ) .unwrap_err(); @@ -128,6 +129,7 @@ fn withdrawing_partially() { denom: denom.to_string(), amount: Some(withdraw_amount), recipient: None, + account_id: None, }, ) .unwrap(); @@ -181,6 +183,7 @@ fn withdrawing_partially() { contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: Addr::unchecked(MarsAddressType::RewardsCollector.to_string()), + account_id: None, denom: denom.to_string(), user_amount_scaled_before: Uint128::zero(), total_amount_scaled_before: initial_market.collateral_total_scaled, @@ -192,6 +195,7 @@ fn withdrawing_partially() { contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: withdrawer_addr.clone(), + account_id: None, denom: denom.to_string(), user_amount_scaled_before: initial_deposit_amount_scaled, total_amount_scaled_before: initial_market.collateral_total_scaled @@ -229,12 +233,13 @@ fn withdrawing_partially() { assert_eq!(market.collateral_total_scaled, expected_total_collateral_amount_scaled); // the user's collateral scaled amount should have been decreased - let collateral = COLLATERALS.load(deps.as_ref().storage, (&withdrawer_addr, denom)).unwrap(); + let collateral = + COLLATERALS.load(deps.as_ref().storage, (&withdrawer_addr, "", denom)).unwrap(); assert_eq!(collateral.amount_scaled, expected_withdraw_amount_scaled_remaining); // the reward collector's collateral scaled amount should have been increased let rewards_addr = Addr::unchecked(MarsAddressType::RewardsCollector.to_string()); - let collateral = COLLATERALS.load(deps.as_ref().storage, (&rewards_addr, denom)).unwrap(); + let collateral = COLLATERALS.load(deps.as_ref().storage, (&rewards_addr, "", denom)).unwrap(); assert_eq!(collateral.amount_scaled, expected_rewards_amount_scaled); } @@ -261,6 +266,7 @@ fn withdrawing_completely() { denom: denom.to_string(), amount: None, recipient: None, + account_id: None, }, ) .unwrap(); @@ -297,6 +303,7 @@ fn withdrawing_completely() { contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: Addr::unchecked(MarsAddressType::RewardsCollector.to_string()), + account_id: None, denom: denom.to_string(), user_amount_scaled_before: Uint128::zero(), total_amount_scaled_before: initial_market.collateral_total_scaled, @@ -308,6 +315,7 @@ fn withdrawing_completely() { contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: withdrawer_addr.clone(), + account_id: None, denom: denom.to_string(), user_amount_scaled_before: withdrawer_balance_scaled, total_amount_scaled_before: initial_market.collateral_total_scaled @@ -368,6 +376,7 @@ fn withdrawing_to_another_user() { denom: denom.to_string(), amount: None, recipient: Some(recipient_addr.to_string()), + account_id: None, }, ) .unwrap(); @@ -405,6 +414,7 @@ fn withdrawing_to_another_user() { contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: Addr::unchecked(MarsAddressType::RewardsCollector.to_string()), + account_id: None, denom: denom.to_string(), user_amount_scaled_before: Uint128::zero(), total_amount_scaled_before: initial_market.collateral_total_scaled, @@ -416,6 +426,7 @@ fn withdrawing_to_another_user() { contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: withdrawer_addr.clone(), + account_id: None, denom: denom.to_string(), user_amount_scaled_before: withdrawer_balance_scaled, total_amount_scaled_before: initial_market.collateral_total_scaled @@ -561,7 +572,9 @@ fn setup_health_check_test() -> HealthCheckTestSuite { denoms.iter().zip(collaterals.iter()).for_each(|(denom, collateral)| { if !collateral.amount_scaled.is_zero() { - COLLATERALS.save(deps.as_mut().storage, (&withdrawer_addr, denom), collateral).unwrap(); + COLLATERALS + .save(deps.as_mut().storage, (&withdrawer_addr, "", denom), collateral) + .unwrap(); } }); @@ -661,6 +674,7 @@ fn withdrawing_if_health_factor_not_met() { denom: denoms[2].to_string(), amount: Some(withdraw_amount), recipient: None, + account_id: None, }, ) .unwrap_err(); @@ -697,6 +711,7 @@ fn withdrawing_if_health_factor_met() { denom: denoms[2].to_string(), amount: Some(withdraw_amount), recipient: None, + account_id: None, }, ) .unwrap(); @@ -711,6 +726,7 @@ fn withdrawing_if_health_factor_met() { contract_addr: MarsAddressType::Incentives.to_string(), msg: to_binary(&incentives::ExecuteMsg::BalanceChange { user_addr: withdrawer_addr.clone(), + account_id: None, denom: denoms[2].to_string(), user_amount_scaled_before: collaterals[2].amount_scaled, // NOTE: Protocol rewards accrued is zero, so here it's initial total supply @@ -733,7 +749,7 @@ fn withdrawing_if_health_factor_met() { let expected_collateral_total_amount_scaled_after = markets[2].collateral_total_scaled - expected_withdraw_amount_scaled; - let col = COLLATERALS.load(deps.as_ref().storage, (&withdrawer_addr, denoms[2])).unwrap(); + let col = COLLATERALS.load(deps.as_ref().storage, (&withdrawer_addr, "", denoms[2])).unwrap(); assert_eq!(col.amount_scaled, expected_withdrawer_balance_after); let market = MARKETS.load(deps.as_ref().storage, denoms[2]).unwrap(); diff --git a/contracts/rewards-collector/base/src/contract.rs b/contracts/rewards-collector/base/src/contract.rs index a3ef2a5c4..3df5d8df6 100644 --- a/contracts/rewards-collector/base/src/contract.rs +++ b/contracts/rewards-collector/base/src/contract.rs @@ -182,6 +182,7 @@ where denom: denom.clone(), amount, recipient: None, + account_id: None, })?, funds: vec![], }); @@ -211,6 +212,7 @@ where let claim_msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: incentives_addr.to_string(), msg: to_binary(&incentives::ExecuteMsg::ClaimRewards { + account_id: None, start_after_collateral_denom, start_after_incentive_denom, limit, diff --git a/contracts/rewards-collector/osmosis/tests/test_withdraw.rs b/contracts/rewards-collector/osmosis/tests/test_withdraw.rs index 788c71444..a2e5645aa 100644 --- a/contracts/rewards-collector/osmosis/tests/test_withdraw.rs +++ b/contracts/rewards-collector/osmosis/tests/test_withdraw.rs @@ -29,7 +29,8 @@ fn withdrawing_from_red_bank() { msg: to_binary(&mars_red_bank_types::red_bank::ExecuteMsg::Withdraw { denom: "uatom".to_string(), amount: Some(Uint128::new(42069)), - recipient: None + recipient: None, + account_id: None }) .unwrap(), funds: vec![] diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 876610864..a9650b18c 100755 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -27,6 +27,7 @@ anyhow = { workspace = true } cosmwasm-std = { workspace = true } cw-multi-test = { workspace = true } cw-it = { workspace = true, features = ["osmosis-test-tube"] } +mars-incentives = { workspace = true } mars-oracle-osmosis = { workspace = true } mars-oracle-base = { workspace = true } mars-osmosis = { workspace = true } diff --git a/integration-tests/tests/helpers.rs b/integration-tests/tests/helpers.rs index 868b8d380..60b54f10b 100644 --- a/integration-tests/tests/helpers.rs +++ b/integration-tests/tests/helpers.rs @@ -4,7 +4,6 @@ use anyhow::Result as AnyResult; use cosmwasm_std::{Coin, Decimal, Fraction, Uint128}; use cw_multi_test::AppResponse; use mars_params::types::asset::{AssetParams, CmSettings, LiquidationBonus, RedBankSettings}; -use mars_red_bank::error::ContractError; use mars_red_bank_types::red_bank::{ InitOrUpdateAssetParams, InterestRateModel, UserHealthStatus, UserPositionResponse, }; @@ -218,11 +217,21 @@ pub fn swap( .unwrap() } -pub fn assert_err(res: AnyResult, err: ContractError) { +pub fn assert_red_bank_err(res: AnyResult, err: mars_red_bank::error::ContractError) { match res { Ok(_) => panic!("Result was not an error"), Err(generic_err) => { - let contract_err: ContractError = generic_err.downcast().unwrap(); + let contract_err: mars_red_bank::error::ContractError = generic_err.downcast().unwrap(); + assert_eq!(contract_err, err); + } + } +} + +pub fn assert_incentives_err(res: AnyResult, err: mars_incentives::ContractError) { + match res { + Ok(_) => panic!("Result was not an error"), + Err(generic_err) => { + let contract_err: mars_incentives::ContractError = generic_err.downcast().unwrap(); assert_eq!(contract_err, err); } } diff --git a/integration-tests/tests/test_incentives.rs b/integration-tests/tests/test_incentives.rs index fe449fdde..793b364db 100644 --- a/integration-tests/tests/test_incentives.rs +++ b/integration-tests/tests/test_incentives.rs @@ -46,12 +46,12 @@ fn rewards_claim() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uusdc"); assert_eq!(user_collateral.amount.u128(), funded_amt); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); mock_env.increment_by_time(86400); // 24 hours - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::new(864000)); incentives.claim_rewards(&mut mock_env, &user).unwrap(); @@ -61,10 +61,185 @@ fn rewards_claim() { let mars_balance = mock_env.query_balance(&incentives.contract_addr, "umars").unwrap(); assert_eq!(mars_balance.amount, Uint128::from(ONE_WEEK_IN_SEC * 10 - 864000)); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); } +// Credit accounts can deposit / withdraw from Red Bank and accure rewards in incentives contract. +#[test] +fn rewards_claim_for_credit_account() { + let owner = Addr::unchecked("owner"); + let mut mock_env = MockEnvBuilder::new(None, owner).build(); + + let red_bank = mock_env.red_bank.clone(); + let params = mock_env.params.clone(); + + let (market_params, asset_params) = default_asset_params("uusdc"); + red_bank.init_asset(&mut mock_env, "uusdc", market_params); + params.init_params(&mut mock_env, asset_params); + + let incentives = mock_env.incentives.clone(); + incentives.whitelist_incentive_denoms(&mut mock_env, &[("umars", 3)]); + incentives.init_asset_incentive_from_current_block( + &mut mock_env, + "uusdc", + "umars", + 10, + ONE_WEEK_IN_SEC, + ); + + // setup credit accounts + let credit_manager = mock_env.credit_manager.clone(); + let funded_amt = 10_000_000_000u128; + mock_env.fund_account(&credit_manager, &[coin(funded_amt, "uusdc")]); + + let acc_id_1 = "101".to_string(); + let acc_id_2 = "205".to_string(); + let funded_amt_acc_id_1 = 600_000_000u128; // 60% of total deposited amount + let funded_amt_acc_id_2 = 400_000_000u128; // 40% of total deposited amount + + // credit accounts deposit to Red Bank + red_bank + .deposit_with_acc_id( + &mut mock_env, + &credit_manager, + coin(funded_amt_acc_id_1, "uusdc"), + Some(acc_id_1.clone()), + ) + .unwrap(); + red_bank + .deposit_with_acc_id( + &mut mock_env, + &credit_manager, + coin(funded_amt_acc_id_2, "uusdc"), + Some(acc_id_2.clone()), + ) + .unwrap(); + let user_collateral = red_bank.query_user_collateral(&mut mock_env, &credit_manager, "uusdc"); + assert_eq!(user_collateral.amount.u128(), 0); + let acc_id_1_collateral = red_bank.query_user_collateral_with_acc_id( + &mut mock_env, + &credit_manager, + Some(acc_id_1.clone()), + "uusdc", + ); + assert_eq!(acc_id_1_collateral.amount.u128(), funded_amt_acc_id_1); + let acc_id_2_collateral = red_bank.query_user_collateral_with_acc_id( + &mut mock_env, + &credit_manager, + Some(acc_id_2.clone()), + "uusdc", + ); + assert_eq!(acc_id_2_collateral.amount.u128(), funded_amt_acc_id_2); + + // no rewards in the deposit block + let rewards_balance_acc_id_1 = incentives + .query_unclaimed_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_1.clone())) + .unwrap(); + assert_eq!(rewards_balance_acc_id_1[0].amount, Uint128::zero()); + let rewards_balance_acc_id_2 = incentives + .query_unclaimed_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_2.clone())) + .unwrap(); + assert_eq!(rewards_balance_acc_id_2[0].amount, Uint128::zero()); + + // move 24 hours + mock_env.increment_by_time(86400); + + // credit accounts should accure rewards proportionally + let rewards_balance_acc_id_1 = incentives + .query_unclaimed_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_1.clone())) + .unwrap(); + assert_eq!(rewards_balance_acc_id_1[0].amount, Uint128::new(518400)); // 60% * 864000 + let rewards_balance_acc_id_2 = incentives + .query_unclaimed_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_2.clone())) + .unwrap(); + assert_eq!(rewards_balance_acc_id_2[0].amount, Uint128::new(345600)); // 40% * 864000 + + // claiming credit manager rewards without account id should fail + incentives.claim_rewards(&mut mock_env, &credit_manager).unwrap_err(); + // query credit manager rewards without account id should return zero + let rewards_balance = + incentives.query_unclaimed_rewards(&mut mock_env, &credit_manager).unwrap(); + assert_eq!(rewards_balance[0].amount, Uint128::zero()); + + // claim rewards for credit accounts + incentives + .claim_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_1.clone())) + .unwrap(); + let rewards_balance_acc_id_1 = incentives + .query_unclaimed_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_1.clone())) + .unwrap(); + assert_eq!(rewards_balance_acc_id_1[0].amount, Uint128::zero()); + incentives + .claim_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_2.clone())) + .unwrap(); + let rewards_balance_acc_id_2 = incentives + .query_unclaimed_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_2.clone())) + .unwrap(); + assert_eq!(rewards_balance_acc_id_2[0].amount, Uint128::zero()); + + // credit accounts withdraw from Red Bank + let withdraw_amt_acc_id_1 = 300_000_000u128; + let withdraw_amt_acc_id_2 = 100_000_000u128; + red_bank + .withdraw_with_acc_id( + &mut mock_env, + &credit_manager, + "uusdc", + Some(Uint128::from(withdraw_amt_acc_id_1)), + Some(acc_id_1.clone()), + ) + .unwrap(); + red_bank + .withdraw_with_acc_id( + &mut mock_env, + &credit_manager, + "uusdc", + Some(Uint128::from(withdraw_amt_acc_id_2)), + Some(acc_id_2.clone()), + ) + .unwrap(); + let user_collateral = red_bank.query_user_collateral(&mut mock_env, &credit_manager, "uusdc"); + assert_eq!(user_collateral.amount.u128(), 0); + let acc_id_1_collateral = red_bank.query_user_collateral_with_acc_id( + &mut mock_env, + &credit_manager, + Some(acc_id_1.clone()), + "uusdc", + ); + assert_eq!(acc_id_1_collateral.amount.u128(), funded_amt_acc_id_1 - withdraw_amt_acc_id_1); + let acc_id_2_collateral = red_bank.query_user_collateral_with_acc_id( + &mut mock_env, + &credit_manager, + Some(acc_id_2.clone()), + "uusdc", + ); + assert_eq!(acc_id_2_collateral.amount.u128(), funded_amt_acc_id_2 - withdraw_amt_acc_id_2); + + // move 24 hours + mock_env.increment_by_time(86400); + + // credit accounts should accure rewards proportionally + let rewards_balance_acc_id_1 = incentives + .query_unclaimed_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_1.clone())) + .unwrap(); + assert_eq!(rewards_balance_acc_id_1[0].amount, Uint128::new(432000)); // 50% * 864000 + let rewards_balance_acc_id_2 = incentives + .query_unclaimed_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_2.clone())) + .unwrap(); + assert_eq!(rewards_balance_acc_id_2[0].amount, Uint128::new(432000)); // 50% * 864000 + + // claim rewards for credit accounts + incentives.claim_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_1)).unwrap(); + incentives.claim_rewards_with_acc_id(&mut mock_env, &credit_manager, Some(acc_id_2)).unwrap(); + + // check balances for umars in credit manager and incentives contracts + let balance = mock_env.query_balance(&credit_manager, "umars").unwrap(); + assert_eq!(balance.amount, Uint128::new(864000 + 864000)); + let mars_balance = mock_env.query_balance(&incentives.contract_addr, "umars").unwrap(); + assert_eq!(mars_balance.amount, Uint128::from(ONE_WEEK_IN_SEC * 10 - 864000 - 864000)); +} + // User A deposited usdc in the redbank when incentives were 5 emissions per second // Then claimed rewards after one day // Then user A later deposits osmo in the red bank when incentives were 10 emissions per second without withdrawing usdc @@ -112,12 +287,12 @@ fn emissions_rates() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uusdc"); assert_eq!(user_collateral.amount.u128(), funded_amt); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); mock_env.increment_by_time(86400); // 24 hours - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::new(432000)); // 86400*5 incentives.claim_rewards(&mut mock_env, &user).unwrap(); @@ -125,7 +300,7 @@ fn emissions_rates() { let balance = mock_env.query_balance(&user, "umars").unwrap(); assert_eq!(balance.amount, Uint128::new(432000)); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); incentives.init_asset_incentive_from_current_block( @@ -144,12 +319,12 @@ fn emissions_rates() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uosmo"); assert_eq!(user_collateral.amount.u128(), funded_amt); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); mock_env.increment_by_time(86400); // 24 hours - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::new(1296000)); // 432000 + (86400*10) incentives.claim_rewards(&mut mock_env, &user).unwrap(); @@ -157,7 +332,7 @@ fn emissions_rates() { let balance = mock_env.query_balance(&user, "umars").unwrap(); assert_eq!(balance.amount, Uint128::new(1728000)); // 1296000 + 432000 - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); } @@ -206,12 +381,12 @@ fn no_incentives_accrued_after_withdraw() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uusdc"); assert_eq!(user_collateral.amount.u128(), funded_amt); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); mock_env.increment_by_time(86400); // 24 hours - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::new(432000)); // 86400 * 5 incentives.claim_rewards(&mut mock_env, &user).unwrap(); @@ -219,7 +394,7 @@ fn no_incentives_accrued_after_withdraw() { let balance = mock_env.query_balance(&user, "umars").unwrap(); assert_eq!(balance.amount, Uint128::new(432000)); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); red_bank.withdraw(&mut mock_env, &user, "uusdc", None).unwrap(); @@ -230,12 +405,12 @@ fn no_incentives_accrued_after_withdraw() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uosmo"); assert_eq!(user_collateral.amount, Uint128::zero()); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); mock_env.increment_by_time(86400); // 24 hours - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); } @@ -315,12 +490,12 @@ fn multiple_assets() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uosmo"); assert_eq!(user_collateral.amount.u128(), funded_amt); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); mock_env.increment_by_time(86400); // 24 hours - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::new(1555200)); } @@ -377,18 +552,18 @@ fn multiple_users() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user_b, "uusdc"); assert_eq!(user_collateral.amount.u128(), funded_amt_two); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_a); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_a).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_b); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_b).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::zero()); mock_env.increment_by_time(86400); // 24 hours - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_a); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_a).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::new(144000)); // (86400*5) * (1/3) - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_b); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_b).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::new(288000)); // (86400*5)/2 * (2/3) // User A withdraws, user B holds @@ -397,10 +572,10 @@ fn multiple_users() { mock_env.increment_by_time(86400); // 24 hours - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_a); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_a).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::new(144000)); // stays the same - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_b); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_b).unwrap(); assert_eq!(rewards_balance[0].amount, Uint128::new(720000)); // 288000 + (86400*5) } @@ -514,15 +689,18 @@ fn rewards_distributed_among_users_and_rewards_collector() { assert_eq!(uosmo_collateral_rc.amount, Uint128::zero()); // rewards-collector accrue rewards - let rewards_balance_rc = - incentives.query_unclaimed_rewards(&mut mock_env, &rewards_collector.contract_addr); + let rewards_balance_rc = incentives + .query_unclaimed_rewards(&mut mock_env, &rewards_collector.contract_addr) + .unwrap(); assert!(!rewards_balance_rc.is_empty()); println!("rewards_balance_rc: {:?}", rewards_balance_rc); // sum of unclaimed rewards should be equal to total umars available for finished incentive - let rewards_balance_user_a = incentives.query_unclaimed_rewards(&mut mock_env, &user_a); + let rewards_balance_user_a = + incentives.query_unclaimed_rewards(&mut mock_env, &user_a).unwrap(); println!("rewards_balance_user_a: {:?}", rewards_balance_user_a); - let rewards_balance_user_b = incentives.query_unclaimed_rewards(&mut mock_env, &user_b); + let rewards_balance_user_b = + incentives.query_unclaimed_rewards(&mut mock_env, &user_b).unwrap(); println!("rewards_balance_user_b: {:?}", rewards_balance_user_b); let total_claimed_rewards = rewards_balance_rc[0].amount + rewards_balance_user_a[0].amount diff --git a/integration-tests/tests/test_oracles.rs b/integration-tests/tests/test_oracles.rs index 6820b771f..e29bf452b 100644 --- a/integration-tests/tests/test_oracles.rs +++ b/integration-tests/tests/test_oracles.rs @@ -949,7 +949,15 @@ fn redbank_should_fail_if_no_price() { ) .unwrap(); - wasm.execute(&red_bank_addr, &Deposit {}, &[coin(1_000_000, "uatom")], depositor).unwrap(); + wasm.execute( + &red_bank_addr, + &Deposit { + account_id: None, + }, + &[coin(1_000_000, "uatom")], + depositor, + ) + .unwrap(); // execute msg should fail since it is attempting to query an asset from the oracle contract that doesn't have an LP pool set up wasm.execute( @@ -1003,7 +1011,15 @@ fn redbank_quering_oracle_successfully() { ) .unwrap(); - wasm.execute(&red_bank_addr, &Deposit {}, &[coin(1_000_000, "uatom")], depositor).unwrap(); + wasm.execute( + &red_bank_addr, + &Deposit { + account_id: None, + }, + &[coin(1_000_000, "uatom")], + depositor, + ) + .unwrap(); wasm.execute( &red_bank_addr, diff --git a/integration-tests/tests/test_rover_flow.rs b/integration-tests/tests/test_rover_flow.rs index e8a61fde1..0abc8c868 100644 --- a/integration-tests/tests/test_rover_flow.rs +++ b/integration-tests/tests/test_rover_flow.rs @@ -3,7 +3,7 @@ use mars_red_bank::error::ContractError; use mars_red_bank_types::red_bank::UserHealthStatus; use mars_testing::integration::mock_env::MockEnvBuilder; -use crate::helpers::{assert_err, default_asset_params}; +use crate::helpers::{assert_red_bank_err, default_asset_params}; mod helpers; @@ -50,7 +50,7 @@ fn rover_flow() { // rover can't borrow above the credit line let res_err = red_bank.borrow(&mut mock_env, &rover, "uusdc", rover_uusdc_limit + 1u128); - assert_err(res_err, ContractError::BorrowAmountExceedsUncollateralizedLoanLimit {}); + assert_red_bank_err(res_err, ContractError::BorrowAmountExceedsUncollateralizedLoanLimit {}); // rover borrows the entire line of credit let balance = mock_env.query_balance(&rover, "uusdc").unwrap(); @@ -76,7 +76,7 @@ fn rover_flow() { // can't borrow above the credit line let res_err = red_bank.borrow(&mut mock_env, &rover, "uusdc", 1u128); - assert_err(res_err, ContractError::BorrowAmountExceedsUncollateralizedLoanLimit {}); + assert_red_bank_err(res_err, ContractError::BorrowAmountExceedsUncollateralizedLoanLimit {}); // rover should be healthy (NotBorrowing because uncollateralized debt is not included in HF calculation) let position = red_bank.query_user_position(&mut mock_env, &rover); @@ -90,7 +90,7 @@ fn rover_flow() { "uusdc", Uint128::zero(), ); - assert_err(res_err, ContractError::UserHasUncollateralizedDebt {}); + assert_red_bank_err(res_err, ContractError::UserHasUncollateralizedDebt {}); let debt = red_bank.query_user_debt(&mut mock_env, &rover, "uusdc"); assert!(debt.uncollateralized); assert_eq!(debt.amount.u128(), rover_uusdc_limit); @@ -129,5 +129,5 @@ fn rover_flow() { "uusdc", Uint128::from(rover_uusdc_limit), ); - assert_err(res_err, ContractError::UserHasCollateralizedDebt {}); + assert_red_bank_err(res_err, ContractError::UserHasCollateralizedDebt {}); } diff --git a/integration-tests/tests/test_user_flow.rs b/integration-tests/tests/test_user_flow.rs index 52ecfbe27..8f3813b76 100644 --- a/integration-tests/tests/test_user_flow.rs +++ b/integration-tests/tests/test_user_flow.rs @@ -5,7 +5,7 @@ use mars_params::types::asset::LiquidationBonus; use mars_red_bank::error::ContractError; use mars_testing::integration::mock_env::{MockEnv, MockEnvBuilder, RedBank}; -use crate::helpers::{assert_err, default_asset_params, default_asset_params_with}; +use crate::helpers::{assert_red_bank_err, default_asset_params, default_asset_params_with}; mod helpers; @@ -343,7 +343,7 @@ fn internally_tracked_balances_used_for_borrow() { mock_env.fund_account(&borrower2, &[coin(funded_osmo, "uosmo")]); red_bank.deposit(&mut mock_env, &borrower2, coin(funded_osmo, "uosmo")).unwrap(); let res = red_bank.borrow(&mut mock_env, &borrower2, "uatom", donated_atom); - assert_err( + assert_red_bank_err( res, ContractError::InvalidBorrowAmount { denom: "uatom".to_string(), diff --git a/packages/testing/src/incentives_querier.rs b/packages/testing/src/incentives_querier.rs index 55421b3c3..1d2b76c8d 100644 --- a/packages/testing/src/incentives_querier.rs +++ b/packages/testing/src/incentives_querier.rs @@ -32,6 +32,7 @@ impl IncentivesQuerier { let ret: ContractResult = match query { QueryMsg::UserUnclaimedRewards { user: _, + account_id: _, start_after_collateral_denom: _, start_after_incentive_denom: _, limit: _, diff --git a/packages/testing/src/integration/mock_env.rs b/packages/testing/src/integration/mock_env.rs index f4da4f848..2eeeaf24b 100644 --- a/packages/testing/src/integration/mock_env.rs +++ b/packages/testing/src/integration/mock_env.rs @@ -33,6 +33,7 @@ pub struct MockEnv { pub red_bank: RedBank, pub rewards_collector: RewardsCollector, pub params: Params, + pub credit_manager: Addr, } #[derive(Clone)] @@ -176,10 +177,20 @@ impl Incentives { } pub fn claim_rewards(&self, env: &mut MockEnv, sender: &Addr) -> AnyResult { + self.claim_rewards_with_acc_id(env, sender, None) + } + + pub fn claim_rewards_with_acc_id( + &self, + env: &mut MockEnv, + sender: &Addr, + account_id: Option, + ) -> AnyResult { env.app.execute_contract( sender.clone(), self.contract_addr.clone(), &incentives::ExecuteMsg::ClaimRewards { + account_id, start_after_collateral_denom: None, start_after_incentive_denom: None, limit: None, @@ -188,19 +199,26 @@ impl Incentives { ) } - pub fn query_unclaimed_rewards(&self, env: &mut MockEnv, user: &Addr) -> Vec { - env.app - .wrap() - .query_wasm_smart( - self.contract_addr.clone(), - &incentives::QueryMsg::UserUnclaimedRewards { - user: user.to_string(), - start_after_collateral_denom: None, - start_after_incentive_denom: None, - limit: None, - }, - ) - .unwrap() + pub fn query_unclaimed_rewards(&self, env: &mut MockEnv, user: &Addr) -> StdResult> { + self.query_unclaimed_rewards_with_acc_id(env, user, None) + } + + pub fn query_unclaimed_rewards_with_acc_id( + &self, + env: &mut MockEnv, + user: &Addr, + account_id: Option, + ) -> StdResult> { + env.app.wrap().query_wasm_smart( + self.contract_addr.clone(), + &incentives::QueryMsg::UserUnclaimedRewards { + account_id, + user: user.to_string(), + start_after_collateral_denom: None, + start_after_incentive_denom: None, + limit: None, + }, + ) } } @@ -271,10 +289,22 @@ impl RedBank { } pub fn deposit(&self, env: &mut MockEnv, sender: &Addr, coin: Coin) -> AnyResult { + self.deposit_with_acc_id(env, sender, coin, None) + } + + pub fn deposit_with_acc_id( + &self, + env: &mut MockEnv, + sender: &Addr, + coin: Coin, + account_id: Option, + ) -> AnyResult { env.app.execute_contract( sender.clone(), self.contract_addr.clone(), - &red_bank::ExecuteMsg::Deposit {}, + &red_bank::ExecuteMsg::Deposit { + account_id, + }, &[coin], ) } @@ -315,6 +345,17 @@ impl RedBank { sender: &Addr, denom: &str, amount: Option, + ) -> AnyResult { + self.withdraw_with_acc_id(env, sender, denom, amount, None) + } + + pub fn withdraw_with_acc_id( + &self, + env: &mut MockEnv, + sender: &Addr, + denom: &str, + amount: Option, + account_id: Option, ) -> AnyResult { env.app.execute_contract( sender.clone(), @@ -323,6 +364,7 @@ impl RedBank { denom: denom.to_string(), amount, recipient: None, + account_id, }, &[], ) @@ -452,6 +494,16 @@ impl RedBank { env: &mut MockEnv, user: &Addr, denom: &str, + ) -> UserCollateralResponse { + self.query_user_collateral_with_acc_id(env, user, None, denom) + } + + pub fn query_user_collateral_with_acc_id( + &self, + env: &mut MockEnv, + user: &Addr, + account_id: Option, + denom: &str, ) -> UserCollateralResponse { env.app .wrap() @@ -459,6 +511,7 @@ impl RedBank { self.contract_addr.clone(), &red_bank::QueryMsg::UserCollateral { user: user.to_string(), + account_id, denom: denom.to_string(), }, ) @@ -469,6 +522,15 @@ impl RedBank { &self, env: &mut MockEnv, user: &Addr, + ) -> HashMap { + self.query_user_collaterals_with_acc_id(env, user, None) + } + + pub fn query_user_collaterals_with_acc_id( + &self, + env: &mut MockEnv, + user: &Addr, + account_id: Option, ) -> HashMap { let res: Vec = env .app @@ -477,6 +539,7 @@ impl RedBank { self.contract_addr.clone(), &red_bank::QueryMsg::UserCollaterals { user: user.to_string(), + account_id, start_after: None, limit: Some(100), }, @@ -616,6 +679,8 @@ pub struct MockEnvBuilder { slippage_tolerance: Decimal, pyth_contract_addr: String, + + credit_manager_contract_addr: String, } impl MockEnvBuilder { @@ -635,6 +700,8 @@ impl MockEnvBuilder { slippage_tolerance: Decimal::percent(5), pyth_contract_addr: "osmo1svg55quy7jjee6dn0qx85qxxvx5cafkkw4tmqpcjr9dx99l0zrhs4usft5" .to_string(), // correct bech32 addr to pass validation + credit_manager_contract_addr: + "osmo1q7khj532p2fyvmnu83tul6xddl6yl0d0kmrzdz2pfel3lkxem92sw6zqrl".to_string(), } } @@ -690,7 +757,6 @@ impl MockEnvBuilder { let red_bank_addr = self.deploy_red_bank(&address_provider_addr); let rewards_collector_addr = self.deploy_rewards_collector_osmosis(&address_provider_addr); let params_addr = self.deploy_params_osmosis(&address_provider_addr); - let credit_manager_addr = Addr::unchecked("credit_manager"); self.update_address_provider( &address_provider_addr, @@ -709,7 +775,12 @@ impl MockEnvBuilder { &rewards_collector_addr, ); self.update_address_provider(&address_provider_addr, MarsAddressType::Params, ¶ms_addr); - self.update_address_provider(&address_provider_addr, MarsAddressType::CreditManager, &credit_manager_addr); + let cm_addr = Addr::unchecked(&self.credit_manager_contract_addr); + self.update_address_provider( + &address_provider_addr, + MarsAddressType::CreditManager, + &cm_addr, + ); MockEnv { app: take(&mut self.app), @@ -732,6 +803,7 @@ impl MockEnvBuilder { params: Params { contract_addr: params_addr, }, + credit_manager: cm_addr, } } diff --git a/packages/testing/src/red_bank_querier.rs b/packages/testing/src/red_bank_querier.rs index 87ce73ea8..d69db38ef 100644 --- a/packages/testing/src/red_bank_querier.rs +++ b/packages/testing/src/red_bank_querier.rs @@ -24,6 +24,7 @@ impl RedBankQuerier { }, QueryMsg::UserCollateral { user, + account_id: _, denom, } => match self.users_denoms_collaterals.get(&(user.clone(), denom)) { Some(collateral) => to_binary(&collateral).into(), diff --git a/packages/types/src/address_provider.rs b/packages/types/src/address_provider.rs index 9d6a6ea2f..c212ec890 100644 --- a/packages/types/src/address_provider.rs +++ b/packages/types/src/address_provider.rs @@ -13,6 +13,7 @@ pub enum MarsAddressType { RedBank, RewardsCollector, Params, + CreditManager, /// Protocol admin is an ICS-27 interchain account controlled by Mars Hub's x/gov module. /// This account will take the owner and admin roles of red-bank contracts. /// @@ -34,12 +35,12 @@ pub enum MarsAddressType { SafetyFund, /// The swapper contract on the chain Swapper, - CreditManager, } impl fmt::Display for MarsAddressType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match self { + MarsAddressType::CreditManager => "credit_manager", MarsAddressType::FeeCollector => "fee_collector", MarsAddressType::Incentives => "incentives", MarsAddressType::Oracle => "oracle", @@ -49,7 +50,6 @@ impl fmt::Display for MarsAddressType { MarsAddressType::RewardsCollector => "rewards_collector", MarsAddressType::SafetyFund => "safety_fund", MarsAddressType::Swapper => "swapper", - MarsAddressType::CreditManager => "credit_manager", }; write!(f, "{s}") } @@ -60,6 +60,7 @@ impl FromStr for MarsAddressType { fn from_str(s: &str) -> Result { match s { + "credit_manager" => Ok(MarsAddressType::CreditManager), "fee_collector" => Ok(MarsAddressType::FeeCollector), "incentives" => Ok(MarsAddressType::Incentives), "oracle" => Ok(MarsAddressType::Oracle), diff --git a/packages/types/src/incentives.rs b/packages/types/src/incentives.rs index 8aceee19b..41b4ad52a 100644 --- a/packages/types/src/incentives.rs +++ b/packages/types/src/incentives.rs @@ -125,6 +125,8 @@ pub enum ExecuteMsg { /// User address. Address is trusted as it must be validated by the Red Bank /// contract before calling this method user_addr: Addr, + /// Credit account id (Rover) + account_id: Option, /// Denom of the asset of which deposited balance is changed denom: String, /// The user's scaled collateral amount up to the instant before the change @@ -136,6 +138,8 @@ pub enum ExecuteMsg { /// Claim rewards. MARS rewards accrued by the user will be staked into xMARS before /// being sent. ClaimRewards { + /// Credit account id (Rover) + account_id: Option, /// Start pagination after this collateral denom start_after_collateral_denom: Option, /// Start pagination after this incentive denom. If supplied you must also supply @@ -229,6 +233,8 @@ pub enum QueryMsg { UserUnclaimedRewards { /// The user address for which to query unclaimed rewards user: String, + /// Credit account id (Rover) + account_id: Option, /// Start pagination after this collateral denom start_after_collateral_denom: Option, /// Start pagination after this incentive denom. If supplied you must also supply diff --git a/packages/types/src/red_bank/msg.rs b/packages/types/src/red_bank/msg.rs index 12359b4c3..a01d99d61 100644 --- a/packages/types/src/red_bank/msg.rs +++ b/packages/types/src/red_bank/msg.rs @@ -53,7 +53,10 @@ pub enum ExecuteMsg { /// Deposit native coins. Deposited coins must be sent in the transaction /// this call is made - Deposit {}, + Deposit { + /// Credit account id (Rover) + account_id: Option, + }, /// Withdraw native coins Withdraw { @@ -63,6 +66,8 @@ pub enum ExecuteMsg { amount: Option, /// The address where the withdrawn amount is sent recipient: Option, + /// Credit account id (Rover) + account_id: Option, }, /// Borrow native coins. If borrow allowed, amount is added to caller's debt @@ -174,6 +179,7 @@ pub enum QueryMsg { #[returns(crate::red_bank::UserCollateralResponse)] UserCollateral { user: String, + account_id: Option, denom: String, }, @@ -181,6 +187,7 @@ pub enum QueryMsg { #[returns(Vec)] UserCollaterals { user: String, + account_id: Option, start_after: Option, limit: Option, }, diff --git a/schemas/mars-incentives/mars-incentives.json b/schemas/mars-incentives/mars-incentives.json index 7ef20e6f0..455cc19a9 100644 --- a/schemas/mars-incentives/mars-incentives.json +++ b/schemas/mars-incentives/mars-incentives.json @@ -141,6 +141,13 @@ "user_amount_scaled_before" ], "properties": { + "account_id": { + "description": "Credit account id (Rover)", + "type": [ + "string", + "null" + ] + }, "denom": { "description": "Denom of the asset of which deposited balance is changed", "type": "string" @@ -185,6 +192,13 @@ "claim_rewards": { "type": "object", "properties": { + "account_id": { + "description": "Credit account id (Rover)", + "type": [ + "string", + "null" + ] + }, "limit": { "description": "The maximum number of results to return. If not set, 5 is used. If larger than 10, 10 is used.", "type": [ @@ -571,6 +585,13 @@ "user" ], "properties": { + "account_id": { + "description": "Credit account id (Rover)", + "type": [ + "string", + "null" + ] + }, "limit": { "description": "The maximum number of results to return. If not set, 5 is used. If larger than 10, 10 is used.", "type": [ diff --git a/schemas/mars-red-bank/mars-red-bank.json b/schemas/mars-red-bank/mars-red-bank.json index 5454bc91b..a9e61ef47 100644 --- a/schemas/mars-red-bank/mars-red-bank.json +++ b/schemas/mars-red-bank/mars-red-bank.json @@ -189,6 +189,15 @@ "properties": { "deposit": { "type": "object", + "properties": { + "account_id": { + "description": "Credit account id (Rover)", + "type": [ + "string", + "null" + ] + } + }, "additionalProperties": false } }, @@ -207,6 +216,13 @@ "denom" ], "properties": { + "account_id": { + "description": "Credit account id (Rover)", + "type": [ + "string", + "null" + ] + }, "amount": { "description": "Amount to be withdrawn. If None is specified, the full amount will be withdrawn.", "anyOf": [ @@ -740,6 +756,12 @@ "user" ], "properties": { + "account_id": { + "type": [ + "string", + "null" + ] + }, "denom": { "type": "string" }, @@ -765,6 +787,12 @@ "user" ], "properties": { + "account_id": { + "type": [ + "string", + "null" + ] + }, "limit": { "type": [ "integer", diff --git a/scripts/types/generated/mars-incentives/MarsIncentives.client.ts b/scripts/types/generated/mars-incentives/MarsIncentives.client.ts index 566f4bbc2..17a229869 100644 --- a/scripts/types/generated/mars-incentives/MarsIncentives.client.ts +++ b/scripts/types/generated/mars-incentives/MarsIncentives.client.ts @@ -72,11 +72,13 @@ export interface MarsIncentivesReadOnlyInterface { startAfterTimestamp?: number }) => Promise userUnclaimedRewards: ({ + accountId, limit, startAfterCollateralDenom, startAfterIncentiveDenom, user, }: { + accountId?: string limit?: number startAfterCollateralDenom?: string startAfterIncentiveDenom?: string @@ -186,11 +188,13 @@ export class MarsIncentivesQueryClient implements MarsIncentivesReadOnlyInterfac }) } userUnclaimedRewards = async ({ + accountId, limit, startAfterCollateralDenom, startAfterIncentiveDenom, user, }: { + accountId?: string limit?: number startAfterCollateralDenom?: string startAfterIncentiveDenom?: string @@ -198,6 +202,7 @@ export class MarsIncentivesQueryClient implements MarsIncentivesReadOnlyInterfac }): Promise => { return this.client.queryContractSmart(this.contractAddress, { user_unclaimed_rewards: { + account_id: accountId, limit, start_after_collateral_denom: startAfterCollateralDenom, start_after_incentive_denom: startAfterIncentiveDenom, @@ -246,11 +251,13 @@ export interface MarsIncentivesInterface extends MarsIncentivesReadOnlyInterface ) => Promise balanceChange: ( { + accountId, denom, totalAmountScaledBefore, userAddr, userAmountScaledBefore, }: { + accountId?: string denom: string totalAmountScaledBefore: Uint128 userAddr: Addr @@ -262,10 +269,12 @@ export interface MarsIncentivesInterface extends MarsIncentivesReadOnlyInterface ) => Promise claimRewards: ( { + accountId, limit, startAfterCollateralDenom, startAfterIncentiveDenom, }: { + accountId?: string limit?: number startAfterCollateralDenom?: string startAfterIncentiveDenom?: string @@ -377,11 +386,13 @@ export class MarsIncentivesClient } balanceChange = async ( { + accountId, denom, totalAmountScaledBefore, userAddr, userAmountScaledBefore, }: { + accountId?: string denom: string totalAmountScaledBefore: Uint128 userAddr: Addr @@ -396,6 +407,7 @@ export class MarsIncentivesClient this.contractAddress, { balance_change: { + account_id: accountId, denom, total_amount_scaled_before: totalAmountScaledBefore, user_addr: userAddr, @@ -409,10 +421,12 @@ export class MarsIncentivesClient } claimRewards = async ( { + accountId, limit, startAfterCollateralDenom, startAfterIncentiveDenom, }: { + accountId?: string limit?: number startAfterCollateralDenom?: string startAfterIncentiveDenom?: string @@ -426,6 +440,7 @@ export class MarsIncentivesClient this.contractAddress, { claim_rewards: { + account_id: accountId, limit, start_after_collateral_denom: startAfterCollateralDenom, start_after_incentive_denom: startAfterIncentiveDenom, diff --git a/scripts/types/generated/mars-incentives/MarsIncentives.react-query.ts b/scripts/types/generated/mars-incentives/MarsIncentives.react-query.ts index 9386101c4..3014ffed2 100644 --- a/scripts/types/generated/mars-incentives/MarsIncentives.react-query.ts +++ b/scripts/types/generated/mars-incentives/MarsIncentives.react-query.ts @@ -94,6 +94,7 @@ export function useMarsIncentivesWhitelistQuery({ export interface MarsIncentivesUserUnclaimedRewardsQuery extends MarsIncentivesReactQuery { args: { + accountId?: string limit?: number startAfterCollateralDenom?: string startAfterIncentiveDenom?: string @@ -110,6 +111,7 @@ export function useMarsIncentivesUserUnclaimedRewardsQuery( () => client ? client.userUnclaimedRewards({ + accountId: args.accountId, limit: args.limit, startAfterCollateralDenom: args.startAfterCollateralDenom, startAfterIncentiveDenom: args.startAfterIncentiveDenom, @@ -304,6 +306,7 @@ export function useMarsIncentivesUpdateConfigMutation( export interface MarsIncentivesClaimRewardsMutation { client: MarsIncentivesClient msg: { + accountId?: string limit?: number startAfterCollateralDenom?: string startAfterIncentiveDenom?: string @@ -329,6 +332,7 @@ export function useMarsIncentivesClaimRewardsMutation( export interface MarsIncentivesBalanceChangeMutation { client: MarsIncentivesClient msg: { + accountId?: string denom: string totalAmountScaledBefore: Uint128 userAddr: Addr diff --git a/scripts/types/generated/mars-incentives/MarsIncentives.types.ts b/scripts/types/generated/mars-incentives/MarsIncentives.types.ts index f3b2ca4de..1098c5bb7 100644 --- a/scripts/types/generated/mars-incentives/MarsIncentives.types.ts +++ b/scripts/types/generated/mars-incentives/MarsIncentives.types.ts @@ -29,6 +29,7 @@ export type ExecuteMsg = } | { balance_change: { + account_id?: string | null denom: string total_amount_scaled_before: Uint128 user_addr: Addr @@ -37,6 +38,7 @@ export type ExecuteMsg = } | { claim_rewards: { + account_id?: string | null limit?: number | null start_after_collateral_denom?: string | null start_after_incentive_denom?: string | null @@ -111,6 +113,7 @@ export type QueryMsg = } | { user_unclaimed_rewards: { + account_id?: string | null limit?: number | null start_after_collateral_denom?: string | null start_after_incentive_denom?: string | null diff --git a/scripts/types/generated/mars-red-bank/MarsRedBank.client.ts b/scripts/types/generated/mars-red-bank/MarsRedBank.client.ts index e603cb6ef..4b84c7f1e 100644 --- a/scripts/types/generated/mars-red-bank/MarsRedBank.client.ts +++ b/scripts/types/generated/mars-red-bank/MarsRedBank.client.ts @@ -67,17 +67,21 @@ export interface MarsRedBankReadOnlyInterface { user: string }) => Promise userCollateral: ({ + accountId, denom, user, }: { + accountId?: string denom: string user: string }) => Promise userCollaterals: ({ + accountId, limit, startAfter, user, }: { + accountId?: string limit?: number startAfter?: string user: string @@ -212,30 +216,36 @@ export class MarsRedBankQueryClient implements MarsRedBankReadOnlyInterface { }) } userCollateral = async ({ + accountId, denom, user, }: { + accountId?: string denom: string user: string }): Promise => { return this.client.queryContractSmart(this.contractAddress, { user_collateral: { + account_id: accountId, denom, user, }, }) } userCollaterals = async ({ + accountId, limit, startAfter, user, }: { + accountId?: string limit?: number startAfter?: string user: string }): Promise => { return this.client.queryContractSmart(this.contractAddress, { user_collaterals: { + account_id: accountId, limit, start_after: startAfter, user, @@ -364,16 +374,23 @@ export interface MarsRedBankInterface extends MarsRedBankReadOnlyInterface { _funds?: Coin[], ) => Promise deposit: ( + { + accountId, + }: { + accountId?: string + }, fee?: number | StdFee | 'auto', memo?: string, _funds?: Coin[], ) => Promise withdraw: ( { + accountId, amount, denom, recipient, }: { + accountId?: string amount?: Uint128 denom: string recipient?: string @@ -578,6 +595,11 @@ export class MarsRedBankClient extends MarsRedBankQueryClient implements MarsRed ) } deposit = async ( + { + accountId, + }: { + accountId?: string + }, fee: number | StdFee | 'auto' = 'auto', memo?: string, _funds?: Coin[], @@ -586,7 +608,9 @@ export class MarsRedBankClient extends MarsRedBankQueryClient implements MarsRed this.sender, this.contractAddress, { - deposit: {}, + deposit: { + account_id: accountId, + }, }, fee, memo, @@ -595,10 +619,12 @@ export class MarsRedBankClient extends MarsRedBankQueryClient implements MarsRed } withdraw = async ( { + accountId, amount, denom, recipient, }: { + accountId?: string amount?: Uint128 denom: string recipient?: string @@ -612,6 +638,7 @@ export class MarsRedBankClient extends MarsRedBankQueryClient implements MarsRed this.contractAddress, { withdraw: { + account_id: accountId, amount, denom, recipient, diff --git a/scripts/types/generated/mars-red-bank/MarsRedBank.react-query.ts b/scripts/types/generated/mars-red-bank/MarsRedBank.react-query.ts index 8ba59b9b5..9047d2f7e 100644 --- a/scripts/types/generated/mars-red-bank/MarsRedBank.react-query.ts +++ b/scripts/types/generated/mars-red-bank/MarsRedBank.react-query.ts @@ -245,6 +245,7 @@ export function useMarsRedBankUserPositionQuery({ export interface MarsRedBankUserCollateralsQuery extends MarsRedBankReactQuery { args: { + accountId?: string limit?: number startAfter?: string user: string @@ -260,6 +261,7 @@ export function useMarsRedBankUserCollateralsQuery client ? client.userCollaterals({ + accountId: args.accountId, limit: args.limit, startAfter: args.startAfter, user: args.user, @@ -271,6 +273,7 @@ export function useMarsRedBankUserCollateralsQuery extends MarsRedBankReactQuery { args: { + accountId?: string denom: string user: string } @@ -285,6 +288,7 @@ export function useMarsRedBankUserCollateralQuery client ? client.userCollateral({ + accountId: args.accountId, denom: args.denom, user: args.user, }) @@ -536,6 +540,7 @@ export function useMarsRedBankBorrowMutation( export interface MarsRedBankWithdrawMutation { client: MarsRedBankClient msg: { + accountId?: string amount?: Uint128 denom: string recipient?: string @@ -559,6 +564,9 @@ export function useMarsRedBankWithdrawMutation( } export interface MarsRedBankDepositMutation { client: MarsRedBankClient + msg: { + accountId?: string + } args?: { fee?: number | StdFee | 'auto' memo?: string @@ -572,7 +580,7 @@ export function useMarsRedBankDepositMutation( >, ) { return useMutation( - ({ client, args: { fee, memo, funds } = {} }) => client.deposit(fee, memo, funds), + ({ client, msg, args: { fee, memo, funds } = {} }) => client.deposit(msg, fee, memo, funds), options, ) } diff --git a/scripts/types/generated/mars-red-bank/MarsRedBank.types.ts b/scripts/types/generated/mars-red-bank/MarsRedBank.types.ts index 54fc21ed7..40c86bf51 100644 --- a/scripts/types/generated/mars-red-bank/MarsRedBank.types.ts +++ b/scripts/types/generated/mars-red-bank/MarsRedBank.types.ts @@ -41,10 +41,13 @@ export type ExecuteMsg = } } | { - deposit: {} + deposit: { + account_id?: string | null + } } | { withdraw: { + account_id?: string | null amount?: Uint128 | null denom: string recipient?: string | null @@ -145,12 +148,14 @@ export type QueryMsg = } | { user_collateral: { + account_id?: string | null denom: string user: string } } | { user_collaterals: { + account_id?: string | null limit?: number | null start_after?: string | null user: string From ca28b628bfd45fc160aefca2bdd55c8a2b64d23a Mon Sep 17 00:00:00 2001 From: piobab Date: Fri, 28 Jul 2023 12:09:15 +0200 Subject: [PATCH 15/27] Migrate to PoolManager for Pool and SpotPrice. Read StableSwap pool. (#279) --- Cargo.lock | 1 + Cargo.toml | 2 +- contracts/oracle/osmosis/src/helpers.rs | 51 +++++- contracts/oracle/osmosis/src/price_source.rs | 13 +- contracts/oracle/osmosis/tests/helpers.rs | 75 ++++++-- .../oracle/osmosis/tests/test_query_price.rs | 10 +- .../osmosis/tests/test_set_price_source.rs | 9 + .../osmosis/tests/helpers.rs | 14 +- contracts/swapper/osmosis/src/route.rs | 7 +- packages/chains/osmosis/Cargo.toml | 1 + packages/chains/osmosis/src/helpers.rs | 164 ++++++++++++++---- packages/chains/osmosis/src/lib.rs | 4 + packages/testing/src/mars_mock_querier.rs | 5 +- packages/testing/src/osmosis_querier.rs | 9 +- 14 files changed, 279 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09f1e4e84..9b70c1c3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1869,6 +1869,7 @@ version = "1.2.0" dependencies = [ "cosmwasm-std", "osmosis-std", + "prost 0.11.9", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index b1729a4f7..68b61d8f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ neutron-sdk = "0.6.0" # dev-dependencies cw-multi-test = "0.16.5" cw-it = "0.1.0" -osmosis-test-tube = "16.0.0" +osmosis-test-tube = "=16.1.1" test-case = "3.0.0" proptest = "1.1.0" diff --git a/contracts/oracle/osmosis/src/helpers.rs b/contracts/oracle/osmosis/src/helpers.rs index 342c9e2ce..4ffc2a1ec 100644 --- a/contracts/oracle/osmosis/src/helpers.rs +++ b/contracts/oracle/osmosis/src/helpers.rs @@ -1,28 +1,63 @@ use mars_oracle_base::{ContractError, ContractResult}; -use mars_osmosis::helpers::{has_denom, Pool}; +use mars_osmosis::{ + helpers::{CommonPoolData, Pool}, + BalancerPool, +}; use crate::DowntimeDetector; /// 48 hours in seconds const TWO_DAYS_IN_SECONDS: u64 = 172800u64; -/// Assert the Osmosis pool indicated by `pool_id` is of XYK type and assets are OSMO and `denom` +/// Assert the Osmosis pool indicated by `pool_id` is of Balancer XYK or StableSwap and assets are OSMO and `denom` pub fn assert_osmosis_pool_assets( pool: &Pool, denom: &str, base_denom: &str, ) -> ContractResult<()> { - assert_osmosis_xyk_pool(pool)?; + match pool { + Pool::Balancer(balancer_pool) => { + assert_osmosis_xyk_pool(balancer_pool)?; + } + Pool::StableSwap(_) => {} + }; + + assert_osmosis_pool_contains_two_assets(pool, denom, base_denom)?; + + Ok(()) +} + +/// Assert the Osmosis pool indicated by `pool_id` is Balancer XYK type +pub fn assert_osmosis_xyk_lp_pool(pool: &Pool) -> ContractResult<()> { + match pool { + Pool::Balancer(balancer_pool) => assert_osmosis_xyk_pool(balancer_pool)?, + Pool::StableSwap(stable_swap_pool) => { + return Err(ContractError::InvalidPriceSource { + reason: format!("StableSwap pool not supported. Pool id {}", stable_swap_pool.id), + }); + } + }; + + Ok(()) +} + +fn assert_osmosis_pool_contains_two_assets( + pool: &Pool, + denom: &str, + base_denom: &str, +) -> ContractResult<()> { + let pool_id = pool.get_pool_id(); + let pool_denoms = pool.get_pool_denoms(); - if !has_denom(base_denom, &pool.pool_assets) { + if !pool_denoms.contains(&base_denom.to_string()) { return Err(ContractError::InvalidPriceSource { - reason: format!("pool {} does not contain the base denom {}", pool.id, base_denom), + reason: format!("pool {} does not contain the base denom {}", pool_id, base_denom), }); } - if !has_denom(denom, &pool.pool_assets) { + if !pool_denoms.contains(&denom.to_string()) { return Err(ContractError::InvalidPriceSource { - reason: format!("pool {} does not contain {}", pool.id, denom), + reason: format!("pool {} does not contain {}", pool_id, denom), }); } @@ -30,7 +65,7 @@ pub fn assert_osmosis_pool_assets( } /// Assert the Osmosis pool indicated by `pool_id` is of XYK type -pub fn assert_osmosis_xyk_pool(pool: &Pool) -> ContractResult<()> { +pub fn assert_osmosis_xyk_pool(pool: &BalancerPool) -> ContractResult<()> { if pool.pool_assets.len() != 2 { return Err(ContractError::InvalidPriceSource { reason: format!( diff --git a/contracts/oracle/osmosis/src/price_source.rs b/contracts/oracle/osmosis/src/price_source.rs index f1c1ed599..22f0eba1a 100644 --- a/contracts/oracle/osmosis/src/price_source.rs +++ b/contracts/oracle/osmosis/src/price_source.rs @@ -3,7 +3,8 @@ use std::{cmp::min, fmt}; use cosmwasm_std::{Addr, Decimal, Decimal256, Deps, Empty, Env, Isqrt, Uint128, Uint256}; use cw_storage_plus::Map; use mars_oracle_base::{ - ContractError::InvalidPrice, ContractResult, PriceSourceChecked, PriceSourceUnchecked, + ContractError::{self, InvalidPrice}, + ContractResult, PriceSourceChecked, PriceSourceUnchecked, }; use mars_osmosis::helpers::{ query_arithmetic_twap_price, query_geometric_twap_price, query_pool, query_spot_price, @@ -369,7 +370,7 @@ impl PriceSourceUnchecked for OsmosisPriceSour pool_id, } => { let pool = query_pool(&deps.querier, *pool_id)?; - helpers::assert_osmosis_xyk_pool(&pool)?; + helpers::assert_osmosis_xyk_lp_pool(&pool)?; Ok(OsmosisPriceSourceChecked::XykLiquidityToken { pool_id: *pool_id, }) @@ -593,6 +594,14 @@ impl OsmosisPriceSourceChecked { ) -> ContractResult { // XYK pool asserted during price source creation let pool = query_pool(&deps.querier, pool_id)?; + let pool = match pool { + Pool::Balancer(pool) => pool, + Pool::StableSwap(pool) => { + return Err(ContractError::InvalidPrice { + reason: format!("StableSwap pool not supported. Pool id {}", pool.id), + }) + } + }; let coin0 = Pool::unwrap_coin(&pool.pool_assets[0].token)?; let coin1 = Pool::unwrap_coin(&pool.pool_assets[1].token)?; diff --git a/contracts/oracle/osmosis/tests/helpers.rs b/contracts/oracle/osmosis/tests/helpers.rs index 80b683e54..82bb4ea76 100644 --- a/contracts/oracle/osmosis/tests/helpers.rs +++ b/contracts/oracle/osmosis/tests/helpers.rs @@ -9,10 +9,10 @@ use cosmwasm_std::{ }; use mars_oracle_base::ContractError; use mars_oracle_osmosis::{contract::entry, msg::ExecuteMsg, OsmosisPriceSourceUnchecked}; -use mars_osmosis::helpers::{Pool, QueryPoolResponse}; +use mars_osmosis::{BalancerPool, StableSwapPool}; use mars_red_bank_types::oracle::msg::{InstantiateMsg, QueryMsg}; use mars_testing::{mock_info, MarsMockQuerier}; -use osmosis_std::types::osmosis::gamm::v1beta1::PoolAsset; +use osmosis_std::types::osmosis::{gamm::v1beta1::PoolAsset, poolmanager::v1beta1::PoolResponse}; use pyth_sdk_cw::PriceIdentifier; pub fn setup_test_with_pools() -> OwnedDeps { @@ -22,25 +22,40 @@ pub fn setup_test_with_pools() -> OwnedDeps OwnedDeps OwnedDeps OwnedDeps OwnedDeps { deps } -pub fn prepare_query_pool_response( +pub fn prepare_query_balancer_pool_response( pool_id: u64, assets: &[Coin], weights: &[u64], shares: &Coin, -) -> QueryPoolResponse { - let pool = Pool { +) -> PoolResponse { + let pool = BalancerPool { address: "address".to_string(), - id: pool_id.to_string(), + id: pool_id, pool_params: None, future_pool_governor: "future_pool_governor".to_string(), total_shares: Some(osmosis_std::types::cosmos::base::v1beta1::Coin { @@ -131,8 +151,8 @@ pub fn prepare_query_pool_response( pool_assets: prepare_pool_assets(assets, weights), total_weight: "".to_string(), }; - QueryPoolResponse { - pool, + PoolResponse { + pool: Some(pool.to_any()), } } @@ -155,6 +175,33 @@ fn prepare_pool_assets(coins: &[Coin], weights: &[u64]) -> Vec { .collect() } +pub fn prepare_query_stable_swap_pool_response(pool_id: u64, assets: &[Coin]) -> PoolResponse { + let pool_liquidity: Vec<_> = assets + .iter() + .map(|coin| osmosis_std::types::cosmos::base::v1beta1::Coin { + denom: coin.denom.clone(), + amount: coin.amount.to_string(), + }) + .collect(); + + let pool = StableSwapPool { + address: "osmo15v4mn84s9flhzpstkf9ql2mu0rnxh42pm8zhq47kh2fzs5zlwjsqaterkr".to_string(), + id: pool_id, + pool_params: None, + future_pool_governor: "".to_string(), + total_shares: Some(osmosis_std::types::cosmos::base::v1beta1::Coin { + denom: format!("gamm/pool/{pool_id}"), + amount: 4497913440357232330148u128.to_string(), + }), + pool_liquidity, + scaling_factors: vec![100000u64, 113890u64], + scaling_factor_controller: "osmo1k8c2m5cn322akk5wy8lpt87dd2f4yh9afcd7af".to_string(), + }; + PoolResponse { + pool: Some(pool.to_any()), + } +} + pub fn set_pyth_price_source(deps: DepsMut, denom: &str, price_id: PriceIdentifier) { set_price_source( deps, diff --git a/contracts/oracle/osmosis/tests/test_query_price.rs b/contracts/oracle/osmosis/tests/test_query_price.rs index 0b330b91d..dc5a1c611 100644 --- a/contracts/oracle/osmosis/tests/test_query_price.rs +++ b/contracts/oracle/osmosis/tests/test_query_price.rs @@ -18,7 +18,7 @@ use osmosis_std::types::osmosis::{ }; use pyth_sdk_cw::{Price, PriceFeed, PriceFeedResponse, PriceIdentifier}; -use crate::helpers::prepare_query_pool_response; +use crate::helpers::prepare_query_balancer_pool_response; mod helpers; @@ -804,7 +804,7 @@ fn querying_xyk_lp_price() { let assets = vec![coin(1, "uatom"), coin(1, "uosmo")]; deps.querier.set_query_pool_response( 10001, - prepare_query_pool_response( + prepare_query_balancer_pool_response( 10001, &assets, &[5000u64, 5000u64], @@ -815,7 +815,7 @@ fn querying_xyk_lp_price() { let assets = vec![coin(1, "umars"), coin(1, "uosmo")]; deps.querier.set_query_pool_response( 10002, - prepare_query_pool_response( + prepare_query_balancer_pool_response( 10002, &assets, &[5000u64, 5000u64], @@ -826,7 +826,7 @@ fn querying_xyk_lp_price() { let assets = vec![coin(10000, "uatom"), coin(885000, "umars")]; deps.querier.set_query_pool_response( 10003, - prepare_query_pool_response( + prepare_query_balancer_pool_response( 10003, &assets, &[5000u64, 5000u64], @@ -889,7 +889,7 @@ fn querying_xyk_lp_price() { let assets = vec![coin(6389, "uatom"), coin(1385000, "umars")]; deps.querier.set_query_pool_response( 10003, - prepare_query_pool_response( + prepare_query_balancer_pool_response( 10003, &assets, &[5000u64, 5000u64], diff --git a/contracts/oracle/osmosis/tests/test_set_price_source.rs b/contracts/oracle/osmosis/tests/test_set_price_source.rs index f7f23c349..d56fa5c91 100644 --- a/contracts/oracle/osmosis/tests/test_set_price_source.rs +++ b/contracts/oracle/osmosis/tests/test_set_price_source.rs @@ -926,6 +926,15 @@ fn setting_price_source_xyk_lp() { } ); + // attempting to use StableSwap pool + let err = set_price_source_xyk_lp("atom_mars_lp", 5555).unwrap_err(); + assert_eq!( + err, + ContractError::InvalidPriceSource { + reason: "StableSwap pool not supported. Pool id 5555".to_string() + } + ); + // properly set xyk lp price source let res = set_price_source_xyk_lp("uosmo_umars_lp", 89).unwrap(); assert_eq!(res.messages.len(), 0); diff --git a/contracts/rewards-collector/osmosis/tests/helpers.rs b/contracts/rewards-collector/osmosis/tests/helpers.rs index a7872d802..6760a31b3 100644 --- a/contracts/rewards-collector/osmosis/tests/helpers.rs +++ b/contracts/rewards-collector/osmosis/tests/helpers.rs @@ -5,11 +5,11 @@ use cosmwasm_std::{ testing::{mock_env, MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR}, Coin, Decimal, Deps, OwnedDeps, }; -use mars_osmosis::helpers::{Pool, QueryPoolResponse}; +use mars_osmosis::BalancerPool; use mars_red_bank_types::rewards_collector::{Config, InstantiateMsg, QueryMsg}; use mars_rewards_collector_osmosis::entry; use mars_testing::{mock_info, MarsMockQuerier}; -use osmosis_std::types::osmosis::gamm::v1beta1::PoolAsset; +use osmosis_std::types::osmosis::{gamm::v1beta1::PoolAsset, poolmanager::v1beta1::PoolResponse}; pub fn mock_instantiate_msg() -> InstantiateMsg { InstantiateMsg { @@ -93,10 +93,10 @@ fn prepare_query_pool_response( assets: &[Coin], weights: &[u64], shares: &Coin, -) -> QueryPoolResponse { - let pool = Pool { +) -> PoolResponse { + let pool = BalancerPool { address: "address".to_string(), - id: pool_id.to_string(), + id: pool_id, pool_params: None, future_pool_governor: "future_pool_governor".to_string(), total_shares: Some(osmosis_std::types::cosmos::base::v1beta1::Coin { @@ -106,8 +106,8 @@ fn prepare_query_pool_response( pool_assets: prepare_pool_assets(assets, weights), total_weight: "".to_string(), }; - QueryPoolResponse { - pool, + PoolResponse { + pool: Some(pool.to_any()), } } diff --git a/contracts/swapper/osmosis/src/route.rs b/contracts/swapper/osmosis/src/route.rs index 81486db4a..698eba5b5 100644 --- a/contracts/swapper/osmosis/src/route.rs +++ b/contracts/swapper/osmosis/src/route.rs @@ -2,7 +2,7 @@ use std::fmt; use cosmwasm_schema::cw_serde; use cosmwasm_std::{BlockInfo, CosmosMsg, Decimal, Empty, Env, Fraction, QuerierWrapper, Uint128}; -use mars_osmosis::helpers::{has_denom, query_arithmetic_twap_price, query_pool}; +use mars_osmosis::helpers::{query_arithmetic_twap_price, query_pool, CommonPoolData}; use mars_red_bank_types::swapper::EstimateExactInSwapResponse; use mars_swapper_base::{ContractError, ContractResult, Route}; use osmosis_std::types::osmosis::gamm::v1beta1::MsgSwapExactAmountIn; @@ -52,8 +52,9 @@ impl Route for OsmosisRoute { let mut seen_denoms = hashset(&[denom_in]); for (i, step) in steps.iter().enumerate() { let pool = query_pool(querier, step.pool_id)?; + let pool_denoms = pool.get_pool_denoms(); - if !has_denom(prev_denom_out, &pool.pool_assets) { + if !pool_denoms.contains(&prev_denom_out.to_string()) { return Err(ContractError::InvalidRoute { reason: format!( "step {}: pool {} does not contain input denom {}", @@ -64,7 +65,7 @@ impl Route for OsmosisRoute { }); } - if !has_denom(&step.token_out_denom, &pool.pool_assets) { + if !pool_denoms.contains(&step.token_out_denom) { return Err(ContractError::InvalidRoute { reason: format!( "step {}: pool {} does not contain output denom {}", diff --git a/packages/chains/osmosis/Cargo.toml b/packages/chains/osmosis/Cargo.toml index d2af81999..7f44f9934 100755 --- a/packages/chains/osmosis/Cargo.toml +++ b/packages/chains/osmosis/Cargo.toml @@ -21,3 +21,4 @@ backtraces = ["cosmwasm-std/backtraces"] cosmwasm-std = { workspace = true } osmosis-std = { workspace = true } serde = { workspace = true } +prost = { workspace = true } diff --git a/packages/chains/osmosis/src/helpers.rs b/packages/chains/osmosis/src/helpers.rs index 1503569fd..6cb9e204d 100644 --- a/packages/chains/osmosis/src/helpers.rs +++ b/packages/chains/osmosis/src/helpers.rs @@ -3,9 +3,6 @@ use std::str::FromStr; use cosmwasm_std::{ coin, Decimal, Empty, QuerierWrapper, QueryRequest, StdError, StdResult, Uint128, }; -/// FIXME: migrate to Spot queries from PoolManager once whitelisted in https://github.com/osmosis-labs/osmosis/blob/main/wasmbinding/stargate_whitelist.go#L127 -#[allow(deprecated)] -use osmosis_std::types::osmosis::gamm::v1beta1::QueryPoolRequest as PoolRequest; use osmosis_std::{ shim::{Duration, Timestamp}, types::{ @@ -13,26 +10,67 @@ use osmosis_std::{ osmosis::{ downtimedetector::v1beta1::DowntimedetectorQuerier, gamm::{ - v1beta1::{PoolAsset, PoolParams}, - v2::GammQuerier, + poolmodels::stableswap::v1beta1::Pool as StableSwapPool, + v1beta1::Pool as BalancerPool, }, + poolmanager::v1beta1::{PoolRequest, PoolResponse, PoolmanagerQuerier}, twap::v1beta1::TwapQuerier, }, }, }; -use serde::{Deserialize, Serialize}; - -// NOTE: Use custom Pool (`id` type as String) due to problem with json (de)serialization discrepancy between go and rust side. -// https://github.com/osmosis-labs/osmosis-rust/issues/42 -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct Pool { - pub id: String, - pub address: String, - pub pool_params: Option, - pub future_pool_governor: String, - pub pool_assets: Vec, - pub total_shares: Option, - pub total_weight: String, +use prost::Message; + +// Get denoms from different type of the pool +pub trait CommonPoolData { + fn get_pool_id(&self) -> u64; + fn get_pool_denoms(&self) -> Vec; +} + +#[derive(Debug, PartialEq)] +pub enum Pool { + Balancer(BalancerPool), + StableSwap(StableSwapPool), +} + +impl CommonPoolData for Pool { + fn get_pool_id(&self) -> u64 { + match self { + Pool::Balancer(pool) => pool.id, + Pool::StableSwap(pool) => pool.id, + } + } + + fn get_pool_denoms(&self) -> Vec { + match self { + Pool::Balancer(pool) => pool + .pool_assets + .iter() + .flat_map(|asset| &asset.token) + .map(|token| token.denom.clone()) + .collect(), + Pool::StableSwap(pool) => { + pool.pool_liquidity.iter().map(|pl| pl.denom.clone()).collect() + } + } + } +} + +impl TryFrom for Pool { + type Error = StdError; + + fn try_from(value: osmosis_std::shim::Any) -> Result { + if let Ok(pool) = BalancerPool::decode(value.value.as_slice()) { + return Ok(Pool::Balancer(pool)); + } + if let Ok(pool) = StableSwapPool::decode(value.value.as_slice()) { + return Ok(Pool::StableSwap(pool)); + } + + Err(StdError::parse_err( + "Pool", + "Unsupported pool: must be either `Balancer` or `StableSwap`.", + )) + } } impl Pool { @@ -48,39 +86,24 @@ impl Pool { } } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct QueryPoolResponse { - pub pool: Pool, -} - /// Query an Osmosis pool's coin depths and the supply of of liquidity token -/// -/// FIXME: migrate to Spot queries from PoolManager once whitelisted in https://github.com/osmosis-labs/osmosis/blob/main/wasmbinding/stargate_whitelist.go#L127 -#[allow(deprecated)] pub fn query_pool(querier: &QuerierWrapper, pool_id: u64) -> StdResult { let req: QueryRequest = PoolRequest { pool_id, } .into(); - let res: QueryPoolResponse = querier.query(&req)?; - Ok(res.pool) -} - -pub fn has_denom(denom: &str, pool_assets: &[PoolAsset]) -> bool { - pool_assets.iter().flat_map(|asset| &asset.token).any(|coin| coin.denom == denom) + let res: PoolResponse = querier.query(&req)?; + res.pool.ok_or_else(|| StdError::not_found("pool"))?.try_into() // convert `Any` to `Pool` } /// Query the spot price of a coin, denominated in OSMO -/// -/// FIXME: migrate to Spot queries from PoolManager once whitelisted in https://github.com/osmosis-labs/osmosis/blob/main/wasmbinding/stargate_whitelist.go#L127 -#[allow(deprecated)] pub fn query_spot_price( querier: &QuerierWrapper, pool_id: u64, base_denom: &str, quote_denom: &str, ) -> StdResult { - let spot_price_res = GammQuerier::new(querier).spot_price( + let spot_price_res = PoolmanagerQuerier::new(querier).spot_price( pool_id, base_denom.to_string(), quote_denom.to_string(), @@ -154,12 +177,14 @@ pub fn recovered_since_downtime_of_length( #[cfg(test)] mod tests { + use osmosis_std::types::osmosis::gamm::v1beta1::PoolAsset; + use super::*; #[test] fn unwrapping_coin() { - let pool = Pool { - id: "1111".to_string(), + let pool = BalancerPool { + id: 1111, address: "".to_string(), pool_params: None, future_pool_governor: "".to_string(), @@ -191,4 +216,67 @@ mod tests { let res = Pool::unwrap_coin(&pool.pool_assets[1].token).unwrap(); assert_eq!(res, coin(430, "denom_2")); } + + #[test] + fn common_data_for_balancer_pool() { + let balancer_pool = BalancerPool { + id: 1111, + address: "".to_string(), + pool_params: None, + future_pool_governor: "".to_string(), + pool_assets: vec![ + PoolAsset { + token: Some(Coin { + denom: "denom_1".to_string(), + amount: "123".to_string(), + }), + weight: "500".to_string(), + }, + PoolAsset { + token: Some(Coin { + denom: "denom_2".to_string(), + amount: "430".to_string(), + }), + weight: "500".to_string(), + }, + ], + total_shares: None, + total_weight: "".to_string(), + }; + + let any_pool = balancer_pool.to_any(); + let pool: Pool = any_pool.try_into().unwrap(); + + assert_eq!(balancer_pool.id, pool.get_pool_id()); + assert_eq!(vec!["denom_1".to_string(), "denom_2".to_string()], pool.get_pool_denoms()) + } + + #[test] + fn common_data_for_stable_swap_pool() { + let stable_swap_pool = StableSwapPool { + address: "".to_string(), + id: 4444, + pool_params: None, + future_pool_governor: "".to_string(), + total_shares: None, + pool_liquidity: vec![ + Coin { + denom: "denom_1".to_string(), + amount: "123".to_string(), + }, + Coin { + denom: "denom_2".to_string(), + amount: "430".to_string(), + }, + ], + scaling_factors: vec![], + scaling_factor_controller: "".to_string(), + }; + + let any_pool = stable_swap_pool.to_any(); + let pool: Pool = any_pool.try_into().unwrap(); + + assert_eq!(stable_swap_pool.id, pool.get_pool_id()); + assert_eq!(vec!["denom_1".to_string(), "denom_2".to_string()], pool.get_pool_denoms()) + } } diff --git a/packages/chains/osmosis/src/lib.rs b/packages/chains/osmosis/src/lib.rs index 1630fabcd..cfa498093 100644 --- a/packages/chains/osmosis/src/lib.rs +++ b/packages/chains/osmosis/src/lib.rs @@ -1 +1,5 @@ pub mod helpers; + +pub use osmosis_std::types::osmosis::gamm::{ + poolmodels::stableswap::v1beta1::Pool as StableSwapPool, v1beta1::Pool as BalancerPool, +}; diff --git a/packages/testing/src/mars_mock_querier.rs b/packages/testing/src/mars_mock_querier.rs index ae7a96886..7b67c75d6 100644 --- a/packages/testing/src/mars_mock_querier.rs +++ b/packages/testing/src/mars_mock_querier.rs @@ -9,12 +9,11 @@ use mars_oracle_osmosis::{ stride::{Price, RedemptionRateResponse}, DowntimeDetector, }; -use mars_osmosis::helpers::QueryPoolResponse; use mars_params::types::asset::AssetParams; use mars_red_bank_types::{address_provider, incentives, oracle, red_bank}; use osmosis_std::types::osmosis::{ downtimedetector::v1beta1::RecoveredSinceDowntimeOfLengthResponse, - poolmanager::v1beta1::SpotPriceResponse, + poolmanager::v1beta1::{PoolResponse, SpotPriceResponse}, twap::v1beta1::{ArithmeticTwapToNowResponse, GeometricTwapToNowResponse}, }; use pyth_sdk_cw::{PriceFeedResponse, PriceIdentifier}; @@ -101,7 +100,7 @@ impl MarsMockQuerier { ); } - pub fn set_query_pool_response(&mut self, pool_id: u64, pool_response: QueryPoolResponse) { + pub fn set_query_pool_response(&mut self, pool_id: u64, pool_response: PoolResponse) { self.osmosis_querier.pools.insert(pool_id, pool_response); } diff --git a/packages/testing/src/osmosis_querier.rs b/packages/testing/src/osmosis_querier.rs index 539ba769a..9773e8772 100644 --- a/packages/testing/src/osmosis_querier.rs +++ b/packages/testing/src/osmosis_querier.rs @@ -1,12 +1,11 @@ use std::collections::HashMap; use cosmwasm_std::{to_binary, Binary, ContractResult, QuerierResult, SystemError}; -use mars_osmosis::helpers::QueryPoolResponse; use osmosis_std::types::osmosis::{ downtimedetector::v1beta1::{ RecoveredSinceDowntimeOfLengthRequest, RecoveredSinceDowntimeOfLengthResponse, }, - poolmanager::v1beta1::{PoolRequest, SpotPriceRequest, SpotPriceResponse}, + poolmanager::v1beta1::{PoolRequest, PoolResponse, SpotPriceRequest, SpotPriceResponse}, twap::v1beta1::{ ArithmeticTwapToNowRequest, ArithmeticTwapToNowResponse, GeometricTwapToNowRequest, GeometricTwapToNowResponse, @@ -23,7 +22,7 @@ pub struct PriceKey { #[derive(Clone, Default)] pub struct OsmosisQuerier { - pub pools: HashMap, + pub pools: HashMap, pub spot_prices: HashMap, pub arithmetic_twap_prices: HashMap, @@ -34,7 +33,7 @@ pub struct OsmosisQuerier { impl OsmosisQuerier { pub fn handle_stargate_query(&self, path: &str, data: &Binary) -> Result { - if path == "/osmosis.gamm.v1beta1.Query/Pool" { + if path == "/osmosis.poolmanager.v1beta1.Query/Pool" { let parse_osmosis_query: Result = Message::decode(data.as_slice()); if let Ok(osmosis_query) = parse_osmosis_query { @@ -42,7 +41,7 @@ impl OsmosisQuerier { } } - if path == "/osmosis.gamm.v2.Query/SpotPrice" { + if path == "/osmosis.poolmanager.v1beta1.Query/SpotPrice" { let parse_osmosis_query: Result = Message::decode(data.as_slice()); if let Ok(osmosis_query) = parse_osmosis_query { From 587551281cd70a9d20aa5653995a571b95cb306f Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sat, 29 Jul 2023 15:51:02 +0100 Subject: [PATCH 16/27] formatting --- contracts/params/src/contract.rs | 2 +- contracts/params/tests/test_deposit_cap.rs | 10 ++++------ packages/testing/src/mars_mock_querier.rs | 4 +--- packages/testing/src/red_bank_querier.rs | 4 ++-- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/contracts/params/src/contract.rs b/contracts/params/src/contract.rs index 12eaca325..ab9b04e64 100644 --- a/contracts/params/src/contract.rs +++ b/contracts/params/src/contract.rs @@ -15,7 +15,7 @@ use crate::{ query::{ query_all_asset_params, query_all_vault_configs, query_total_deposit, query_vault_config, }, - state::{ASSET_PARAMS, OWNER, TARGET_HEALTH_FACTOR, ADDRESS_PROVIDER}, + state::{ADDRESS_PROVIDER, ASSET_PARAMS, OWNER, TARGET_HEALTH_FACTOR}, }; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); diff --git a/contracts/params/tests/test_deposit_cap.rs b/contracts/params/tests/test_deposit_cap.rs index 4ee1f3854..302f6192a 100644 --- a/contracts/params/tests/test_deposit_cap.rs +++ b/contracts/params/tests/test_deposit_cap.rs @@ -45,11 +45,7 @@ const TIMESTAMP: u64 = 1690573960; Uint128::new(1751191642); "real data queried from mainnet" )] -fn querying_total_deposit( - rb_market: Market, - rb_debt: UserDebtResponse, - cm_balance: Uint128, -) { +fn querying_total_deposit(rb_market: Market, rb_debt: UserDebtResponse, cm_balance: Uint128) { let mut deps = mock_dependencies(&[]); let env = mock_env_at_block_time(TIMESTAMP); @@ -60,7 +56,9 @@ fn querying_total_deposit( ADDRESS_PROVIDER.save(deps.as_mut().storage, &Addr::unchecked("address_provider")).unwrap(); // compute the correct, expected total deposit - let rb_deposit = get_underlying_liquidity_amount(rb_market.collateral_total_scaled, &rb_market, TIMESTAMP).unwrap(); + let rb_deposit = + get_underlying_liquidity_amount(rb_market.collateral_total_scaled, &rb_market, TIMESTAMP) + .unwrap(); let exp_total_deposit = rb_deposit + cm_balance - rb_debt.amount; // query total deposit diff --git a/packages/testing/src/mars_mock_querier.rs b/packages/testing/src/mars_mock_querier.rs index 7b67c75d6..785fff316 100644 --- a/packages/testing/src/mars_mock_querier.rs +++ b/packages/testing/src/mars_mock_querier.rs @@ -194,9 +194,7 @@ impl MarsMockQuerier { user: impl Into, debt: red_bank::UserDebtResponse, ) { - self.redbank_querier - .users_denoms_debts - .insert((user.into(), debt.denom.clone()), debt); + self.redbank_querier.users_denoms_debts.insert((user.into(), debt.denom.clone()), debt); } pub fn set_redbank_user_position( diff --git a/packages/testing/src/red_bank_querier.rs b/packages/testing/src/red_bank_querier.rs index d69db38ef..6c5a1fca7 100644 --- a/packages/testing/src/red_bank_querier.rs +++ b/packages/testing/src/red_bank_querier.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use cosmwasm_std::{to_binary, Binary, ContractResult, QuerierResult}; use mars_red_bank_types::red_bank::{ - Market, QueryMsg, UserCollateralResponse, UserPositionResponse, UserDebtResponse, + Market, QueryMsg, UserCollateralResponse, UserDebtResponse, UserPositionResponse, }; #[derive(Default)] @@ -21,7 +21,7 @@ impl RedBankQuerier { } => { let maybe_market = self.markets.get(&denom); to_binary(&maybe_market).into() - }, + } QueryMsg::UserCollateral { user, account_id: _, From 82358a383ee863a5afa25a67e3f1d970c72125fa Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sat, 29 Jul 2023 15:51:18 +0100 Subject: [PATCH 17/27] update schema From 7ac03adcdc375b5851ea2987f9d33ba8060b570b Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sat, 29 Jul 2023 16:06:34 +0100 Subject: [PATCH 18/27] add total deposit to mock params querier --- packages/testing/src/mars_mock_querier.rs | 4 ++++ packages/testing/src/params_querier.rs | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/testing/src/mars_mock_querier.rs b/packages/testing/src/mars_mock_querier.rs index 785fff316..39dd8cae5 100644 --- a/packages/testing/src/mars_mock_querier.rs +++ b/packages/testing/src/mars_mock_querier.rs @@ -213,6 +213,10 @@ impl MarsMockQuerier { self.params_querier.target_health_factor = thf; } + pub fn set_total_deposit(&mut self, denom: String, amount: Uint128) { + self.params_querier.total_deposits.insert(denom, amount); + } + pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { match &request { QueryRequest::Wasm(WasmQuery::Smart { diff --git a/packages/testing/src/params_querier.rs b/packages/testing/src/params_querier.rs index 21f18e09e..f3598e2ba 100644 --- a/packages/testing/src/params_querier.rs +++ b/packages/testing/src/params_querier.rs @@ -1,12 +1,13 @@ use std::collections::HashMap; -use cosmwasm_std::{to_binary, Binary, ContractResult, Decimal, QuerierResult}; +use cosmwasm_std::{to_binary, Binary, Coin, ContractResult, Decimal, QuerierResult, Uint128}; use mars_params::{msg::QueryMsg, types::asset::AssetParams}; #[derive(Default)] pub struct ParamsQuerier { pub target_health_factor: Decimal, pub params: HashMap, + pub total_deposits: HashMap, } impl ParamsQuerier { @@ -19,6 +20,16 @@ impl ParamsQuerier { Some(params) => to_binary(¶ms).into(), None => Err(format!("[mock]: could not find the params for {denom}")).into(), }, + QueryMsg::TotalDeposit { + denom, + } => match self.total_deposits.get(&denom) { + Some(amount) => to_binary(&Coin { + denom, + amount: *amount, + }) + .into(), + None => Err(format!("[mock]: could not find total deposit for {denom}")).into(), + }, _ => Err("[mock]: Unsupported params query".to_string()).into(), }; Ok(ret).into() From e6e1ef4017fa50463e5872f9425efa827956f68f Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sat, 29 Jul 2023 16:23:53 +0100 Subject: [PATCH 19/27] update tests --- Cargo.lock | 1 + contracts/red-bank/Cargo.toml | 1 + contracts/red-bank/tests/test_deposit.rs | 84 +++++++++++++--------- contracts/red-bank/tests/test_liquidate.rs | 6 ++ packages/testing/src/mars_mock_querier.rs | 4 +- 5 files changed, 60 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b70c1c3a..f98970d92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1926,6 +1926,7 @@ dependencies = [ "mars-red-bank-types", "mars-testing", "mars-utils", + "test-case", "thiserror", ] diff --git a/contracts/red-bank/Cargo.toml b/contracts/red-bank/Cargo.toml index 98cfdd6f1..4352b9f90 100644 --- a/contracts/red-bank/Cargo.toml +++ b/contracts/red-bank/Cargo.toml @@ -40,3 +40,4 @@ anyhow = { workspace = true } cosmwasm-schema = { workspace = true } cw-multi-test = { workspace = true } mars-testing = { workspace = true } +test-case = { workspace = true } diff --git a/contracts/red-bank/tests/test_deposit.rs b/contracts/red-bank/tests/test_deposit.rs index 29dbebb97..a9e3dce13 100644 --- a/contracts/red-bank/tests/test_deposit.rs +++ b/contracts/red-bank/tests/test_deposit.rs @@ -3,13 +3,15 @@ use std::any::type_name; use cosmwasm_std::{ attr, coin, coins, testing::{mock_env, mock_info, MockApi, MockStorage}, - to_binary, Addr, Decimal, OwnedDeps, StdError, StdResult, SubMsg, Uint128, WasmMsg, + to_binary, Addr, Decimal, OwnedDeps, StdError, SubMsg, Uint128, WasmMsg, }; use cw_utils::PaymentError; use helpers::{ set_collateral, th_build_interests_updated_event, th_get_expected_indices_and_rates, th_setup, }; -use mars_interest_rate::{compute_scaled_amount, ScalingOperation, SCALING_FACTOR}; +use mars_interest_rate::{ + compute_scaled_amount, get_underlying_liquidity_amount, ScalingOperation, SCALING_FACTOR, +}; use mars_params::types::asset::{AssetParams, CmSettings, LiquidationBonus, RedBankSettings}; use mars_red_bank::{ contract::execute, @@ -22,6 +24,7 @@ use mars_red_bank_types::{ red_bank::{Collateral, ExecuteMsg, Market}, }; use mars_testing::{mock_env_at_block_time, MarsMockQuerier}; +use test_case::test_case; use crate::helpers::th_default_asset_params; @@ -80,6 +83,16 @@ fn setup_test() -> TestSuite { }, ); + deps.querier.set_total_deposit( + denom, + get_underlying_liquidity_amount( + market.collateral_total_scaled, + &market, + market.indexes_last_updated, + ) + .unwrap(), + ); + TestSuite { deps, denom, @@ -196,23 +209,33 @@ fn depositing_to_disabled_market() { ); } -#[test] -fn depositing_above_cap() { +// note: the initial deposit amount set in the TestSuite is 11_000_000 uosmo +#[test_case( + 1_000_001, + 12_000_000, + false; + "deposit cap exceeded, should fail" +)] +#[test_case( + 999_999, + 12_000_000, + true; + "deposit cap not exceeded, should work" +)] +fn depositing_above_cap( + amount_to_deposit: u128, + deposit_cap: u128, + exp_ok: bool, +) { let TestSuite { mut deps, denom, depositor_addr, + initial_market, .. } = setup_test(); - // set a deposit cap - MARKETS - .update(deps.as_mut().storage, denom, |opt| -> StdResult<_> { - let mut market = opt.unwrap(); - market.collateral_total_scaled = Uint128::new(9_000_000) * SCALING_FACTOR; - Ok(market) - }) - .unwrap(); + // set deposit cap deps.querier.set_redbank_params( denom, AssetParams { @@ -223,39 +246,32 @@ fn depositing_above_cap() { red_bank: RedBankSettings { deposit_enabled: true, borrow_enabled: true, - deposit_cap: Uint128::new(10_000_000), + deposit_cap: Uint128::new(deposit_cap), }, ..th_default_asset_params() }, ); - // try deposit with a big amount, should fail - let err = execute( + // try deposit with the given amount + let res = execute( deps.as_mut(), - mock_env_at_block_time(10000100), - mock_info(depositor_addr.as_str(), &coins(1_000_001, denom)), + mock_env_at_block_time(initial_market.indexes_last_updated), + mock_info(depositor_addr.as_str(), &coins(amount_to_deposit, denom)), ExecuteMsg::Deposit { account_id: None, }, - ) - .unwrap_err(); - assert_eq!( - err, - ContractError::DepositCapExceeded { - denom: denom.to_string() - } ); - // deposit a smaller amount, should work - let result = execute( - deps.as_mut(), - mock_env_at_block_time(10000100), - mock_info(depositor_addr.as_str(), &coins(123, denom)), - ExecuteMsg::Deposit { - account_id: None, - }, - ); - assert!(result.is_ok()); + if exp_ok { + assert!(res.is_ok()); + } else { + assert_eq!( + res, + Err(ContractError::DepositCapExceeded { + denom: denom.to_string(), + }), + ); + } } #[test] diff --git a/contracts/red-bank/tests/test_liquidate.rs b/contracts/red-bank/tests/test_liquidate.rs index ed9efcdf6..227f51afc 100644 --- a/contracts/red-bank/tests/test_liquidate.rs +++ b/contracts/red-bank/tests/test_liquidate.rs @@ -796,6 +796,12 @@ fn response_verification() { deps.querier.set_oracle_price("uusdc", Decimal::from_ratio(68u128, 10u128)); deps.querier.set_oracle_price("untrn", Decimal::from_ratio(55u128, 10u128)); + // no deposit yet, initialize total deposit to zero + deps.querier.set_total_deposit("uosmo", Uint128::zero()); + deps.querier.set_total_deposit("uatom", Uint128::zero()); + deps.querier.set_total_deposit("uusdc", Uint128::zero()); + deps.querier.set_total_deposit("untrn", Uint128::zero()); + // provider deposits collaterals execute( deps.as_mut(), diff --git a/packages/testing/src/mars_mock_querier.rs b/packages/testing/src/mars_mock_querier.rs index 39dd8cae5..c0255c7bb 100644 --- a/packages/testing/src/mars_mock_querier.rs +++ b/packages/testing/src/mars_mock_querier.rs @@ -213,8 +213,8 @@ impl MarsMockQuerier { self.params_querier.target_health_factor = thf; } - pub fn set_total_deposit(&mut self, denom: String, amount: Uint128) { - self.params_querier.total_deposits.insert(denom, amount); + pub fn set_total_deposit(&mut self, denom: impl Into, amount: impl Into) { + self.params_querier.total_deposits.insert(denom.into(), amount.into()); } pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { From ae6e292912cd0fc1fbdebe3b1fe9cc239216c49e Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sat, 29 Jul 2023 16:26:40 +0100 Subject: [PATCH 20/27] formatting --- contracts/red-bank/tests/test_deposit.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/contracts/red-bank/tests/test_deposit.rs b/contracts/red-bank/tests/test_deposit.rs index a9e3dce13..da01bf2a1 100644 --- a/contracts/red-bank/tests/test_deposit.rs +++ b/contracts/red-bank/tests/test_deposit.rs @@ -222,11 +222,7 @@ fn depositing_to_disabled_market() { true; "deposit cap not exceeded, should work" )] -fn depositing_above_cap( - amount_to_deposit: u128, - deposit_cap: u128, - exp_ok: bool, -) { +fn depositing_above_cap(amount_to_deposit: u128, deposit_cap: u128, exp_ok: bool) { let TestSuite { mut deps, denom, From 129a5c0c547639b3d758c92b7e8e8ad2cf8b875d Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sat, 29 Jul 2023 16:41:47 +0100 Subject: [PATCH 21/27] update typescript types From df48e3ace7b5e93fc0adac9a50473b9880828b69 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sun, 30 Jul 2023 20:41:37 +0100 Subject: [PATCH 22/27] use `cosmwasm_std::Coins` over `HashMap` --- contracts/incentives/src/contract.rs | 59 ++++++++----------- .../incentives/tests/test_claim_rewards.rs | 2 +- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/contracts/incentives/src/contract.rs b/contracts/incentives/src/contract.rs index a877040f5..37b97456a 100644 --- a/contracts/incentives/src/contract.rs +++ b/contracts/incentives/src/contract.rs @@ -1,10 +1,8 @@ -use std::collections::HashMap; - #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - attr, coins, to_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, Deps, DepsMut, Env, - Event, MessageInfo, Order, Response, StdError, StdResult, Uint128, + attr, to_binary, Addr, BankMsg, Binary, Coin, Coins, Decimal, Deps, DepsMut, Env, Event, + MessageInfo, Order, Response, StdError, StdResult, Uint128, }; use cw_storage_plus::Bound; use mars_owner::{OwnerInit::SetInitialOwner, OwnerUpdate}; @@ -447,7 +445,7 @@ pub fn execute_claim_rewards( } else { base_event }; - let mut events = vec![base_event]; + response = response.add_event(base_event); let asset_incentives = state::paginate_incentive_states( deps.storage, @@ -456,7 +454,7 @@ pub fn execute_claim_rewards( limit, )?; - let mut total_unclaimed_rewards: HashMap = HashMap::new(); + let mut total_unclaimed_rewards = Coins::default(); for ((collateral_denom, incentive_denom), _) in asset_incentives { let querier = deps.querier; @@ -478,25 +476,25 @@ pub fn execute_claim_rewards( &Uint128::zero(), )?; - total_unclaimed_rewards - .entry(incentive_denom) - .and_modify(|amount| *amount += unclaimed_rewards) - .or_insert(unclaimed_rewards); + total_unclaimed_rewards.add(Coin { + denom: incentive_denom, + amount: unclaimed_rewards, + })?; } - for (denom, amount) in total_unclaimed_rewards.iter() { - response = response.add_message(CosmosMsg::Bank(BankMsg::Send { - to_address: user_addr.to_string(), - amount: coins(amount.u128(), denom), - })); - events.push( - Event::new("mars/incentives/claim_rewards/claimed_reward") - .add_attribute("denom", denom) - .add_attribute("amount", *amount), - ); + if !total_unclaimed_rewards.is_empty() { + response = response + .add_event( + Event::new("mars/incentives/claim_rewards/claimed_rewards") + .add_attribute("coins", total_unclaimed_rewards.to_string()), + ) + .add_message(BankMsg::Send { + to_address: user_addr.into(), + amount: total_unclaimed_rewards.into(), + }); } - Ok(response.add_events(events)) + Ok(response) } pub fn execute_update_config( @@ -675,7 +673,7 @@ pub fn query_user_unclaimed_rewards( limit, )?; - let mut total_unclaimed_rewards: HashMap = HashMap::new(); + let mut total_unclaimed_rewards = Coins::default(); for ((collateral_denom, incentive_denom), _) in incentive_states { let unclaimed_rewards = compute_user_unclaimed_rewards( @@ -688,19 +686,14 @@ pub fn query_user_unclaimed_rewards( &collateral_denom, &incentive_denom, )?; - total_unclaimed_rewards - .entry(incentive_denom) - .and_modify(|amount| *amount += unclaimed_rewards) - .or_insert(unclaimed_rewards); + + total_unclaimed_rewards.add(Coin { + denom: incentive_denom, + amount: unclaimed_rewards, + })?; } - Ok(total_unclaimed_rewards - .into_iter() - .map(|(denom, amount)| Coin { - denom, - amount, - }) - .collect()) + Ok(total_unclaimed_rewards.into()) } fn query_red_bank_address(deps: Deps) -> StdResult { diff --git a/contracts/incentives/tests/test_claim_rewards.rs b/contracts/incentives/tests/test_claim_rewards.rs index 8320d81ec..333b167a1 100644 --- a/contracts/incentives/tests/test_claim_rewards.rs +++ b/contracts/incentives/tests/test_claim_rewards.rs @@ -254,7 +254,7 @@ fn execute_claim_rewards() { ); assert_eq!( res.events[1].attributes, - vec![attr("denom", "umars"), attr("amount", expected_accrued_rewards),] + vec![attr("coins", format!("{expected_accrued_rewards}umars"))] ); // asset and zero incentives get updated, no_user does not let asset_incentive = From 66370f83e5a33967a6f14afc6c15178af83a6802 Mon Sep 17 00:00:00 2001 From: Piotr Babel Date: Mon, 31 Jul 2023 15:31:20 +0200 Subject: [PATCH 23/27] Fix incentives test. Enable test job. --- integration-tests/tests/test_incentives.rs | 52 +++++++++++----------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/integration-tests/tests/test_incentives.rs b/integration-tests/tests/test_incentives.rs index 793b364db..efa0cc46b 100644 --- a/integration-tests/tests/test_incentives.rs +++ b/integration-tests/tests/test_incentives.rs @@ -46,8 +46,8 @@ fn rewards_claim() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uusdc"); assert_eq!(user_collateral.amount.u128(), funded_amt); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + assert!(rewards_balance.is_empty()); mock_env.increment_by_time(86400); // 24 hours @@ -61,8 +61,8 @@ fn rewards_claim() { let mars_balance = mock_env.query_balance(&incentives.contract_addr, "umars").unwrap(); assert_eq!(mars_balance.amount, Uint128::from(ONE_WEEK_IN_SEC * 10 - 864000)); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + assert!(rewards_balance.is_empty()); } // Credit accounts can deposit / withdraw from Red Bank and accure rewards in incentives contract. @@ -287,8 +287,8 @@ fn emissions_rates() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uusdc"); assert_eq!(user_collateral.amount.u128(), funded_amt); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + assert!(rewards_balance.is_empty()); mock_env.increment_by_time(86400); // 24 hours @@ -300,8 +300,8 @@ fn emissions_rates() { let balance = mock_env.query_balance(&user, "umars").unwrap(); assert_eq!(balance.amount, Uint128::new(432000)); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + assert!(rewards_balance.is_empty()); incentives.init_asset_incentive_from_current_block( &mut mock_env, @@ -319,8 +319,8 @@ fn emissions_rates() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uosmo"); assert_eq!(user_collateral.amount.u128(), funded_amt); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + assert!(rewards_balance.is_empty()); mock_env.increment_by_time(86400); // 24 hours @@ -332,8 +332,8 @@ fn emissions_rates() { let balance = mock_env.query_balance(&user, "umars").unwrap(); assert_eq!(balance.amount, Uint128::new(1728000)); // 1296000 + 432000 - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + assert!(rewards_balance.is_empty()); } // User A deposits usdc in the redbank and claimed rewards after one day @@ -381,8 +381,8 @@ fn no_incentives_accrued_after_withdraw() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uusdc"); assert_eq!(user_collateral.amount.u128(), funded_amt); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + assert!(rewards_balance.is_empty()); mock_env.increment_by_time(86400); // 24 hours @@ -394,8 +394,8 @@ fn no_incentives_accrued_after_withdraw() { let balance = mock_env.query_balance(&user, "umars").unwrap(); assert_eq!(balance.amount, Uint128::new(432000)); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + assert!(rewards_balance.is_empty()); red_bank.withdraw(&mut mock_env, &user, "uusdc", None).unwrap(); let balance = mock_env.query_balance(&user, "uusdc").unwrap(); @@ -405,13 +405,13 @@ fn no_incentives_accrued_after_withdraw() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uosmo"); assert_eq!(user_collateral.amount, Uint128::zero()); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + assert!(rewards_balance.is_empty()); mock_env.increment_by_time(86400); // 24 hours - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + assert!(rewards_balance.is_empty()); } // User A deposits usdc, osmo, and atom all with different emissions per second & claims rewards after one day @@ -490,8 +490,8 @@ fn multiple_assets() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user, "uosmo"); assert_eq!(user_collateral.amount.u128(), funded_amt); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user); + assert!(rewards_balance.is_empty()); mock_env.increment_by_time(86400); // 24 hours @@ -552,11 +552,11 @@ fn multiple_users() { let user_collateral = red_bank.query_user_collateral(&mut mock_env, &user_b, "uusdc"); assert_eq!(user_collateral.amount.u128(), funded_amt_two); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_a).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_a); + assert!(rewards_balance.is_empty()); - let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_b).unwrap(); - assert_eq!(rewards_balance[0].amount, Uint128::zero()); + let rewards_balance = incentives.query_unclaimed_rewards(&mut mock_env, &user_b); + assert!(rewards_balance.is_empty()); mock_env.increment_by_time(86400); // 24 hours From bf5556ff768fb8385a30b36a9a41641f4ce14412 Mon Sep 17 00:00:00 2001 From: Piotr Babel Date: Mon, 31 Jul 2023 17:42:34 +0200 Subject: [PATCH 24/27] Delete execute.rs file. --- contracts/red-bank/src/execute.rs | 1019 ----------------------------- 1 file changed, 1019 deletions(-) delete mode 100644 contracts/red-bank/src/execute.rs diff --git a/contracts/red-bank/src/execute.rs b/contracts/red-bank/src/execute.rs deleted file mode 100644 index c87806682..000000000 --- a/contracts/red-bank/src/execute.rs +++ /dev/null @@ -1,1019 +0,0 @@ -use std::{cmp::min, str}; - -use cosmwasm_std::{ - Addr, Decimal, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Uint128, -}; -use mars_interest_rate::{ - get_scaled_debt_amount, get_scaled_liquidity_amount, get_underlying_debt_amount, - get_underlying_liquidity_amount, -}; -use mars_owner::{OwnerInit::SetInitialOwner, OwnerUpdate}; -use mars_params::types::AssetParams; -use mars_red_bank_types::{ - address_provider::{self, MarsAddressType}, - error::MarsError, - red_bank::{ - Config, CreateOrUpdateConfig, Debt, InitOrUpdateAssetParams, InstantiateMsg, Market, - }, -}; -use mars_utils::{ - helpers::{build_send_asset_msg, option_string_to_addr, validate_native_denom, zero_address}, - math, -}; - -use crate::{ - error::ContractError, - health::{ - assert_below_liq_threshold_after_withdraw, assert_below_max_ltv_after_borrow, - assert_liquidatable, - }, - helpers::{query_asset_params, query_close_factor}, - interest_rates::{apply_accumulated_interests, update_interest_rates}, - state::{COLLATERALS, CONFIG, DEBTS, MARKETS, OWNER, UNCOLLATERALIZED_LOAN_LIMITS}, - user::User, -}; - -pub const CONTRACT_NAME: &str = "crates.io:mars-red-bank"; -pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); - -pub fn instantiate(deps: DepsMut, msg: InstantiateMsg) -> Result { - cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - // Destructuring a struct’s fields into separate variables in order to force - // compile error if we add more params - let CreateOrUpdateConfig { - address_provider, - } = msg.config; - - if address_provider.is_none() { - return Err(MarsError::InstantiateParamsUnavailable {}.into()); - }; - - let config = Config { - address_provider: option_string_to_addr(deps.api, address_provider, zero_address())?, - }; - - CONFIG.save(deps.storage, &config)?; - - OWNER.initialize( - deps.storage, - deps.api, - SetInitialOwner { - owner: msg.owner, - }, - )?; - - Ok(Response::default()) -} - -pub fn update_owner( - deps: DepsMut, - info: MessageInfo, - update: OwnerUpdate, -) -> Result { - Ok(OWNER.update(deps, info, update)?) -} - -/// Update config -pub fn update_config( - deps: DepsMut, - info: MessageInfo, - new_config: CreateOrUpdateConfig, -) -> Result { - OWNER.assert_owner(deps.storage, &info.sender)?; - - let mut config = CONFIG.load(deps.storage)?; - - // Destructuring a struct’s fields into separate variables in order to force - // compile error if we add more params - let CreateOrUpdateConfig { - address_provider, - } = new_config; - - // Update config - config.address_provider = - option_string_to_addr(deps.api, address_provider, config.address_provider)?; - - CONFIG.save(deps.storage, &config)?; - - Ok(Response::new().add_attribute("action", "update_config")) -} - -/// Initialize asset if not exist. -/// Initialization requires that all params are provided and there is no asset in state. -pub fn init_asset( - deps: DepsMut, - env: Env, - info: MessageInfo, - denom: String, - params: InitOrUpdateAssetParams, -) -> Result { - OWNER.assert_owner(deps.storage, &info.sender)?; - - validate_native_denom(&denom)?; - - if MARKETS.may_load(deps.storage, &denom)?.is_some() { - return Err(ContractError::AssetAlreadyInitialized {}); - } - - let new_market = create_market(env.block.time.seconds(), &denom, params)?; - MARKETS.save(deps.storage, &denom, &new_market)?; - - Ok(Response::new().add_attribute("action", "init_asset").add_attribute("denom", denom)) -} - -/// Initialize new market -pub fn create_market( - block_time: u64, - denom: &str, - params: InitOrUpdateAssetParams, -) -> Result { - // Destructuring a struct’s fields into separate variables in order to force - // compile error if we add more params - let InitOrUpdateAssetParams { - reserve_factor, - interest_rate_model, - } = params; - - // All fields should be available - let available = reserve_factor.is_some() && interest_rate_model.is_some(); - - if !available { - return Err(MarsError::InstantiateParamsUnavailable {}.into()); - } - - let new_market = Market { - denom: denom.to_string(), - borrow_index: Decimal::one(), - liquidity_index: Decimal::one(), - borrow_rate: Decimal::zero(), - liquidity_rate: Decimal::zero(), - reserve_factor: reserve_factor.unwrap(), - indexes_last_updated: block_time, - collateral_total_scaled: Uint128::zero(), - debt_total_scaled: Uint128::zero(), - interest_rate_model: interest_rate_model.unwrap(), - }; - - new_market.validate()?; - - Ok(new_market) -} - -/// Update asset with new params. -pub fn update_asset( - deps: DepsMut, - env: Env, - info: MessageInfo, - denom: String, - params: InitOrUpdateAssetParams, -) -> Result { - OWNER.assert_owner(deps.storage, &info.sender)?; - - let market_option = MARKETS.may_load(deps.storage, &denom)?; - match market_option { - None => Err(ContractError::AssetNotInitialized {}), - Some(mut market) => { - // Destructuring a struct’s fields into separate variables in order to force - // compile error if we add more params - let InitOrUpdateAssetParams { - reserve_factor, - interest_rate_model, - } = params; - - // If reserve factor or interest rates are updated we update indexes with - // current values before applying the change to prevent applying this - // new params to a period where they were not valid yet. Interests rates are - // recalculated after changes are applied. - let should_update_interest_rates = (reserve_factor.is_some() - && reserve_factor.unwrap() != market.reserve_factor) - || interest_rate_model.is_some(); - - let mut response = Response::new(); - - if should_update_interest_rates { - let config = CONFIG.load(deps.storage)?; - let addresses = address_provider::helpers::query_contract_addrs( - deps.as_ref(), - &config.address_provider, - vec![MarsAddressType::Incentives, MarsAddressType::RewardsCollector], - )?; - let rewards_collector_addr = &addresses[&MarsAddressType::RewardsCollector]; - let incentives_addr = &addresses[&MarsAddressType::Incentives]; - - response = apply_accumulated_interests( - deps.storage, - &env, - &mut market, - rewards_collector_addr, - incentives_addr, - response, - )?; - } - - let mut updated_market = Market { - reserve_factor: reserve_factor.unwrap_or(market.reserve_factor), - interest_rate_model: interest_rate_model.unwrap_or(market.interest_rate_model), - ..market - }; - - updated_market.validate()?; - - if should_update_interest_rates { - response = update_interest_rates(&env, &mut updated_market, response)?; - } - MARKETS.save(deps.storage, &denom, &updated_market)?; - - Ok(response.add_attribute("action", "update_asset").add_attribute("denom", denom)) - } - } -} - -/// Update uncollateralized loan limit by a given amount in base asset -pub fn update_uncollateralized_loan_limit( - deps: DepsMut, - info: MessageInfo, - user_addr: Addr, - denom: String, - new_limit: Uint128, -) -> Result { - OWNER.assert_owner(deps.storage, &info.sender)?; - - // Check that the user has no collateralized debt - let current_limit = UNCOLLATERALIZED_LOAN_LIMITS - .may_load(deps.storage, (&user_addr, &denom))? - .unwrap_or_else(Uint128::zero); - let current_debt = DEBTS - .may_load(deps.storage, (&user_addr, &denom))? - .map(|debt| debt.amount_scaled) - .unwrap_or_else(Uint128::zero); - if current_limit.is_zero() && !current_debt.is_zero() { - return Err(ContractError::UserHasCollateralizedDebt {}); - } - if !current_limit.is_zero() && new_limit.is_zero() && !current_debt.is_zero() { - return Err(ContractError::UserHasUncollateralizedDebt {}); - } - - UNCOLLATERALIZED_LOAN_LIMITS.save(deps.storage, (&user_addr, &denom), &new_limit)?; - - DEBTS.update(deps.storage, (&user_addr, &denom), |debt_opt: Option| -> StdResult<_> { - let mut debt = debt_opt.unwrap_or(Debt { - amount_scaled: Uint128::zero(), - uncollateralized: false, - }); - // if limit == 0 then uncollateralized = false, otherwise uncollateralized = true - debt.uncollateralized = !new_limit.is_zero(); - Ok(debt) - })?; - - Ok(Response::new() - .add_attribute("action", "update_uncollateralized_loan_limit") - .add_attribute("user", user_addr) - .add_attribute("denom", denom) - .add_attribute("new_allowance", new_limit)) -} - -/// Execute deposits -pub fn deposit( - deps: DepsMut, - env: Env, - info: MessageInfo, - denom: String, - deposit_amount: Uint128, -) -> Result { - let mut market = MARKETS.load(deps.storage, &denom)?; - - let config = CONFIG.load(deps.storage)?; - - let addresses = address_provider::helpers::query_contract_addrs( - deps.as_ref(), - &config.address_provider, - vec![ - MarsAddressType::Incentives, - MarsAddressType::RewardsCollector, - MarsAddressType::Params, - ], - )?; - let rewards_collector_addr = &addresses[&MarsAddressType::RewardsCollector]; - let incentives_addr = &addresses[&MarsAddressType::Incentives]; - let params_addr = &addresses[&MarsAddressType::Params]; - - let asset_params = query_asset_params(&deps.querier, params_addr, &denom)?; - - if !asset_params.red_bank.deposit_enabled { - return Err(ContractError::DepositNotEnabled { - denom, - }); - } - - let total_scaled_deposits = market.collateral_total_scaled; - let total_deposits = - get_underlying_liquidity_amount(total_scaled_deposits, &market, env.block.time.seconds())?; - if total_deposits.checked_add(deposit_amount)? > asset_params.red_bank.deposit_cap { - return Err(ContractError::DepositCapExceeded { - denom, - }); - } - - let mut response = Response::new(); - - // update indexes and interest rates - response = apply_accumulated_interests( - deps.storage, - &env, - &mut market, - rewards_collector_addr, - incentives_addr, - response, - )?; - - if market.liquidity_index.is_zero() { - return Err(ContractError::InvalidLiquidityIndex {}); - } - let deposit_amount_scaled = - get_scaled_liquidity_amount(deposit_amount, &market, env.block.time.seconds())?; - - response = User(&info.sender).increase_collateral( - deps.storage, - &market, - deposit_amount_scaled, - incentives_addr, - response, - )?; - - market.increase_collateral(deposit_amount_scaled)?; - - response = update_interest_rates(&env, &mut market, response)?; - - MARKETS.save(deps.storage, &denom, &market)?; - - Ok(response - .add_attribute("action", "deposit") - .add_attribute("sender", &info.sender) - .add_attribute("denom", denom) - .add_attribute("amount", deposit_amount) - .add_attribute("amount_scaled", deposit_amount_scaled)) -} - -/// Burns sent maAsset in exchange of underlying asset -pub fn withdraw( - deps: DepsMut, - env: Env, - info: MessageInfo, - denom: String, - amount: Option, - recipient: Option, -) -> Result { - let withdrawer = User(&info.sender); - - let mut market = MARKETS.load(deps.storage, &denom)?; - - let collateral = withdrawer.collateral(deps.storage, &denom)?; - let withdrawer_balance_scaled_before = collateral.amount_scaled; - - if withdrawer_balance_scaled_before.is_zero() { - return Err(ContractError::UserNoCollateralBalance { - user: withdrawer.into(), - denom, - }); - } - - let withdrawer_balance_before = get_underlying_liquidity_amount( - withdrawer_balance_scaled_before, - &market, - env.block.time.seconds(), - )?; - - let withdraw_amount = match amount { - // Check user has sufficient balance to send back - Some(amount) if amount.is_zero() || amount > withdrawer_balance_before => { - return Err(ContractError::InvalidWithdrawAmount { - denom, - }); - } - Some(amount) => amount, - // If no amount is specified, the full balance is withdrawn - None => withdrawer_balance_before, - }; - - let config = CONFIG.load(deps.storage)?; - - let addresses = address_provider::helpers::query_contract_addrs( - deps.as_ref(), - &config.address_provider, - vec![ - MarsAddressType::Oracle, - MarsAddressType::Incentives, - MarsAddressType::RewardsCollector, - MarsAddressType::Params, - ], - )?; - let rewards_collector_addr = &addresses[&MarsAddressType::RewardsCollector]; - let incentives_addr = &addresses[&MarsAddressType::Incentives]; - let oracle_addr = &addresses[&MarsAddressType::Oracle]; - let params_addr = &addresses[&MarsAddressType::Params]; - - // if asset is used as collateral and user is borrowing we need to validate health factor after withdraw, - // otherwise no reasons to block the withdraw - if collateral.enabled - && withdrawer.is_borrowing(deps.storage) - && !assert_below_liq_threshold_after_withdraw( - &deps.as_ref(), - &env, - withdrawer.address(), - oracle_addr, - params_addr, - &denom, - withdraw_amount, - )? - { - return Err(ContractError::InvalidHealthFactorAfterWithdraw {}); - } - - let mut response = Response::new(); - - // update indexes and interest rates - response = apply_accumulated_interests( - deps.storage, - &env, - &mut market, - rewards_collector_addr, - incentives_addr, - response, - )?; - - // reduce the withdrawer's scaled collateral amount - let withdrawer_balance_after = withdrawer_balance_before.checked_sub(withdraw_amount)?; - let withdrawer_balance_scaled_after = - get_scaled_liquidity_amount(withdrawer_balance_after, &market, env.block.time.seconds())?; - - let withdraw_amount_scaled = - withdrawer_balance_scaled_before.checked_sub(withdrawer_balance_scaled_after)?; - - response = withdrawer.decrease_collateral( - deps.storage, - &market, - withdraw_amount_scaled, - incentives_addr, - response, - )?; - - market.decrease_collateral(withdraw_amount_scaled)?; - - response = update_interest_rates(&env, &mut market, response)?; - - MARKETS.save(deps.storage, &denom, &market)?; - - // send underlying asset to user or another recipient - let recipient_addr = if let Some(recipient) = recipient { - deps.api.addr_validate(&recipient)? - } else { - withdrawer.address().clone() - }; - - Ok(response - .add_message(build_send_asset_msg(&recipient_addr, &denom, withdraw_amount)) - .add_attribute("action", "withdraw") - .add_attribute("sender", withdrawer) - .add_attribute("recipient", recipient_addr) - .add_attribute("denom", denom) - .add_attribute("amount", withdraw_amount) - .add_attribute("amount_scaled", withdraw_amount_scaled)) -} - -/// Add debt for the borrower and send the borrowed funds -pub fn borrow( - deps: DepsMut, - env: Env, - info: MessageInfo, - denom: String, - borrow_amount: Uint128, - recipient: Option, -) -> Result { - let borrower = User(&info.sender); - - let config = CONFIG.load(deps.storage)?; - - let addresses = address_provider::helpers::query_contract_addrs( - deps.as_ref(), - &config.address_provider, - vec![ - MarsAddressType::Oracle, - MarsAddressType::Incentives, - MarsAddressType::RewardsCollector, - MarsAddressType::Params, - ], - )?; - let rewards_collector_addr = &addresses[&MarsAddressType::RewardsCollector]; - let incentives_addr = &addresses[&MarsAddressType::Incentives]; - let oracle_addr = &addresses[&MarsAddressType::Oracle]; - let params_addr = &addresses[&MarsAddressType::Params]; - - let asset_params = query_asset_params(&deps.querier, params_addr, &denom)?; - - if !asset_params.red_bank.borrow_enabled { - return Err(ContractError::BorrowNotEnabled { - denom, - }); - } - - // Load market and user state - let mut borrow_market = MARKETS.load(deps.storage, &denom)?; - - let collateral_balance_before = get_underlying_liquidity_amount( - borrow_market.collateral_total_scaled, - &borrow_market, - env.block.time.seconds(), - )?; - - // Cannot borrow zero amount or more than available collateral - if borrow_amount.is_zero() || borrow_amount > collateral_balance_before { - return Err(ContractError::InvalidBorrowAmount { - denom, - }); - } - - let uncollateralized_loan_limit = borrower.uncollateralized_loan_limit(deps.storage, &denom)?; - - // Check if user can borrow specified amount - let mut uncollateralized_debt = false; - if uncollateralized_loan_limit.is_zero() { - if !assert_below_max_ltv_after_borrow( - &deps.as_ref(), - &env, - borrower.address(), - oracle_addr, - params_addr, - &denom, - borrow_amount, - )? { - return Err(ContractError::BorrowAmountExceedsGivenCollateral {}); - } - } else { - // Uncollateralized loan: check borrow amount plus debt does not exceed uncollateralized loan limit - uncollateralized_debt = true; - - let debt_amount_scaled = borrower.debt_amount_scaled(deps.storage, &denom)?; - - let asset_market = MARKETS.load(deps.storage, &denom)?; - let debt_amount = get_underlying_debt_amount( - debt_amount_scaled, - &asset_market, - env.block.time.seconds(), - )?; - - let debt_after_borrow = debt_amount.checked_add(borrow_amount)?; - if debt_after_borrow > uncollateralized_loan_limit { - return Err(ContractError::BorrowAmountExceedsUncollateralizedLoanLimit {}); - } - } - - let mut response = Response::new(); - - response = apply_accumulated_interests( - deps.storage, - &env, - &mut borrow_market, - rewards_collector_addr, - incentives_addr, - response, - )?; - - // Set new debt - let borrow_amount_scaled = - get_scaled_debt_amount(borrow_amount, &borrow_market, env.block.time.seconds())?; - - borrow_market.increase_debt(borrow_amount_scaled)?; - borrower.increase_debt(deps.storage, &denom, borrow_amount_scaled, uncollateralized_debt)?; - - response = update_interest_rates(&env, &mut borrow_market, response)?; - MARKETS.save(deps.storage, &denom, &borrow_market)?; - - // Send borrow amount to borrower or another recipient - let recipient_addr = if let Some(recipient) = recipient { - deps.api.addr_validate(&recipient)? - } else { - borrower.address().clone() - }; - - Ok(response - .add_message(build_send_asset_msg(&recipient_addr, &denom, borrow_amount)) - .add_attribute("action", "borrow") - .add_attribute("sender", borrower) - .add_attribute("recipient", recipient_addr) - .add_attribute("denom", denom) - .add_attribute("amount", borrow_amount) - .add_attribute("amount_scaled", borrow_amount_scaled)) -} - -/// Handle the repay of native tokens. Refund extra funds if they exist -pub fn repay( - deps: DepsMut, - env: Env, - info: MessageInfo, - on_behalf_of: Option, - denom: String, - repay_amount: Uint128, -) -> Result { - let user_addr: Addr; - let user = if let Some(address) = on_behalf_of { - user_addr = deps.api.addr_validate(&address)?; - let user = User(&user_addr); - // Uncollateralized loans should not have 'on behalf of' because it creates accounting complexity for them - if !user.uncollateralized_loan_limit(deps.storage, &denom)?.is_zero() { - return Err(ContractError::CannotRepayUncollateralizedLoanOnBehalfOf {}); - } - user - } else { - User(&info.sender) - }; - - // Check new debt - let debt = DEBTS - .may_load(deps.storage, (user.address(), &denom))? - .ok_or(ContractError::CannotRepayZeroDebt {})?; - - let config = CONFIG.load(deps.storage)?; - - let addresses = address_provider::helpers::query_contract_addrs( - deps.as_ref(), - &config.address_provider, - vec![MarsAddressType::Incentives, MarsAddressType::RewardsCollector], - )?; - let rewards_collector_addr = &addresses[&MarsAddressType::RewardsCollector]; - let incentives_addr = &addresses[&MarsAddressType::Incentives]; - - let mut market = MARKETS.load(deps.storage, &denom)?; - - let mut response = Response::new(); - - response = apply_accumulated_interests( - deps.storage, - &env, - &mut market, - rewards_collector_addr, - incentives_addr, - response, - )?; - - let debt_amount_scaled_before = debt.amount_scaled; - let debt_amount_before = - get_underlying_debt_amount(debt.amount_scaled, &market, env.block.time.seconds())?; - - // If repay amount exceeds debt, refund any excess amounts - let mut refund_amount = Uint128::zero(); - let mut debt_amount_after = Uint128::zero(); - if repay_amount > debt_amount_before { - refund_amount = repay_amount - debt_amount_before; - let refund_msg = build_send_asset_msg(&info.sender, &denom, refund_amount); - response = response.add_message(refund_msg); - } else { - debt_amount_after = debt_amount_before - repay_amount; - } - - let debt_amount_scaled_after = - get_scaled_debt_amount(debt_amount_after, &market, env.block.time.seconds())?; - - let debt_amount_scaled_delta = - debt_amount_scaled_before.checked_sub(debt_amount_scaled_after)?; - - market.decrease_debt(debt_amount_scaled_delta)?; - user.decrease_debt(deps.storage, &denom, debt_amount_scaled_delta)?; - - response = update_interest_rates(&env, &mut market, response)?; - MARKETS.save(deps.storage, &denom, &market)?; - - Ok(response - .add_attribute("action", "repay") - .add_attribute("sender", &info.sender) - .add_attribute("on_behalf_of", user) - .add_attribute("denom", denom) - .add_attribute("amount", repay_amount.checked_sub(refund_amount)?) - .add_attribute("amount_scaled", debt_amount_scaled_delta)) -} - -/// Execute loan liquidations on under-collateralized loans -pub fn liquidate( - deps: DepsMut, - env: Env, - info: MessageInfo, - collateral_denom: String, - debt_denom: String, - user_addr: Addr, - sent_debt_amount: Uint128, - recipient: Option, -) -> Result { - let block_time = env.block.time.seconds(); - let user = User(&user_addr); - // The recipient address for receiving underlying collateral - let recipient_addr = option_string_to_addr(deps.api, recipient, info.sender.clone())?; - let recipient = User(&recipient_addr); - - // 1. Validate liquidation - - // User cannot liquidate themselves - if info.sender == user_addr { - return Err(ContractError::CannotLiquidateSelf {}); - } - - // If user (contract) has a positive uncollateralized limit then the user - // cannot be liquidated - if !user.uncollateralized_loan_limit(deps.storage, &debt_denom)?.is_zero() { - return Err(ContractError::CannotLiquidateWhenPositiveUncollateralizedLoanLimit {}); - }; - - // check if the user has enabled the collateral asset as collateral - let user_collateral = COLLATERALS - .may_load(deps.storage, (&user_addr, &collateral_denom))? - .ok_or(ContractError::CannotLiquidateWhenNoCollateralBalance {})?; - if !user_collateral.enabled { - return Err(ContractError::CannotLiquidateWhenCollateralUnset { - denom: collateral_denom, - }); - } - - // check if user has available collateral in specified collateral asset to be liquidated - let collateral_market = MARKETS.load(deps.storage, &collateral_denom)?; - - // check if user has outstanding debt in the deposited asset that needs to be repayed - let user_debt = DEBTS - .may_load(deps.storage, (&user_addr, &debt_denom))? - .ok_or(ContractError::CannotLiquidateWhenNoDebtBalance {})?; - - // 2. Compute health factor - let config = CONFIG.load(deps.storage)?; - - let addresses = address_provider::helpers::query_contract_addrs( - deps.as_ref(), - &config.address_provider, - vec![ - MarsAddressType::Oracle, - MarsAddressType::Incentives, - MarsAddressType::RewardsCollector, - MarsAddressType::Params, - ], - )?; - let rewards_collector_addr = &addresses[&MarsAddressType::RewardsCollector]; - let incentives_addr = &addresses[&MarsAddressType::Incentives]; - let oracle_addr = &addresses[&MarsAddressType::Oracle]; - let params_addr = &addresses[&MarsAddressType::Params]; - - let (liquidatable, assets_positions) = - assert_liquidatable(&deps.as_ref(), &env, &user_addr, oracle_addr, params_addr)?; - - if !liquidatable { - return Err(ContractError::CannotLiquidateHealthyPosition {}); - } - - let collateral_and_debt_are_the_same_asset = debt_denom == collateral_denom; - - let debt_market = if !collateral_and_debt_are_the_same_asset { - MARKETS.load(deps.storage, &debt_denom)? - } else { - collateral_market.clone() - }; - - // 3. Compute debt to repay and collateral to liquidate - let collateral_price = assets_positions - .get(&collateral_denom) - .ok_or(ContractError::CannotLiquidateWhenNoCollateralBalance {})? - .asset_price; - let debt_price = assets_positions - .get(&debt_denom) - .ok_or(ContractError::CannotLiquidateWhenNoDebtBalance {})? - .asset_price; - - let mut response = Response::new(); - - let user_debt_amount = - get_underlying_debt_amount(user_debt.amount_scaled, &debt_market, block_time)?; - - let collateral_params = query_asset_params(&deps.querier, params_addr, &collateral_denom)?; - let close_factor = query_close_factor(&deps.querier, params_addr)?; - - let ( - debt_amount_to_repay, - collateral_amount_to_liquidate, - collateral_amount_to_liquidate_scaled, - refund_amount, - ) = liquidation_compute_amounts( - user_collateral.amount_scaled, - user_debt_amount, - sent_debt_amount, - &collateral_market, - &collateral_params, - collateral_price, - debt_price, - block_time, - close_factor, - )?; - - // 4. Transfer collateral shares from the user to the liquidator - response = user.decrease_collateral( - deps.storage, - &collateral_market, - collateral_amount_to_liquidate_scaled, - incentives_addr, - response, - )?; - response = recipient.increase_collateral( - deps.storage, - &collateral_market, - collateral_amount_to_liquidate_scaled, - incentives_addr, - response, - )?; - - // 5. Reduce the user's debt shares - let user_debt_amount_after = user_debt_amount.checked_sub(debt_amount_to_repay)?; - let user_debt_amount_scaled_after = - get_scaled_debt_amount(user_debt_amount_after, &debt_market, block_time)?; - - // Compute delta so it can be substracted to total debt - let debt_amount_scaled_delta = - user_debt.amount_scaled.checked_sub(user_debt_amount_scaled_after)?; - - user.decrease_debt(deps.storage, &debt_denom, debt_amount_scaled_delta)?; - - let debt_market_debt_total_scaled_after = - debt_market.debt_total_scaled.checked_sub(debt_amount_scaled_delta)?; - - // 6. Update markets depending on whether the collateral and debt markets are the same - // and whether the liquidator receives coins (no change in liquidity) or underlying asset - // (changes liquidity) - if collateral_and_debt_are_the_same_asset { - // NOTE: for the sake of clarity copy attributes from collateral market and - // give generic naming. Debt market could have been used as well - let mut asset_market_after = collateral_market; - let denom = &collateral_denom; - - response = apply_accumulated_interests( - deps.storage, - &env, - &mut asset_market_after, - rewards_collector_addr, - incentives_addr, - response, - )?; - - asset_market_after.debt_total_scaled = debt_market_debt_total_scaled_after; - - response = update_interest_rates(&env, &mut asset_market_after, response)?; - - MARKETS.save(deps.storage, denom, &asset_market_after)?; - } else { - let mut debt_market_after = debt_market; - - response = apply_accumulated_interests( - deps.storage, - &env, - &mut debt_market_after, - rewards_collector_addr, - incentives_addr, - response, - )?; - - debt_market_after.debt_total_scaled = debt_market_debt_total_scaled_after; - - response = update_interest_rates(&env, &mut debt_market_after, response)?; - - MARKETS.save(deps.storage, &debt_denom, &debt_market_after)?; - } - - // 7. Build response - // refund sent amount in excess of actual debt amount to liquidate - if !refund_amount.is_zero() { - response = - response.add_message(build_send_asset_msg(&info.sender, &debt_denom, refund_amount)); - } - - Ok(response - .add_attribute("action", "liquidate") - .add_attribute("user", user) - .add_attribute("liquidator", info.sender.to_string()) - .add_attribute("recipient", recipient) - .add_attribute("collateral_denom", collateral_denom) - .add_attribute("collateral_amount", collateral_amount_to_liquidate) - .add_attribute("collateral_amount_scaled", collateral_amount_to_liquidate_scaled) - .add_attribute("debt_denom", debt_denom) - .add_attribute("debt_amount", debt_amount_to_repay) - .add_attribute("debt_amount_scaled", debt_amount_scaled_delta)) -} - -/// Computes debt to repay (in debt asset), -/// collateral to liquidate (in collateral asset) and -/// amount to refund the liquidator (in debt asset) -pub fn liquidation_compute_amounts( - user_collateral_amount_scaled: Uint128, - user_debt_amount: Uint128, - sent_debt_amount: Uint128, - collateral_market: &Market, - collateral_params: &AssetParams, - collateral_price: Decimal, - debt_price: Decimal, - block_time: u64, - close_factor: Decimal, -) -> StdResult<(Uint128, Uint128, Uint128, Uint128)> { - // Debt: Only up to a fraction of the total debt (determined by the close factor) can be - // repayed. - let mut debt_amount_to_repay = min(sent_debt_amount, close_factor * user_debt_amount); - - // Collateral: debt to repay in base asset times the liquidation bonus - let mut collateral_amount_to_liquidate = math::divide_uint128_by_decimal( - debt_amount_to_repay * debt_price * (Decimal::one() + collateral_params.liquidation_bonus), - collateral_price, - )?; - let mut collateral_amount_to_liquidate_scaled = - get_scaled_liquidity_amount(collateral_amount_to_liquidate, collateral_market, block_time)?; - - // If collateral amount to liquidate is higher than user_collateral_balance, - // liquidate the full balance and adjust the debt amount to repay accordingly - if collateral_amount_to_liquidate_scaled > user_collateral_amount_scaled { - collateral_amount_to_liquidate_scaled = user_collateral_amount_scaled; - collateral_amount_to_liquidate = get_underlying_liquidity_amount( - collateral_amount_to_liquidate_scaled, - collateral_market, - block_time, - )?; - debt_amount_to_repay = math::divide_uint128_by_decimal( - math::divide_uint128_by_decimal( - collateral_amount_to_liquidate * collateral_price, - debt_price, - )?, - Decimal::one() + collateral_params.liquidation_bonus, - )?; - } - - // In some edges scenarios: - // - if debt_amount_to_repay = 0, some liquidators could drain collaterals and all their coins - // would be refunded, i.e.: without spending coins. - // - if collateral_amount_to_liquidate is 0, some users could liquidate without receiving collaterals - // in return. - if (!collateral_amount_to_liquidate.is_zero() && debt_amount_to_repay.is_zero()) - || (collateral_amount_to_liquidate.is_zero() && !debt_amount_to_repay.is_zero()) - { - return Err(StdError::generic_err( - format!("Can't process liquidation. Invalid collateral_amount_to_liquidate ({collateral_amount_to_liquidate}) and debt_amount_to_repay ({debt_amount_to_repay})") - )); - } - - let refund_amount = sent_debt_amount - debt_amount_to_repay; - - Ok(( - debt_amount_to_repay, - collateral_amount_to_liquidate, - collateral_amount_to_liquidate_scaled, - refund_amount, - )) -} - -/// Update (enable / disable) collateral asset for specific user -pub fn update_asset_collateral_status( - deps: DepsMut, - env: Env, - info: MessageInfo, - denom: String, - enable: bool, -) -> Result { - let user = User(&info.sender); - - let mut collateral = - COLLATERALS.may_load(deps.storage, (user.address(), &denom))?.ok_or_else(|| { - ContractError::UserNoCollateralBalance { - user: user.into(), - denom: denom.clone(), - } - })?; - - let previously_enabled = collateral.enabled; - - collateral.enabled = enable; - COLLATERALS.save(deps.storage, (user.address(), &denom), &collateral)?; - - // if the collateral was previously enabled, but is not disabled, it is necessary to ensure the - // user is not liquidatable after disabling - if previously_enabled && !enable { - let config = CONFIG.load(deps.storage)?; - - let addresses = address_provider::helpers::query_contract_addrs( - deps.as_ref(), - &config.address_provider, - vec![MarsAddressType::Oracle, MarsAddressType::Params], - )?; - let oracle_addr = &addresses[&MarsAddressType::Oracle]; - let params_addr = &addresses[&MarsAddressType::Params]; - - let (liquidatable, _) = - assert_liquidatable(&deps.as_ref(), &env, user.address(), oracle_addr, params_addr)?; - - if liquidatable { - return Err(ContractError::InvalidHealthFactorAfterDisablingCollateral {}); - } - } - - Ok(Response::new() - .add_attribute("action", "update_asset_collateral_status") - .add_attribute("user", user) - .add_attribute("denom", denom) - .add_attribute("enable", enable.to_string())) -} From 75482251d1007a3b70dea43add59d833e2ff517a Mon Sep 17 00:00:00 2001 From: Piotr Babel Date: Mon, 31 Jul 2023 17:54:56 +0200 Subject: [PATCH 25/27] Fix build. --- integration-tests/tests/test_oracles.rs | 2 +- scripts/deploy/base/deployer.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/integration-tests/tests/test_oracles.rs b/integration-tests/tests/test_oracles.rs index e29bf452b..45a6b8ae9 100644 --- a/integration-tests/tests/test_oracles.rs +++ b/integration-tests/tests/test_oracles.rs @@ -1104,7 +1104,7 @@ fn setup_redbank(wasm: &Wasm, signer: &SigningAccount) -> (Strin OSMOSIS_PARAMS_CONTRACT_NAME, &mars_params::msg::InstantiateMsg { owner: (signer.address()), - address_provider: "address_provider".into(), + address_provider: addr_provider_addr.clone(), target_health_factor: Decimal::from_str("1.05").unwrap(), }, ); diff --git a/scripts/deploy/base/deployer.ts b/scripts/deploy/base/deployer.ts index bc4dd6230..e7f7a44c7 100644 --- a/scripts/deploy/base/deployer.ts +++ b/scripts/deploy/base/deployer.ts @@ -175,6 +175,7 @@ export class Deployer { async instantiateParams() { const msg: ParamsInstantiateMsg = { owner: this.deployerAddress, + address_provider: this.storage.addresses['address-provider']!, target_health_factor: this.config.targetHealthFactor, } await this.instantiate('params', this.storage.codeIds.params!, msg) From 591251eec0a42ad822adf974a792b790766b5289 Mon Sep 17 00:00:00 2001 From: Piotr Babel Date: Mon, 31 Jul 2023 18:06:02 +0200 Subject: [PATCH 26/27] Fix test. --- integration-tests/tests/test_oracles.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/integration-tests/tests/test_oracles.rs b/integration-tests/tests/test_oracles.rs index 45a6b8ae9..2fc5b0dcf 100644 --- a/integration-tests/tests/test_oracles.rs +++ b/integration-tests/tests/test_oracles.rs @@ -1164,6 +1164,18 @@ fn setup_redbank(wasm: &Wasm, signer: &SigningAccount) -> (Strin ) .unwrap(); + // We can simulate credit manager contract balance with own params address (used by params contract for deposit caps logic) + wasm.execute( + &addr_provider_addr, + &SetAddress { + address_type: MarsAddressType::CreditManager, + address: params_addr.clone(), + }, + &[], + signer, + ) + .unwrap(); + let (market_params, asset_params) = default_asset_params("uosmo"); wasm.execute( From 942c994d9b7def90caa465a92c692271c784ded8 Mon Sep 17 00:00:00 2001 From: Piotr Babel Date: Wed, 2 Aug 2023 10:09:24 +0200 Subject: [PATCH 27/27] Move deposit_cap from RedBank struct in params contract. --- contracts/params/src/types/asset.rs | 4 +++- contracts/params/tests/helpers/generator.rs | 2 +- contracts/red-bank/src/deposit.rs | 2 +- contracts/red-bank/tests/helpers.rs | 2 +- contracts/red-bank/tests/test_borrow.rs | 1 - contracts/red-bank/tests/test_deposit.rs | 5 ++-- contracts/red-bank/tests/test_liquidate.rs | 2 +- integration-tests/tests/helpers.rs | 4 ++-- .../tests/test_from_coins_to_positions.rs | 4 ++-- .../health/tests/test_health_from_coins.rs | 6 ++--- schemas/mars-params/mars-params.json | 24 +++++++++---------- scripts/deploy/base/deployer.ts | 2 +- scripts/deploy/neutron/config_mainnet.ts | 6 ++--- scripts/deploy/neutron/config_testnet.ts | 6 ++--- .../deploy/neutron/config_testnet_multisig.ts | 6 ++--- scripts/deploy/osmosis/config.ts | 12 +++++----- scripts/deploy/osmosis/mainnetConfig.ts | 6 ++--- scripts/deploy/osmosis/testnetConfig.ts | 6 ++--- scripts/types/config.ts | 2 ++ .../generated/mars-params/MarsParams.types.ts | 3 ++- 20 files changed, 54 insertions(+), 51 deletions(-) diff --git a/contracts/params/src/types/asset.rs b/contracts/params/src/types/asset.rs index bd606a4e1..2021c317e 100644 --- a/contracts/params/src/types/asset.rs +++ b/contracts/params/src/types/asset.rs @@ -21,7 +21,6 @@ pub struct CmSettings { pub struct RedBankSettings { pub deposit_enabled: bool, pub borrow_enabled: bool, - pub deposit_cap: Uint128, } /// The LB will depend on the Health Factor and a couple other parameters as follows: @@ -122,6 +121,7 @@ pub struct AssetParamsBase { pub liquidation_threshold: Decimal, pub liquidation_bonus: LiquidationBonus, pub protocol_liquidation_fee: Decimal, + pub deposit_cap: Uint128, } pub type AssetParams = AssetParamsBase; @@ -140,6 +140,7 @@ impl From for AssetParamsUnchecked { liquidation_threshold: p.liquidation_threshold, liquidation_bonus: p.liquidation_bonus, protocol_liquidation_fee: p.protocol_liquidation_fee, + deposit_cap: p.deposit_cap, } } } @@ -174,6 +175,7 @@ impl AssetParamsUnchecked { liquidation_threshold: self.liquidation_threshold, liquidation_bonus: self.liquidation_bonus.clone(), protocol_liquidation_fee: self.protocol_liquidation_fee, + deposit_cap: self.deposit_cap, }) } } diff --git a/contracts/params/tests/helpers/generator.rs b/contracts/params/tests/helpers/generator.rs index e5ee7dbc1..267b0ab36 100644 --- a/contracts/params/tests/helpers/generator.rs +++ b/contracts/params/tests/helpers/generator.rs @@ -16,7 +16,6 @@ pub fn default_asset_params(denom: &str) -> AssetParamsUnchecked { red_bank: RedBankSettings { deposit_enabled: true, borrow_enabled: false, - deposit_cap: Uint128::new(1_000_000_000), }, max_loan_to_value: Decimal::from_str("0.6").unwrap(), liquidation_threshold: Decimal::from_str("0.7").unwrap(), @@ -27,6 +26,7 @@ pub fn default_asset_params(denom: &str) -> AssetParamsUnchecked { max_lb: Decimal::percent(8), }, protocol_liquidation_fee: Decimal::percent(2), + deposit_cap: Uint128::new(1_000_000_000), } } diff --git a/contracts/red-bank/src/deposit.rs b/contracts/red-bank/src/deposit.rs index 54fdaf26d..711f4e0ef 100644 --- a/contracts/red-bank/src/deposit.rs +++ b/contracts/red-bank/src/deposit.rs @@ -44,7 +44,7 @@ pub fn deposit( } let total_deposits = query_total_deposit(&deps.querier, params_addr, &denom)?; - if total_deposits.amount.checked_add(deposit_amount)? > asset_params.red_bank.deposit_cap { + if total_deposits.amount.checked_add(deposit_amount)? > asset_params.deposit_cap { return Err(ContractError::DepositCapExceeded { denom, }); diff --git a/contracts/red-bank/tests/helpers.rs b/contracts/red-bank/tests/helpers.rs index 72190cdd1..944b10908 100644 --- a/contracts/red-bank/tests/helpers.rs +++ b/contracts/red-bank/tests/helpers.rs @@ -122,7 +122,6 @@ pub fn th_default_asset_params() -> AssetParams { red_bank: RedBankSettings { deposit_enabled: true, borrow_enabled: true, - deposit_cap: Uint128::MAX, }, max_loan_to_value: Decimal::zero(), liquidation_threshold: Decimal::one(), @@ -133,6 +132,7 @@ pub fn th_default_asset_params() -> AssetParams { max_lb: Decimal::percent(5u64), }, protocol_liquidation_fee: Decimal::percent(2u64), + deposit_cap: Uint128::MAX, } } diff --git a/contracts/red-bank/tests/test_borrow.rs b/contracts/red-bank/tests/test_borrow.rs index e9df9af19..98314002e 100644 --- a/contracts/red-bank/tests/test_borrow.rs +++ b/contracts/red-bank/tests/test_borrow.rs @@ -1008,7 +1008,6 @@ fn cannot_borrow_if_market_not_enabled() { red_bank: RedBankSettings { deposit_enabled: false, borrow_enabled: false, - deposit_cap: Default::default(), }, ..th_default_asset_params() }, diff --git a/contracts/red-bank/tests/test_deposit.rs b/contracts/red-bank/tests/test_deposit.rs index da01bf2a1..a10f508af 100644 --- a/contracts/red-bank/tests/test_deposit.rs +++ b/contracts/red-bank/tests/test_deposit.rs @@ -77,9 +77,9 @@ fn setup_test() -> TestSuite { red_bank: RedBankSettings { deposit_enabled: true, borrow_enabled: true, - deposit_cap: Uint128::new(12_000_000), }, protocol_liquidation_fee: Decimal::percent(2u64), + deposit_cap: Uint128::new(12_000_000), }, ); @@ -186,7 +186,6 @@ fn depositing_to_disabled_market() { red_bank: RedBankSettings { deposit_enabled: false, borrow_enabled: true, - deposit_cap: Default::default(), }, ..th_default_asset_params() }, @@ -242,8 +241,8 @@ fn depositing_above_cap(amount_to_deposit: u128, deposit_cap: u128, exp_ok: bool red_bank: RedBankSettings { deposit_enabled: true, borrow_enabled: true, - deposit_cap: Uint128::new(deposit_cap), }, + deposit_cap: Uint128::new(deposit_cap), ..th_default_asset_params() }, ); diff --git a/contracts/red-bank/tests/test_liquidate.rs b/contracts/red-bank/tests/test_liquidate.rs index 227f51afc..a8c052e8f 100644 --- a/contracts/red-bank/tests/test_liquidate.rs +++ b/contracts/red-bank/tests/test_liquidate.rs @@ -1161,7 +1161,6 @@ fn default_asset_params_with( red_bank: RedBankSettings { deposit_enabled: true, borrow_enabled: true, - deposit_cap: Uint128::MAX, }, max_loan_to_value, liquidation_threshold, @@ -1172,6 +1171,7 @@ fn default_asset_params_with( max_lb: Decimal::percent(10), }, protocol_liquidation_fee: Decimal::percent(2), + deposit_cap: Uint128::MAX, }; (market_params, asset_params) } diff --git a/integration-tests/tests/helpers.rs b/integration-tests/tests/helpers.rs index 60b54f10b..a8a3c51bb 100644 --- a/integration-tests/tests/helpers.rs +++ b/integration-tests/tests/helpers.rs @@ -32,7 +32,6 @@ pub fn default_asset_params(denom: &str) -> (InitOrUpdateAssetParams, AssetParam red_bank: RedBankSettings { deposit_enabled: true, borrow_enabled: true, - deposit_cap: Uint128::MAX, }, max_loan_to_value: Decimal::percent(60), liquidation_threshold: Decimal::percent(80), @@ -43,6 +42,7 @@ pub fn default_asset_params(denom: &str) -> (InitOrUpdateAssetParams, AssetParam max_lb: Decimal::percent(5u64), }, protocol_liquidation_fee: Decimal::percent(2u64), + deposit_cap: Uint128::MAX, }; (market_params, asset_params) } @@ -71,12 +71,12 @@ pub fn default_asset_params_with( red_bank: RedBankSettings { deposit_enabled: true, borrow_enabled: true, - deposit_cap: Uint128::MAX, }, max_loan_to_value, liquidation_threshold, liquidation_bonus, protocol_liquidation_fee: Decimal::percent(2u64), + deposit_cap: Uint128::MAX, }; (market_params, asset_params) } diff --git a/packages/health/tests/test_from_coins_to_positions.rs b/packages/health/tests/test_from_coins_to_positions.rs index 48508bac2..9aa85fe0a 100644 --- a/packages/health/tests/test_from_coins_to_positions.rs +++ b/packages/health/tests/test_from_coins_to_positions.rs @@ -136,7 +136,6 @@ fn mock_setup() -> MarsMockQuerier { red_bank: RedBankSettings { deposit_enabled: false, borrow_enabled: false, - deposit_cap: Default::default(), }, max_loan_to_value: Decimal::from_atomics(50u128, 2).unwrap(), liquidation_threshold: Decimal::from_atomics(55u128, 2).unwrap(), @@ -147,6 +146,7 @@ fn mock_setup() -> MarsMockQuerier { max_lb: Decimal::percent(5u64), }, protocol_liquidation_fee: Decimal::zero(), + deposit_cap: Default::default(), }, ); let atom_market = Market { @@ -165,7 +165,6 @@ fn mock_setup() -> MarsMockQuerier { red_bank: RedBankSettings { deposit_enabled: false, borrow_enabled: false, - deposit_cap: Default::default(), }, max_loan_to_value: Decimal::from_atomics(70u128, 2).unwrap(), liquidation_threshold: Decimal::from_atomics(75u128, 2).unwrap(), @@ -176,6 +175,7 @@ fn mock_setup() -> MarsMockQuerier { max_lb: Decimal::percent(5u64), }, protocol_liquidation_fee: Decimal::zero(), + deposit_cap: Default::default(), }, ); diff --git a/packages/health/tests/test_health_from_coins.rs b/packages/health/tests/test_health_from_coins.rs index 20da4adb5..234cedcea 100644 --- a/packages/health/tests/test_health_from_coins.rs +++ b/packages/health/tests/test_health_from_coins.rs @@ -30,7 +30,6 @@ fn health_success_from_coins() { red_bank: RedBankSettings { deposit_enabled: true, borrow_enabled: true, - deposit_cap: Uint128::MAX, }, max_loan_to_value: Decimal::from_atomics(50u128, 2).unwrap(), liquidation_threshold: Decimal::from_atomics(55u128, 2).unwrap(), @@ -41,6 +40,7 @@ fn health_success_from_coins() { max_lb: Decimal::percent(5u64), }, protocol_liquidation_fee: Decimal::zero(), + deposit_cap: Uint128::MAX, }, ); let atom_market = Market { @@ -59,7 +59,6 @@ fn health_success_from_coins() { red_bank: RedBankSettings { deposit_enabled: true, borrow_enabled: true, - deposit_cap: Uint128::MAX, }, max_loan_to_value: Decimal::from_atomics(70u128, 2).unwrap(), liquidation_threshold: Decimal::from_atomics(75u128, 2).unwrap(), @@ -70,6 +69,7 @@ fn health_success_from_coins() { max_lb: Decimal::percent(5u64), }, protocol_liquidation_fee: Decimal::zero(), + deposit_cap: Uint128::MAX, }, ); @@ -127,7 +127,6 @@ fn health_error_from_coins() { red_bank: RedBankSettings { deposit_enabled: false, borrow_enabled: false, - deposit_cap: Default::default(), }, max_loan_to_value: Decimal::from_atomics(50u128, 2).unwrap(), liquidation_threshold: Decimal::from_atomics(55u128, 2).unwrap(), @@ -138,6 +137,7 @@ fn health_error_from_coins() { max_lb: Decimal::percent(5u64), }, protocol_liquidation_fee: Decimal::zero(), + deposit_cap: Default::default(), }, ); diff --git a/schemas/mars-params/mars-params.json b/schemas/mars-params/mars-params.json index 48c010b14..b74a5a050 100644 --- a/schemas/mars-params/mars-params.json +++ b/schemas/mars-params/mars-params.json @@ -108,6 +108,7 @@ "required": [ "credit_manager", "denom", + "deposit_cap", "liquidation_bonus", "liquidation_threshold", "max_loan_to_value", @@ -121,6 +122,9 @@ "denom": { "type": "string" }, + "deposit_cap": { + "$ref": "#/definitions/Uint128" + }, "liquidation_bonus": { "$ref": "#/definitions/LiquidationBonus" }, @@ -484,16 +488,12 @@ "type": "object", "required": [ "borrow_enabled", - "deposit_cap", "deposit_enabled" ], "properties": { "borrow_enabled": { "type": "boolean" }, - "deposit_cap": { - "$ref": "#/definitions/Uint128" - }, "deposit_enabled": { "type": "boolean" } @@ -744,6 +744,7 @@ "required": [ "credit_manager", "denom", + "deposit_cap", "liquidation_bonus", "liquidation_threshold", "max_loan_to_value", @@ -757,6 +758,9 @@ "denom": { "type": "string" }, + "deposit_cap": { + "$ref": "#/definitions/Uint128" + }, "liquidation_bonus": { "$ref": "#/definitions/LiquidationBonus" }, @@ -920,16 +924,12 @@ "type": "object", "required": [ "borrow_enabled", - "deposit_cap", "deposit_enabled" ], "properties": { "borrow_enabled": { "type": "boolean" }, - "deposit_cap": { - "$ref": "#/definitions/Uint128" - }, "deposit_enabled": { "type": "boolean" } @@ -1094,6 +1094,7 @@ "required": [ "credit_manager", "denom", + "deposit_cap", "liquidation_bonus", "liquidation_threshold", "max_loan_to_value", @@ -1107,6 +1108,9 @@ "denom": { "type": "string" }, + "deposit_cap": { + "$ref": "#/definitions/Uint128" + }, "liquidation_bonus": { "$ref": "#/definitions/LiquidationBonus" }, @@ -1274,16 +1278,12 @@ "type": "object", "required": [ "borrow_enabled", - "deposit_cap", "deposit_enabled" ], "properties": { "borrow_enabled": { "type": "boolean" }, - "deposit_cap": { - "$ref": "#/definitions/Uint128" - }, "deposit_enabled": { "type": "boolean" } diff --git a/scripts/deploy/base/deployer.ts b/scripts/deploy/base/deployer.ts index 5eccb891f..a5c7ea102 100644 --- a/scripts/deploy/base/deployer.ts +++ b/scripts/deploy/base/deployer.ts @@ -205,8 +205,8 @@ export class Deployer { red_bank: { borrow_enabled: assetConfig.red_bank.borrow_enabled, deposit_enabled: assetConfig.red_bank.borrow_enabled, - deposit_cap: assetConfig.red_bank.deposit_cap, }, + deposit_cap: assetConfig.deposit_cap, }, }, }, diff --git a/scripts/deploy/neutron/config_mainnet.ts b/scripts/deploy/neutron/config_mainnet.ts index 336137906..2189aaf6b 100644 --- a/scripts/deploy/neutron/config_mainnet.ts +++ b/scripts/deploy/neutron/config_mainnet.ts @@ -289,10 +289,10 @@ export const ntrnAsset: AssetConfig = { whitelisted: false, }, red_bank: { - deposit_cap: '5000000000000', borrow_enabled: true, deposit_enabled: true, }, + deposit_cap: '5000000000000', } export const atomAsset: AssetConfig = { @@ -319,10 +319,10 @@ export const atomAsset: AssetConfig = { whitelisted: false, }, red_bank: { - deposit_cap: '150000000000', borrow_enabled: true, deposit_enabled: true, }, + deposit_cap: '150000000000', } export const axlUSDCAsset: AssetConfig = { @@ -349,10 +349,10 @@ export const axlUSDCAsset: AssetConfig = { whitelisted: false, }, red_bank: { - deposit_cap: '500000000000', borrow_enabled: true, deposit_enabled: true, }, + deposit_cap: '500000000000', } export const neutronMainnetConfig: DeploymentConfig = { diff --git a/scripts/deploy/neutron/config_testnet.ts b/scripts/deploy/neutron/config_testnet.ts index 904c98c0f..e15dcd429 100644 --- a/scripts/deploy/neutron/config_testnet.ts +++ b/scripts/deploy/neutron/config_testnet.ts @@ -248,10 +248,10 @@ export const ntrnAsset: AssetConfig = { whitelisted: false, }, red_bank: { - deposit_cap: '5000000000000', borrow_enabled: true, deposit_enabled: true, }, + deposit_cap: '5000000000000', } export const atomAsset: AssetConfig = { @@ -278,10 +278,10 @@ export const atomAsset: AssetConfig = { whitelisted: false, }, red_bank: { - deposit_cap: '150000000000', borrow_enabled: true, deposit_enabled: true, }, + deposit_cap: '150000000000', } export const axlUSDCAsset: AssetConfig = { @@ -308,10 +308,10 @@ export const axlUSDCAsset: AssetConfig = { whitelisted: false, }, red_bank: { - deposit_cap: '500000000000', borrow_enabled: true, deposit_enabled: true, }, + deposit_cap: '500000000000', } export const neutronTestnetConfig: DeploymentConfig = { diff --git a/scripts/deploy/neutron/config_testnet_multisig.ts b/scripts/deploy/neutron/config_testnet_multisig.ts index 304dd32e1..15a0a2964 100644 --- a/scripts/deploy/neutron/config_testnet_multisig.ts +++ b/scripts/deploy/neutron/config_testnet_multisig.ts @@ -285,10 +285,10 @@ export const ntrnAsset: AssetConfig = { whitelisted: false, }, red_bank: { - deposit_cap: '5000000000000', borrow_enabled: true, deposit_enabled: true, }, + deposit_cap: '5000000000000', } export const atomAsset: AssetConfig = { @@ -315,10 +315,10 @@ export const atomAsset: AssetConfig = { whitelisted: false, }, red_bank: { - deposit_cap: '150000000000', borrow_enabled: true, deposit_enabled: true, }, + deposit_cap: '150000000000', } export const axlUSDCAsset: AssetConfig = { @@ -345,10 +345,10 @@ export const axlUSDCAsset: AssetConfig = { whitelisted: false, }, red_bank: { - deposit_cap: '500000000000', borrow_enabled: true, deposit_enabled: true, }, + deposit_cap: '500000000000', } export const neutronTetstnetMultisigConfig: DeploymentConfig = { diff --git a/scripts/deploy/osmosis/config.ts b/scripts/deploy/osmosis/config.ts index 522b49b12..9dc457a75 100644 --- a/scripts/deploy/osmosis/config.ts +++ b/scripts/deploy/osmosis/config.ts @@ -47,9 +47,9 @@ export const osmoAsset: AssetConfig = { }, red_bank: { borrow_enabled: true, - deposit_cap: '2500000000000', deposit_enabled: true, }, + deposit_cap: '2500000000000', } export const atomAsset: AssetConfig = { @@ -76,9 +76,9 @@ export const atomAsset: AssetConfig = { }, red_bank: { borrow_enabled: true, - deposit_cap: '100000000000', deposit_enabled: true, }, + deposit_cap: '100000000000', } export const atomAssetTest: AssetConfig = { @@ -105,9 +105,9 @@ export const atomAssetTest: AssetConfig = { }, red_bank: { borrow_enabled: true, - deposit_cap: '100000000000', deposit_enabled: true, }, + deposit_cap: '100000000000', } export const axlUSDCAsset: AssetConfig = { @@ -134,9 +134,9 @@ export const axlUSDCAsset: AssetConfig = { }, red_bank: { borrow_enabled: true, - deposit_cap: '500000000000', deposit_enabled: true, }, + deposit_cap: '500000000000', } export const axlUSDCAssetTest: AssetConfig = { @@ -163,9 +163,9 @@ export const axlUSDCAssetTest: AssetConfig = { }, red_bank: { borrow_enabled: true, - deposit_cap: '500000000000', deposit_enabled: true, }, + deposit_cap: '500000000000', } export const marsAssetTest: AssetConfig = { @@ -192,9 +192,9 @@ export const marsAssetTest: AssetConfig = { }, red_bank: { borrow_enabled: true, - deposit_cap: '500000000000', deposit_enabled: true, }, + deposit_cap: '500000000000', } // export const osmoOracle: OracleConfig = { diff --git a/scripts/deploy/osmosis/mainnetConfig.ts b/scripts/deploy/osmosis/mainnetConfig.ts index ea094b00a..fecc8d4c2 100644 --- a/scripts/deploy/osmosis/mainnetConfig.ts +++ b/scripts/deploy/osmosis/mainnetConfig.ts @@ -30,9 +30,9 @@ export const osmoAsset: AssetConfig = { max_loan_to_value: '0.59', red_bank: { borrow_enabled: true, - deposit_cap: '2500000000000', deposit_enabled: true, }, + deposit_cap: '2500000000000', } export const atomAsset: AssetConfig = { @@ -52,9 +52,9 @@ export const atomAsset: AssetConfig = { max_loan_to_value: '0.68', red_bank: { borrow_enabled: true, - deposit_cap: '100000000000', deposit_enabled: true, }, + deposit_cap: '100000000000', } export const axlUSDCAsset: AssetConfig = { @@ -74,9 +74,9 @@ export const axlUSDCAsset: AssetConfig = { max_loan_to_value: '0.74', red_bank: { borrow_enabled: true, - deposit_cap: '500000000000', deposit_enabled: true, }, + deposit_cap: '500000000000', } export const atomOracle: OracleConfig = { diff --git a/scripts/deploy/osmosis/testnetConfig.ts b/scripts/deploy/osmosis/testnetConfig.ts index dc4e4aa73..947a1fb11 100644 --- a/scripts/deploy/osmosis/testnetConfig.ts +++ b/scripts/deploy/osmosis/testnetConfig.ts @@ -30,9 +30,9 @@ export const osmoAsset: AssetConfig = { max_loan_to_value: '0.59', red_bank: { borrow_enabled: true, - deposit_cap: '2500000000000', deposit_enabled: true, }, + deposit_cap: '2500000000000', } export const atomAsset: AssetConfig = { @@ -52,9 +52,9 @@ export const atomAsset: AssetConfig = { max_loan_to_value: '0.68', red_bank: { borrow_enabled: true, - deposit_cap: '100000000000', deposit_enabled: true, }, + deposit_cap: '100000000000', } export const USDCAsset: AssetConfig = { @@ -74,9 +74,9 @@ export const USDCAsset: AssetConfig = { max_loan_to_value: '0.74', red_bank: { borrow_enabled: true, - deposit_cap: '500000000000', deposit_enabled: true, }, + deposit_cap: '500000000000', } export const usdcOsmoVault: VaultConfig = { diff --git a/scripts/types/config.ts b/scripts/types/config.ts index 5b8515bcc..5243ab649 100644 --- a/scripts/types/config.ts +++ b/scripts/types/config.ts @@ -14,6 +14,7 @@ import { RedBankSettings, } from './generated/mars-params/MarsParams.types' import { NeutronIbcConfig } from './generated/mars-rewards-collector-base/MarsRewardsCollectorBase.types' +import { Uint128 } from './generated/mars-red-bank/MarsRedBank.types' type SwapRoute = { denom_in: string @@ -79,6 +80,7 @@ export interface AssetConfig { max_loan_to_value: Decimal protocol_liquidation_fee: Decimal red_bank: RedBankSettings + deposit_cap: Uint128 } export interface VaultConfig { addr: string diff --git a/scripts/types/generated/mars-params/MarsParams.types.ts b/scripts/types/generated/mars-params/MarsParams.types.ts index 3a4c7a3a5..11eb13940 100644 --- a/scripts/types/generated/mars-params/MarsParams.types.ts +++ b/scripts/types/generated/mars-params/MarsParams.types.ts @@ -87,6 +87,7 @@ export type RedBankEmergencyUpdate = { export interface AssetParamsBaseForString { credit_manager: CmSettingsForString denom: string + deposit_cap: Uint128 liquidation_bonus: LiquidationBonus liquidation_threshold: Decimal max_loan_to_value: Decimal @@ -110,7 +111,6 @@ export interface LiquidationBonus { } export interface RedBankSettings { borrow_enabled: boolean - deposit_cap: Uint128 deposit_enabled: boolean } export interface VaultConfigBaseForString { @@ -176,6 +176,7 @@ export type ArrayOfAssetParamsBaseForAddr = AssetParamsBaseForAddr[] export interface AssetParamsBaseForAddr { credit_manager: CmSettingsForAddr denom: string + deposit_cap: Uint128 liquidation_bonus: LiquidationBonus liquidation_threshold: Decimal max_loan_to_value: Decimal