use crate::contract::{execute, instantiate};
use crate::contract::query;
use crate::testing::mock_querier::mock_dependencies;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{attr, to_json_binary, Decimal256, StdError, Uint128, Uint256,from_json};
use cw20::Cw20ReceiveMsg;
use solid_moneymarket::liquidation_queue::{Cw20HookMsg, ExecuteMsg, InstantiateMsg, QueryMsg};
use solid_moneymarket::liquidation_queue::{BidPoolResponse, BidResponse};
#[test]
fn one_bidder_distribution() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 60u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(3000),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::zero(),
premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 1u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let mut env = mock_env();
env.block.time = wait_end;
let info = mock_info("alice0000", &[]);
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(2u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "2"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: None,
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "1"),
attr("amount", "41"),
]
);
}
#[test]
fn two_bidder_distribution() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 101u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(1000),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::zero(),
premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 0u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(4u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(60u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 0u8,
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(2u128)]),
};
let info = mock_info("bob0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(2000),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(6u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "7"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: None,
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
assert_eq!(
res,
StdError::generic_err("No bids with the specified information exist")
);
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("bob0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "3"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(2u128),
amount: None,
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
assert_eq!(
res,
StdError::generic_err("No bids with the specified information exist")
);
}
#[test]
fn two_bidder_distribution_big_numbers() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 101u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(1000000000),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::zero(),
premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(10000000000u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 0u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(400u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(6000000000u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 0u8,
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(2u128)]),
};
let info = mock_info("bob0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(2000000000),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(600u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "700"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: None,
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
assert_eq!(
res,
StdError::generic_err("No bids with the specified information exist")
);
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("bob0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "300"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(2u128),
amount: None,
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
assert_eq!(
res,
StdError::generic_err("No bids with the specified information exist")
);
}
#[test]
fn one_user_two_bid_slots() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 10u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(1000),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::zero(),
premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(100000000u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 5u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env.clone(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(100000000u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 10u8,
})
.unwrap(),
});
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128), Uint128::from(2u128)]),
};
let info = mock_info("alice0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(5000000u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
println!("first assert res: {:?}", res);
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "5000000"),
]
);
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(10000000u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
println!("second assert res: {:?}", res);
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "9999999"), ]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: None,
};
execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap_err();
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(2u128),
amount: None,
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "2"),
attr("amount", "59736835"), ]
);
}
#[test]
fn partial_withdraw_after_execution() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 101u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(5000),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::zero(),
premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(1000u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 0u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(10u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: Some(Uint256::from(250u64)),
};
let info = mock_info("alice0000", &[]);
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(250u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 0u8,
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(2u128)]),
};
let info = mock_info("bob0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(4u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
let info = mock_info("col0000", &[]);
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "12"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: None,
};
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg.clone()).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "1"),
attr("amount", "150"),
]
);
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
assert_eq!(
res,
StdError::generic_err("No bids with the specified information exist")
);
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("bob0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "2"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(2u128),
amount: None,
};
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg.clone()).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "2"),
attr("amount", "150"),
]
);
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
assert_eq!(
res,
StdError::generic_err("No bids with the specified information exist")
);
}
#[test]
fn completely_empty_pool() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 101u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(5000),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::from(10000u128), premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(1000u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 0u8,
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(20u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(2000u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 0u8,
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let bid_response: BidResponse = from_json(
&query(
deps.as_ref(),
mock_env(),
QueryMsg::Bid {
bid_idx: Uint128::from(2u128),
},
)
.unwrap(),
)
.unwrap();
assert!(!bid_response.product_snapshot.is_zero(),);
assert!(bid_response.epoch_snapshot == Uint128::from(1u128));
let bid_pool: BidPoolResponse = from_json(
&query(
deps.as_ref(),
mock_env(),
QueryMsg::BidPool {
collateral_token: "col0000".to_string(),
bid_slot: 0u8,
},
)
.unwrap(),
)
.unwrap();
assert_eq!(
bid_pool,
BidPoolResponse {
sum_snapshot: Decimal256::zero(), product_snapshot: Decimal256::one(), premium_rate: Decimal256::zero(),
total_bid_amount: Uint256::from(2000u128), current_epoch: Uint128::from(1u128), current_scale: Uint128::zero(),
}
);
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(20u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "20"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: None,
};
execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("bob0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "20"),
]
);
}
#[test]
fn product_truncated_to_zero() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 101u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(100),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::from(10000u128), premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
for _ in 0..8 {
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(1000000000u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 0u8,
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(999999995u128), msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
}
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "7999999959"), ]
);
let bid_pool: BidPoolResponse = from_json(
&query(
deps.as_ref(),
mock_env(),
QueryMsg::BidPool {
collateral_token: "col0000".to_string(),
bid_slot: 0u8,
},
)
.unwrap(),
)
.unwrap();
assert_eq!(bid_pool.total_bid_amount, Uint256::from(40u128));
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(8u128), amount: None,
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "8"),
attr("amount", "40"),
]
);
}
#[test]
fn two_bidder_reward_distribution_common_slot() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 101u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(1000),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::zero(),
premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 5u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 5u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(2u128)]),
};
let info = mock_info("bob0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(10u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "5"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: Some(Uint256::from(52u64)),
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "1"),
attr("amount", "52"),
]
);
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("bob0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "5"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(2u128),
amount: Some(Uint256::from(52u128)),
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "2"),
attr("amount", "53"),
]
);
}
#[test]
fn two_bidder_distribution_multiple_common_slots() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 60u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(200),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::zero(),
premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 5u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let mut env = mock_env();
env.block.time = wait_end;
let info = mock_info("alice0000", &[]);
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 5u8,
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(2u128)]),
};
let info = mock_info("bob0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(200u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 10u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(3u128)]),
};
let info = mock_info("alice0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(200u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 10u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(4u128)]),
};
let info = mock_info("bob0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(10u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(22u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "15"),
]
);
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(3u128)]),
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "0"),
]
);
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(2u128)]),
};
let info = mock_info("bob0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "16"),
]
);
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(4u128)]),
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "0"),
]
);
let info = mock_info("alice0000", &[]);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: Some(Uint256::from(2u128)),
};
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "1"),
attr("amount", "2"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(3u128),
amount: Some(Uint256::from(1u128)),
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "3"),
attr("amount", "1"),
]
);
let info = mock_info("bob0000", &[]);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(2u128),
amount: Some(Uint256::from(2u128)),
};
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "2"),
attr("amount", "2"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(4u128),
amount: Some(Uint256::from(1u128)),
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "4"),
attr("amount", "1"),
]
);
}
#[test]
fn two_bidder_unequal_deposit_reward_distribution() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 101u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(200),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::zero(),
premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(150u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 2u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(200u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 2u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(2u128)]),
};
let info = mock_info("bob0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(51u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "21"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: Some(Uint256::from(107u64)),
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "1"),
attr("amount", "107"),
]
);
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("bob0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "29"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(2u128),
amount: Some(Uint256::from(142u128)),
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "2"),
attr("amount", "143"),
]
);
}
#[test]
fn scalable_reward_distribution_after_multiple_liquidations() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 60u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(200),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::zero(),
premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(50u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 10u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 10u8,
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(2u128)]),
};
let mut env = mock_env();
env.block.time = wait_end;
let info = mock_info("bob0000", &[]);
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "john0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 10u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(3u128)]),
};
let info = mock_info("john0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(250u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 10u8,
})
.unwrap(),
});
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(4u128)]),
};
let info = mock_info("bob0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "john0000".to_string(),
amount: Uint128::from(250u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 10u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(5u128)]),
};
let info = mock_info("john0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(50u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "21"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: Some(Uint256::from(8u128)),
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "1"),
attr("amount", "8"),
]
);
}
#[test]
fn not_enough_bid_for_collateral() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 101u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(300),
env.block.time.seconds(),
env.block.time.seconds(),
),
)]);
let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::zero(),
premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 6u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let info = mock_info("alice0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 6u8,
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(2u128)]),
};
let info = mock_info("bob0000", &[]);
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(100u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
assert_eq!(
res,
StdError::generic_err("Not enough bids to execute this liquidation")
)
}
#[test]
fn two_bidder_reward_distribution_common_slot_large_numbers() {
let mut deps = mock_dependencies(&[]);
deps.querier
.with_collateral_max_ltv(&[(&"col0000".to_string(), &Decimal256::percent(90))]);
let msg = InstantiateMsg {
owner: "owner0000".to_string(),
oracle_contract: "oracle0000".to_string(),
stable_contract: "stable0000".to_string(),
safe_ratio: Decimal256::percent(10),
bid_fee: Decimal256::percent(1),
liquidator_fee: Decimal256::percent(0),
liquidation_threshold: Uint256::from(100000000u64),
price_timeframe: 101u64,
waiting_period: 60u64,
overseer: "overseer0000".to_string(),
};
let info = mock_info("addr0000", &[]);
let env = mock_env();
deps.querier.with_oracle_price(&[(
&("col0000".to_string(), "uusd".to_string()),
&(
Decimal256::percent(100000000000),
env.block.time.seconds(),
env.block.time.seconds(),
), )]);
let _res = instantiate(deps.as_mut(), env, info, msg).unwrap();
let msg = ExecuteMsg::WhitelistCollateral {
collateral_token: "col0000".to_string(),
max_slot: 30u8,
bid_threshold: Uint256::zero(),
premium_rate_per_slot: Decimal256::percent(1),
};
let info = mock_info("owner0000", &[]);
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "alice0000".to_string(),
amount: Uint128::from(1000000000000000000u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 5u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("alice0000", &[]);
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(1u128)]),
};
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("stable0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "bob0000".to_string(),
amount: Uint128::from(1000000000000000000u128),
msg: to_json_binary(&Cw20HookMsg::SubmitBid {
collateral_token: "col0000".to_string(),
premium_slot: 5u8,
})
.unwrap(),
});
let env = mock_env();
let wait_end = env.block.time.plus_seconds(60u64);
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("bob0000", &[]);
let msg = ExecuteMsg::ActivateBids {
collateral_token: "col0000".to_string(),
bids_idx: Some(vec![Uint128::from(2u128)]),
};
let mut env = mock_env();
env.block.time = wait_end;
execute(deps.as_mut(), env, info, msg).unwrap();
let info = mock_info("col0000", &[]);
let msg = ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: "custody0000".to_string(),
amount: Uint128::from(1000000000u128),
msg: to_json_binary(&Cw20HookMsg::ExecuteBid {
liquidator: "liquidator00000".to_string(),
fee_address: Some("fee0000".to_string()),
repay_address: Some("repay0000".to_string()),
borrower_address: Some("addr0000".to_string()),
})
.unwrap(),
});
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("alice0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "500000000"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(1u128),
amount: Some(Uint256::from(525000000000000u64)),
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "1"),
attr("amount", "525000000000000"),
]
);
let msg = ExecuteMsg::ClaimLiquidations {
collateral_token: "col0000".to_string(),
bids_idx: None,
};
let info = mock_info("bob0000", &[]);
let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "claim_liquidations"),
attr("collateral_token", "col0000"),
attr("collateral_amount", "500000000"),
]
);
let msg = ExecuteMsg::RetractBid {
bid_idx: Uint128::from(2u128),
amount: Some(Uint256::from(525000000u128)),
};
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
res.attributes,
vec![
attr("action", "retract_bid"),
attr("bid_idx", "2"),
attr("amount", "525000000"),
]
);
}