use super::*;
use crate::{
house_order_book::{
HOUSE_ROLE,
HouseOrderBookDeploy,
HouseOrderBookDeployConfig,
},
prop::PropOrderBookCleanup,
};
const UNIT: u64 = 1_000_000_000;
const PRICE: u64 = 2_000_000;
async fn house_pair(fixture: &PropFixture) -> HouseOrderBookDeploy<Wallet> {
let house = HouseOrderBookDeploy::deploy(
&fixture.deployer,
&fixture.order_book.order_book,
&HouseOrderBookDeployConfig::default(),
)
.await
.unwrap();
house
.canonical
.methods()
.set_sidecar(Some(house.book.contract_id))
.with_contracts(&[&house.house])
.call()
.await
.unwrap();
house
.canonical
.methods()
.grant_role(HOUSE_ROLE, Identity::Address(fixture.deployer.address()))
.call()
.await
.unwrap();
house
}
async fn source_order(
fixture: &PropFixture,
house: &HouseOrderBookDeploy<Wallet>,
price: u64,
quantity: u64,
buy: bool,
) {
let (asset, coins) = if buy {
(
fixture.collateral_asset,
(quantity as u128 * price as u128 / UNIT as u128) as u64,
)
} else {
(fixture.base_asset, quantity)
};
house
.canonical
.methods()
.create_order(OrderArgs {
price,
quantity,
order_type: OrderType::TurboSharedPostOnly,
})
.with_contract_ids(&house.contract_ids())
.call_params(CallParameters::new(coins, asset, 30_000_000))
.unwrap()
.with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
.call()
.await
.unwrap();
}
fn dependencies(
fixture: &PropFixture,
house: &HouseOrderBookDeploy<Wallet>,
) -> Vec<ContractId> {
vec![
fixture.deployment.oracle_id,
fixture.deployment.pool_id,
fixture.deployment.registry_id,
fixture.deployment.price_feed_id,
fixture.child_id,
house.book.contract_id,
house.canonical.contract_id(),
]
}
#[tokio::test]
async fn house_turbo_account_cannot_bypass_route_and_pool_values_only_its_own_assets() {
let mut fixture = PropFixture::new().await;
let house = house_pair(&fixture).await;
fixture.close_session().await;
fixture.order_book = house.book.clone();
publish_turbo_tier(&fixture, turbo_terms(DRAWDOWN_SCHEDULE)).await;
start_turbo_session(&fixture).await;
let deps = dependencies(&fixture, &house);
source_order(&fixture, &house, PRICE, 10 * UNIT, false).await;
let draw = fixture.draw_call(6_000_000);
fixture
.call_account_with_contract_ids(vec![draw], &deps)
.await
.unwrap();
let initial_value = fixture.session_value().await;
let mut bypass = fixture.book_call(PRICE, UNIT, fixture.collateral_asset, PRICE);
bypass.contract_id = house.canonical.contract_id();
let error = fixture
.call_account_with_contract_ids(vec![bypass], &deps)
.await
.unwrap_err();
assert!(error.to_string().contains("TargetNotAllowed"), "{error:#}");
let account_base_before = fixture.account_balance(fixture.base_asset).await;
let buy = fixture.book_call(PRICE, UNIT, fixture.collateral_asset, PRICE);
let bought = fixture
.call_account_with_contract_ids(vec![buy], &deps)
.await
.unwrap();
let fills = house
.decode_shared_executions(&bought.tx_status.receipts)
.unwrap();
assert_eq!(fills.len(), 1);
assert_eq!(fills[0].quantity, UNIT);
assert_eq!(fills[0].price, PRICE);
assert_eq!(
house
.house
.methods()
.get_total_balance_of(Identity::ContractId(fixture.child_id))
.call()
.await
.unwrap()
.value,
(0, 0)
);
assert_eq!(
fixture.account_balance(fixture.base_asset).await,
account_base_before + UNIT
);
assert_eq!(
house
.canonical
.methods()
.get_total_balance_of(Identity::ContractId(fixture.child_id))
.call()
.await
.unwrap()
.value,
(0, 0)
);
assert_eq!(fixture.session_value().await, initial_value);
let rest = fixture.book_call(1_900_000, UNIT, fixture.collateral_asset, 1_900_000);
fixture
.call_account_with_contract_ids(vec![rest], &deps)
.await
.unwrap();
let error = fixture
.deployment
.pool
.methods()
.min_sellable(fixture.child_id, fixture.base_asset)
.with_contract_ids(&deps)
.call()
.await
.unwrap_err();
assert!(error.to_string().contains("NoBestBid"), "{error}");
source_order(&fixture, &house, 1_800_000, UNIT, true).await;
let minimum = fixture
.deployment
.pool
.methods()
.min_sellable(fixture.child_id, fixture.base_asset)
.with_contract_ids(&deps)
.call()
.await
.unwrap()
.value;
assert_eq!(minimum, Some(556));
assert_eq!(fixture.session_value().await, initial_value);
let resting = house
.house
.methods()
.get_orders_at_price(crate::order_book_deploy::Side::Buy, 1_900_000)
.with_contract_ids(&house.contract_ids())
.call()
.await
.unwrap()
.value[0]
.order_id;
let cleanup = PropOrderBookCleanup {
order_book: house.book.contract_id,
order_ids: vec![resting],
};
fixture.fund_collateral(6_000_000).await;
let repay = fixture.repay_from_collateral_call(6_000_000);
fixture
.call_account_with_contract_ids(vec![repay], &deps)
.await
.unwrap();
house.house.methods().pause().call().await.unwrap();
let error = fixture
.account
.methods()
.close_session(vec![cleanup.clone()])
.with_contracts(&[&house.house, &fixture.deployment.pool])
.with_contract_ids(&deps)
.with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
.call()
.await
.unwrap_err();
assert!(error.to_string().contains("Paused"), "{error}");
assert!(fixture.session_opt().await.is_some());
house.house.methods().unpause().call().await.unwrap();
fixture
.account
.methods()
.close_session(vec![cleanup])
.with_contracts(&[&house.house, &fixture.deployment.pool])
.with_contract_ids(&deps)
.with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
.call()
.await
.unwrap();
assert!(fixture.session_opt().await.is_none());
assert_eq!(
house
.house
.methods()
.get_total_balance_of(Identity::ContractId(fixture.child_id))
.call()
.await
.unwrap()
.value,
(0, 0)
);
assert_eq!(
house
.canonical
.methods()
.get_total_balance_of(Identity::Address(fixture.deployer.address()))
.call()
.await
.unwrap()
.value,
(9 * UNIT, 3_800_000)
);
}
#[tokio::test]
async fn house_tier_publication_preserves_existing_session_cleanup_until_reopening() {
let mut fixture = PropFixture::new().await;
publish_turbo_tier(&fixture, turbo_terms(DRAWDOWN_SCHEDULE)).await;
open_turbo_session(&fixture).await;
let canonical = fixture.order_book.clone();
let draw = fixture.draw_call(1_000_000);
let rest = fixture.book_call(1_000_000, UNIT, fixture.collateral_asset, 1_000_000);
fixture.call_account(vec![draw, rest], 0).await.unwrap();
let order = canonical
.order_book
.methods()
.get_orders_at_price(crate::order_book_deploy::Side::Buy, 1_000_000)
.call()
.await
.unwrap()
.value[0]
.order_id;
let house = house_pair(&fixture).await;
fixture.order_book = house.book.clone();
let version = publish_turbo_tier(&fixture, turbo_terms(DRAWDOWN_SCHEDULE)).await;
let books = fixture
.deployment
.pool
.methods()
.session_books(fixture.child_id)
.call()
.await
.unwrap()
.value;
assert_eq!(books, vec![canonical.contract_id]);
assert!(fixture.session().await.tier_version < version);
let deps = dependencies(&fixture, &house);
fixture.fund_collateral(1_000_000).await;
let repay = fixture.repay_from_collateral_call(1_000_000);
fixture
.call_account_with_contract_ids(vec![repay], &deps)
.await
.unwrap();
fixture
.account
.methods()
.close_session(vec![PropOrderBookCleanup {
order_book: canonical.contract_id,
order_ids: vec![order],
}])
.with_contracts(&[&canonical.order_book, &fixture.deployment.pool])
.with_contract_ids(&deps)
.with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
.call()
.await
.unwrap();
assert_eq!(
canonical
.order_book
.methods()
.get_total_balance_of(Identity::ContractId(fixture.child_id))
.call()
.await
.unwrap()
.value,
(0, 0)
);
start_turbo_session(&fixture).await;
assert_eq!(fixture.session().await.tier_version, version);
assert_eq!(
fixture
.deployment
.pool
.methods()
.session_books(fixture.child_id)
.call()
.await
.unwrap()
.value,
vec![house.book.contract_id]
);
assert!(
!fixture
.deployment
.pool
.methods()
.is_call_allowed(fixture.child_id, canonical.contract_id)
.call()
.await
.unwrap()
.value
);
}