Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions contracts/rewards-collector/base/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use mars_red_bank_types::{
address_provider::{self, AddressResponseItem, MarsAddressType},
incentives, red_bank,
rewards_collector::{
credit_manager::{self, Action},
Config, ConfigResponse, ExecuteMsg, InstantiateMsg, QueryMsg, UpdateConfig,
},
};
Expand Down Expand Up @@ -85,6 +86,10 @@ where
denom,
amount,
} => self.withdraw_from_red_bank(deps, denom, amount),
ExecuteMsg::WithdrawFromCreditManager {
account_id,
actions,
} => self.withdraw_from_credit_manager(deps, account_id, actions),
ExecuteMsg::DistributeRewards {
denom,
amount,
Expand Down Expand Up @@ -194,6 +199,42 @@ where
.add_attribute("amount", stringify_option_amount(amount)))
}

pub fn withdraw_from_credit_manager(
&self,
deps: DepsMut,
account_id: String,
actions: Vec<Action>,
) -> ContractResult<Response<M>> {
let cfg = self.config.load(deps.storage)?;

let valid_actions = actions.iter().all(|action| {
matches!(action, Action::Withdraw(..) | Action::WithdrawLiquidity { .. })
});
if !valid_actions {
return Err(ContractError::InvalidActionsForCreditManager {});
}

let cm_addr = address_provider::helpers::query_contract_addr(
deps.as_ref(),
&cfg.address_provider,
MarsAddressType::CreditManager,
)?;

let withdraw_from_cm_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: cm_addr.to_string(),
msg: to_binary(&credit_manager::ExecuteMsg::UpdateCreditAccount {
account_id: account_id.clone(),
actions,
})?,
funds: vec![],
});

Ok(Response::new()
.add_message(withdraw_from_cm_msg)
.add_attribute("action", "withdraw_from_credit_manager")
.add_attribute("account_id", account_id))
}

pub fn claim_incentive_rewards(
&self,
deps: DepsMut,
Expand Down
3 changes: 3 additions & 0 deletions contracts/rewards-collector/base/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ pub enum ContractError {
InvalidRoute {
reason: String,
},

#[error("Invalid actions. Only Withdraw and WithdrawLiquidity is possible to pass for CreditManager")]
InvalidActionsForCreditManager {},
}

pub type ContractResult<T> = Result<T, ContractError>;
81 changes: 79 additions & 2 deletions contracts/rewards-collector/osmosis/tests/test_withdraw.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use cosmwasm_std::{testing::mock_env, to_binary, CosmosMsg, SubMsg, Uint128, WasmMsg};
use mars_red_bank_types::rewards_collector::ExecuteMsg;
use cosmwasm_std::{coin, testing::mock_env, to_binary, CosmosMsg, SubMsg, Uint128, WasmMsg};
use mars_red_bank_types::rewards_collector::{
credit_manager::{self, Action, ActionAmount, ActionCoin},
ExecuteMsg,
};
use mars_rewards_collector_base::ContractError;
use mars_rewards_collector_osmosis::entry::execute;
use mars_testing::mock_info;

Expand Down Expand Up @@ -37,3 +41,76 @@ fn withdrawing_from_red_bank() {
}))
)
}

#[test]
fn withdrawing_from_cm_if_action_not_allowed() {
let mut deps = helpers::setup_test();

// anyone can execute a withdrawal
let error_res = execute(
deps.as_mut(),
mock_env(),
mock_info("jake"),
ExecuteMsg::WithdrawFromCreditManager {
account_id: "random_id".to_string(),
actions: vec![
Action::Withdraw(coin(100u128, "uatom")),
Action::Unknown {},
Action::WithdrawLiquidity {
lp_token: ActionCoin {
denom: "gamm/pool/1".to_string(),
amount: ActionAmount::AccountBalance,
},
minimum_receive: vec![],
},
],
},
)
.unwrap_err();
assert_eq!(error_res, ContractError::InvalidActionsForCreditManager {});
}

#[test]
fn withdrawing_from_cm_successfully() {
let mut deps = helpers::setup_test();

let account_id = "random_id".to_string();
let actions = vec![
Action::Withdraw(coin(100u128, "uusdc")),
Action::WithdrawLiquidity {
lp_token: ActionCoin {
denom: "gamm/pool/1".to_string(),
amount: ActionAmount::AccountBalance,
},
minimum_receive: vec![],
},
Action::Withdraw(coin(120u128, "uatom")),
Action::Withdraw(coin(140u128, "uosmo")),
];

// anyone can execute a withdrawal
let res = execute(
deps.as_mut(),
mock_env(),
mock_info("jake"),
ExecuteMsg::WithdrawFromCreditManager {
account_id: account_id.clone(),
actions: actions.clone(),
},
)
.unwrap();

assert_eq!(res.messages.len(), 1);
assert_eq!(
res.messages[0],
SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: "credit_manager".to_string(),
msg: to_binary(&credit_manager::ExecuteMsg::UpdateCreditAccount {
account_id,
actions
})
.unwrap(),
funds: vec![]
}))
)
}
44 changes: 44 additions & 0 deletions packages/types/src/rewards_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use mars_utils::{
helpers::{decimal_param_le_one, integer_param_gt_zero, validate_native_denom},
};

use self::credit_manager::Action;

const MAX_SLIPPAGE_TOLERANCE_PERCENTAGE: u64 = 50;

#[cw_serde]
Expand Down Expand Up @@ -130,6 +132,12 @@ pub enum ExecuteMsg {
amount: Option<Uint128>,
},

/// Withdraw coins from the credit manager
WithdrawFromCreditManager {
account_id: String,
actions: Vec<Action>,
},

/// Distribute the accrued protocol income between the safety fund and the fee modules on mars hub,
/// according to the split set in config.
/// Callable by any address.
Expand Down Expand Up @@ -191,3 +199,39 @@ pub enum QueryMsg {
#[returns(ConfigResponse)]
Config {},
}

// TODO: rover is private repo for now so can't use it as a dependency. Use rover types once repo is public.
pub mod credit_manager {
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Coin, Uint128};

#[cw_serde]
pub enum ExecuteMsg {
UpdateCreditAccount {
account_id: String,
actions: Vec<Action>,
},
}

#[cw_serde]
pub enum Action {
Withdraw(Coin),
WithdrawLiquidity {
lp_token: ActionCoin,
minimum_receive: Vec<Coin>,
},
Unknown {}, // Used to simulate allowance only for: Withdraw and WithdrawLiquidity
}

#[cw_serde]
pub struct ActionCoin {
pub denom: String,
pub amount: ActionAmount,
}

#[cw_serde]
pub enum ActionAmount {
Exact(Uint128),
AccountBalance,
}
}
124 changes: 124 additions & 0 deletions schemas/mars-rewards-collector-base/mars-rewards-collector-base.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,35 @@
},
"additionalProperties": false
},
{
"description": "Withdraw coins from the credit manager",
"type": "object",
"required": [
"withdraw_from_credit_manager"
],
"properties": {
"withdraw_from_credit_manager": {
"type": "object",
"required": [
"account_id",
"actions"
],
"properties": {
"account_id": {
"type": "string"
},
"actions": {
"type": "array",
"items": {
"$ref": "#/definitions/Action"
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Distribute the accrued protocol income between the safety fund and the fee modules on mars hub, according to the split set in config. Callable by any address.",
"type": "object",
Expand Down Expand Up @@ -300,6 +329,101 @@
}
],
"definitions": {
"Action": {
"oneOf": [
{
"type": "object",
"required": [
"withdraw"
],
"properties": {
"withdraw": {
"$ref": "#/definitions/Coin"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"withdraw_liquidity"
],
"properties": {
"withdraw_liquidity": {
"type": "object",
"required": [
"lp_token",
"minimum_receive"
],
"properties": {
"lp_token": {
"$ref": "#/definitions/ActionCoin"
},
"minimum_receive": {
"type": "array",
"items": {
"$ref": "#/definitions/Coin"
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"unknown"
],
"properties": {
"unknown": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
}
]
},
"ActionAmount": {
"oneOf": [
{
"type": "string",
"enum": [
"account_balance"
]
},
{
"type": "object",
"required": [
"exact"
],
"properties": {
"exact": {
"$ref": "#/definitions/Uint128"
}
},
"additionalProperties": false
}
]
},
"ActionCoin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/ActionAmount"
},
"denom": {
"type": "string"
}
},
"additionalProperties": false
},
"Coin": {
"type": "object",
"required": [
Expand Down
Loading