From b8d4b7d1a4f2ceb89bf67b8b9799b547c34afc4e Mon Sep 17 00:00:00 2001 From: sparqet Date: Thu, 28 Mar 2024 12:43:39 +0100 Subject: [PATCH 1/2] refactor integration tests --- ...et.cairo => test_deposit_withdrawal.cairo} | 621 ----------- tests/integration/test_long_integration.cairo | 969 ++++++++++++++++++ .../integration/test_short_integration.cairo | 965 +++++++++++++++++ tests/integration/test_swap_integration.cairo | 955 +++++++++++++++++ tests/lib.cairo | 5 +- 5 files changed, 2893 insertions(+), 622 deletions(-) rename tests/integration/{create_market.cairo => test_deposit_withdrawal.cairo} (70%) create mode 100644 tests/integration/test_long_integration.cairo create mode 100644 tests/integration/test_short_integration.cairo create mode 100644 tests/integration/test_swap_integration.cairo diff --git a/tests/integration/create_market.cairo b/tests/integration/test_deposit_withdrawal.cairo similarity index 70% rename from tests/integration/create_market.cairo rename to tests/integration/test_deposit_withdrawal.cairo index e8aa9402..339fd6cd 100644 --- a/tests/integration/create_market.cairo +++ b/tests/integration/test_deposit_withdrawal.cairo @@ -922,627 +922,6 @@ fn test_deposit_withdraw_integration() { teardown(data_store, market_factory); } -#[test] -fn test_long_market_integration() { - // ********************************************************************************************* - // * SETUP * - // ********************************************************************************************* - let ( - caller_address, - market_factory_address, - role_store_address, - data_store_address, - market_token_class_hash, - market_factory, - role_store, - data_store, - event_emitter, - exchange_router, - deposit_handler, - deposit_vault, - oracle, - order_handler, - order_vault, - reader, - referal_storage, - withdrawal_handler, - withdrawal_vault, - ) = - setup(); - - // ********************************************************************************************* - // * TEST LOGIC * - // ********************************************************************************************* - - // Create a market. - let market = data_store.get_market(create_market(market_factory)); - - // Set params in data_store - data_store.set_address(keys::fee_token(), market.index_token); - data_store.set_u256(keys::max_swap_path_length(), 5); - - // Set max pool amount. - data_store - .set_u256( - keys::max_pool_amount_key(market.market_token, market.long_token), 500000000000000000 - ); - data_store - .set_u256( - keys::max_pool_amount_key(market.market_token, market.short_token), 500000000000000000 - ); - - oracle.set_price_testing_eth(5000); - - // Fill the pool. - IERC20Dispatcher { contract_address: market.long_token }.mint(market.market_token, 50000000000); - IERC20Dispatcher { contract_address: market.short_token } - .mint(market.market_token, 50000000000); - // TODO Check why we don't need to set pool_amount_key - // // Set pool amount in data_store. - // let mut key = keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()); - // data_store.set_u256(key, 50000000000); - // key = keys::pool_amount_key(market.market_token, contract_address_const::<'USDC'>()); - // data_store.set_u256(key, 50000000000); - - // Send token to deposit in the deposit vault (this should be in a multi call with create_deposit) - IERC20Dispatcher { contract_address: market.long_token } - .mint(deposit_vault.contract_address, 50000000000); - IERC20Dispatcher { contract_address: market.short_token } - .mint(deposit_vault.contract_address, 50000000000); - - let balance_deposit_vault_before = IERC20Dispatcher { contract_address: market.short_token } - .balance_of(deposit_vault.contract_address); - - // Create Deposit - let user1: ContractAddress = contract_address_const::<'user1'>(); - let user2: ContractAddress = contract_address_const::<'user2'>(); - - let addresss_zero: ContractAddress = 0.try_into().unwrap(); - - let params = CreateDepositParams { - receiver: user1, - callback_contract: user2, - ui_fee_receiver: addresss_zero, - market: market.market_token, - initial_long_token: market.long_token, - initial_short_token: market.short_token, - long_token_swap_path: Array32Trait::::span32(@array![]), - short_token_swap_path: Array32Trait::::span32(@array![]), - min_market_tokens: 0, - execution_fee: 0, - callback_gas_limit: 0, - }; - - start_roll(deposit_handler.contract_address, 1910); - let key = deposit_handler.create_deposit(caller_address, params); - let first_deposit = data_store.get_deposit(key); - - assert(first_deposit.account == caller_address, 'Wrong account depositer'); - assert(first_deposit.receiver == user1, 'Wrong account receiver'); - assert(first_deposit.initial_long_token == market.long_token, 'Wrong initial long token'); - assert( - first_deposit.initial_long_token_amount == 50000000000, 'Wrong initial long token amount' - ); - assert( - first_deposit.initial_short_token_amount == 50000000000, 'Wrong init short token amount' - ); - - let price_params = SetPricesParams { // TODO - signer_info: 1, - tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], - compacted_min_oracle_block_numbers: array![1900, 1900], - compacted_max_oracle_block_numbers: array![1910, 1910], - compacted_oracle_timestamps: array![9999, 9999], - compacted_decimals: array![18, 18], - compacted_min_prices: array![4294967346000000], // 50000000, 1000000 compacted - compacted_min_prices_indexes: array![0], - compacted_max_prices: array![4294967346000000], // 50000000, 1000000 compacted - compacted_max_prices_indexes: array![0], - signatures: array![ - array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() - ], - price_feed_tokens: array![] - }; - - start_prank(role_store.contract_address, caller_address); - - role_store.grant_role(caller_address, role::ORDER_KEEPER); - role_store.grant_role(caller_address, role::ROLE_ADMIN); - role_store.grant_role(caller_address, role::CONTROLLER); - role_store.grant_role(caller_address, role::MARKET_KEEPER); - - // Execute Deposit - start_roll(deposit_handler.contract_address, 1915); - deposit_handler.execute_deposit(key, price_params); - - let pool_value_info = market_utils::get_pool_value_info( - data_store, - market, - Price { min: 1999, max: 2000 }, - Price { min: 1999, max: 2000 }, - Price { min: 1999, max: 2000 }, - keys::max_pnl_factor_for_deposits(), - true, - ); - - assert(pool_value_info.pool_value.mag == 200000000000000, 'wrong pool value amount'); - assert(pool_value_info.long_token_amount == 50000000000, 'wrong long token amount'); - assert(pool_value_info.short_token_amount == 50000000000, 'wrong short token amount'); - - let not_deposit = data_store.get_deposit(key); - let default_deposit: Deposit = Default::default(); - assert(not_deposit == default_deposit, 'Still existing deposit'); - - // let market_token_dispatcher = IMarketTokenDispatcher { contract_address: market.market_token }; - - // let balance = market_token_dispatcher.balance_of(user1); - - let balance_deposit_vault = IERC20Dispatcher { contract_address: market.short_token } - .balance_of(deposit_vault.contract_address); - - let pool_value_info = market_utils::get_pool_value_info( - data_store, - market, - Price { min: 5000, max: 5000, }, - Price { min: 5000, max: 5000, }, - Price { min: 1, max: 1, }, - keys::max_pnl_factor_for_deposits(), - true, - ); - - pool_value_info.pool_value.mag.print(); - pool_value_info.long_token_amount.print(); - pool_value_info.short_token_amount.print(); - - // ************************************* TEST LONG ********************************************* - - 'begining of LONG TEST'.print(); - - let key_open_interest = keys::open_interest_key( - market.market_token, contract_address_const::<'ETH'>(), true - ); - data_store.set_u256(key_open_interest, 1); - let max_key_open_interest = keys::max_open_interest_key(market.market_token, true); - data_store.set_u256(max_key_open_interest, 10000); - - start_prank(contract_address_const::<'ETH'>(), caller_address); - // Send token to order_vault in multicall with create_order - IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } - .transfer(order_vault.contract_address, 1); - - 'transfer made'.print(); - // Create order_params Struct - let contract_address = contract_address_const::<0>(); - start_prank(market.market_token, caller_address); - start_prank(market.long_token, caller_address); - let order_params_long = CreateOrderParams { - receiver: caller_address, - callback_contract: contract_address, - ui_fee_receiver: contract_address, - market: market.market_token, - initial_collateral_token: market.long_token, - swap_path: Array32Trait::::span32(@array![market.market_token]), - size_delta_usd: 5000, - initial_collateral_delta_amount: 1, // 10^18 - trigger_price: 5000, - acceptable_price: 5500, - execution_fee: 0, - callback_gas_limit: 0, - min_output_amount: 0, - order_type: OrderType::MarketIncrease(()), - decrease_position_swap_type: DecreasePositionSwapType::NoSwap(()), - is_long: true, - referral_code: 0 - }; - // Create the swap order. - start_roll(order_handler.contract_address, 1930); - 'try to create prder'.print(); - start_prank(order_handler.contract_address, caller_address); - let key_long = order_handler.create_order(caller_address, order_params_long); - 'long created'.print(); - let got_order_long = data_store.get_order(key_long); - // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'USDC'>()), ); - // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()), 1000000); - // Execute the swap order. - - let signatures: Span = array![0].span(); - let set_price_params = SetPricesParams { - signer_info: 2, - tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], - compacted_min_oracle_block_numbers: array![1910, 1910], - compacted_max_oracle_block_numbers: array![1920, 1920], - compacted_oracle_timestamps: array![9999, 9999], - compacted_decimals: array![1, 1], - compacted_min_prices: array![2147483648010000], // 500000, 10000 compacted - compacted_min_prices_indexes: array![0], - compacted_max_prices: array![2147483648010000], // 500000, 10000 compacted - compacted_max_prices_indexes: array![0], - signatures: array![ - array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() - ], - price_feed_tokens: array![] - }; - - let keeper_address = contract_address_const::<'keeper'>(); - role_store.grant_role(keeper_address, role::ORDER_KEEPER); - - stop_prank(order_handler.contract_address); - start_prank(order_handler.contract_address, keeper_address); - start_roll(order_handler.contract_address, 1935); - // TODO add real signatures check on Oracle Account - order_handler.execute_order_keeper(key_long, set_price_params, keeper_address); - 'long position SUCCEEDED'.print(); - let position_key = position_utils::get_position_key( - caller_address, market.market_token, contract_address_const::<'USDC'>(), true - ); - - let first_position = data_store.get_position(position_key); - let market_prices = market_utils::MarketPrices { - index_token_price: Price { min: 8000, max: 8000, }, - long_token_price: Price { min: 8000, max: 8000, }, - short_token_price: Price { min: 1, max: 1, }, - }; - 'size tokens'.print(); - first_position.size_in_tokens.print(); - 'size in usd'.print(); - first_position.size_in_usd.print(); - - oracle.set_price_testing_eth(7000); - let position_key_after_pump = position_utils::get_position_key( - caller_address, market.market_token, contract_address_const::<'USDC'>(), true - ); - let first_position_after_pump = data_store.get_position(position_key_after_pump); - 'size tokens after pump'.print(); - first_position_after_pump.size_in_tokens.print(); - 'size in usd after pump'.print(); - first_position_after_pump.size_in_usd.print(); - - let position_info = reader - .get_position_info( - data_store, - referal_storage, - position_key_after_pump, - market_prices, - 0, - contract_address, - true - ); - 'pnl'.print(); - position_info.base_pnl_usd.mag.print(); - - let second_swap_pool_value_info = market_utils::get_pool_value_info( - data_store, - market, - Price { min: 5000, max: 5000, }, - Price { min: 5000, max: 5000, }, - Price { min: 1, max: 1, }, - keys::max_pnl_factor_for_deposits(), - true, - ); - - second_swap_pool_value_info.pool_value.mag.print(); - second_swap_pool_value_info.long_token_amount.print(); - second_swap_pool_value_info.short_token_amount.print(); - // let (position_pnl_usd, uncapped_position_pnl_usd, size_delta_in_tokens) = - // position_utils::get_position_pnl_usd( - // data_store, market, market_prices, first_position, 5000 - // ); - // position_pnl_usd.mag.print(); - - // ********************************************************************************************* - // * TEARDOWN * - // ********************************************************************************************* - teardown(data_store, market_factory); -} - -// #[test] -// fn test_short_market_integration() { -// // ********************************************************************************************* -// // * SETUP * -// // ********************************************************************************************* -// let ( -// caller_address, -// market_factory_address, -// role_store_address, -// data_store_address, -// market_token_class_hash, -// market_factory, -// role_store, -// data_store, -// event_emitter, -// exchange_router, -// deposit_handler, -// deposit_vault, -// oracle, -// order_handler, -// order_vault, -// ) = -// setup(); - -// // ********************************************************************************************* -// // * TEST LOGIC * -// // ********************************************************************************************* - -// // Create a market. -// let market = data_store.get_market(create_market(market_factory)); - -// // Set params in data_store -// data_store.set_address(keys::fee_token(), market.index_token); -// data_store.set_u256(keys::max_swap_path_length(), 5); - -// // Set max pool amount. -// data_store -// .set_u256( -// keys::max_pool_amount_key(market.market_token, market.long_token), 500000000000000000 -// ); -// data_store -// .set_u256( -// keys::max_pool_amount_key(market.market_token, market.short_token), 500000000000000000 -// ); - -// // Fill the pool. -// IERC20Dispatcher { contract_address: market.long_token }.mint(market.market_token, 50000000000); -// IERC20Dispatcher { contract_address: market.short_token } -// .mint(market.market_token, 50000000000); -// // TODO Check why we don't need to set pool_amount_key -// // // Set pool amount in data_store. -// // let mut key = keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()); -// // data_store.set_u256(key, 50000000000); -// // key = keys::pool_amount_key(market.market_token, contract_address_const::<'USDC'>()); -// // data_store.set_u256(key, 50000000000); - -// // Send token to deposit in the deposit vault (this should be in a multi call with create_deposit) -// IERC20Dispatcher { contract_address: market.long_token } -// .mint(deposit_vault.contract_address, 50000000000); -// IERC20Dispatcher { contract_address: market.short_token } -// .mint(deposit_vault.contract_address, 50000000000); - -// let balance_deposit_vault_before = IERC20Dispatcher { contract_address: market.short_token } -// .balance_of(deposit_vault.contract_address); - -// // Create Deposit -// let user1: ContractAddress = contract_address_const::<'user1'>(); -// let user2: ContractAddress = contract_address_const::<'user2'>(); - -// let addresss_zero: ContractAddress = 0.try_into().unwrap(); - -// let params = CreateDepositParams { -// receiver: user1, -// callback_contract: user2, -// ui_fee_receiver: addresss_zero, -// market: market.market_token, -// initial_long_token: market.long_token, -// initial_short_token: market.short_token, -// long_token_swap_path: Array32Trait::::span32(@array![]), -// short_token_swap_path: Array32Trait::::span32(@array![]), -// min_market_tokens: 0, -// execution_fee: 0, -// callback_gas_limit: 0, -// }; - -// start_roll(deposit_handler.contract_address, 1910); -// let key = deposit_handler.create_deposit(caller_address, params); -// let first_deposit = data_store.get_deposit(key); - -// assert(first_deposit.account == caller_address, 'Wrong account depositer'); -// assert(first_deposit.receiver == user1, 'Wrong account receiver'); -// assert(first_deposit.initial_long_token == market.long_token, 'Wrong initial long token'); -// assert( -// first_deposit.initial_long_token_amount == 50000000000, 'Wrong initial long token amount' -// ); -// assert( -// first_deposit.initial_short_token_amount == 50000000000, 'Wrong init short token amount' -// ); - -// let price_params = SetPricesParams { // TODO -// signer_info: 1, -// tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], -// compacted_min_oracle_block_numbers: array![1900, 1900], -// compacted_max_oracle_block_numbers: array![1910, 1910], -// compacted_oracle_timestamps: array![9999, 9999], -// compacted_decimals: array![18, 18], -// compacted_min_prices: array![4294967346000000], // 50000000, 1000000 compacted -// compacted_min_prices_indexes: array![0], -// compacted_max_prices: array![4294967346000000], // 50000000, 1000000 compacted -// compacted_max_prices_indexes: array![0], -// signatures: array![ -// array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() -// ], -// price_feed_tokens: array![] -// }; - -// start_prank(role_store.contract_address, caller_address); - -// role_store.grant_role(caller_address, role::ORDER_KEEPER); -// role_store.grant_role(caller_address, role::ROLE_ADMIN); -// role_store.grant_role(caller_address, role::CONTROLLER); -// role_store.grant_role(caller_address, role::MARKET_KEEPER); - -// // Execute Deposit -// start_roll(deposit_handler.contract_address, 1915); -// deposit_handler.execute_deposit(key, price_params); - -// let pool_value_info = market_utils::get_pool_value_info( -// data_store, -// market, -// Price { min: 1999, max: 2000 }, -// Price { min: 1999, max: 2000 }, -// Price { min: 1999, max: 2000 }, -// keys::max_pnl_factor_for_deposits(), -// true, -// ); - -// assert(pool_value_info.pool_value.mag == 200000000000000, 'wrong pool value amount'); -// assert(pool_value_info.long_token_amount == 50000000000, 'wrong long token amount'); -// assert(pool_value_info.short_token_amount == 50000000000, 'wrong short token amount'); - -// let not_deposit = data_store.get_deposit(key); -// let default_deposit: Deposit = Default::default(); -// assert(not_deposit == default_deposit, 'Still existing deposit'); - -// // let market_token_dispatcher = IMarketTokenDispatcher { contract_address: market.market_token }; - -// // let balance = market_token_dispatcher.balance_of(user1); - -// let balance_deposit_vault = IERC20Dispatcher { contract_address: market.short_token } -// .balance_of(deposit_vault.contract_address); - -// let pool_value_info = market_utils::get_pool_value_info( -// data_store, -// market, -// Price { -// min: 5000, -// max: 5000, -// } -// , -// Price { -// min: 5000, -// max: 5000, -// }, -// Price { -// min: 1, -// max: 1, -// }, -// keys::max_pnl_factor_for_deposits(), -// true, -// ); - -// pool_value_info.pool_value.mag.print(); -// pool_value_info.long_token_amount.print(); -// pool_value_info.short_token_amount.print(); - -// // ************************************* TEST SHORT ********************************************* - -// 'begining of SHORT TEST'.print(); - -// let key_open_interest = keys::open_interest_key(market.market_token, contract_address_const::<'ETH'>(), true); -// data_store.set_u256(key_open_interest, 1); -// let max_key_open_interest = keys::max_open_interest_key(market.market_token, true); -// data_store.set_u256(max_key_open_interest, 10000); - -// start_prank(contract_address_const::<'ETH'>(), caller_address); -// // Send token to order_vault in multicall with create_order -// IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } -// .transfer(order_vault.contract_address, 1); - -// 'transfer made'.print(); -// // Create order_params Struct -// let contract_address = contract_address_const::<0>(); -// start_prank(market.market_token, caller_address); -// start_prank(market.long_token, caller_address); -// let order_params_short = CreateOrderParams { -// receiver: caller_address, -// callback_contract: contract_address, -// ui_fee_receiver: contract_address, -// market: market.market_token, -// initial_collateral_token: market.long_token, -// swap_path: Array32Trait::::span32(@array![market.market_token]), -// size_delta_usd: 5000, -// initial_collateral_delta_amount: 5000, // 10^18 -// trigger_price: 5000, -// acceptable_price: 5500, -// execution_fee: 0, -// callback_gas_limit: 0, -// min_output_amount: 0, -// order_type: OrderType::MarketDecrease(()), -// decrease_position_swap_type: DecreasePositionSwapType::NoSwap(()), -// is_long: false, -// referral_code: 0 -// }; -// // Create the swap order. -// start_roll(order_handler.contract_address, 1930); -// 'try to create prder'.print(); -// start_prank(order_handler.contract_address, caller_address); -// let key_short = order_handler.create_order(caller_address, order_params_short); -// 'short created'.print(); -// let got_order_short = data_store.get_order(key_short); -// // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'USDC'>()), ); -// // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()), 1000000); -// // Execute the swap order. - -// let signatures: Span = array![0].span(); -// let set_price_params = SetPricesParams { -// signer_info: 2, -// tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], -// compacted_min_oracle_block_numbers: array![1910, 1910], -// compacted_max_oracle_block_numbers: array![1920, 1920], -// compacted_oracle_timestamps: array![9999, 9999], -// compacted_decimals: array![1, 1], -// compacted_min_prices: array![2147483648010000], // 500000, 10000 compacted -// compacted_min_prices_indexes: array![0], -// compacted_max_prices: array![2147483648010000], // 500000, 10000 compacted -// compacted_max_prices_indexes: array![0], -// signatures: array![ -// array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() -// ], -// price_feed_tokens: array![] -// }; - -// let keeper_address = contract_address_const::<'keeper'>(); -// role_store.grant_role(keeper_address, role::ORDER_KEEPER); - -// stop_prank(order_handler.contract_address); -// start_prank(order_handler.contract_address, keeper_address); -// start_roll(order_handler.contract_address, 1935); -// // TODO add real signatures check on Oracle Account -// order_handler.execute_order_keeper(key_short, set_price_params, keeper_address); -// 'short position SUCCEEDED'.print(); -// let position_key = position_utils::get_position_key(caller_address, market.market_token, contract_address_const::<'USDC'>(), true); -// let first_position = data_store.get_position(position_key); -// let market_prices = market_utils::MarketPrices { -// index_token_price: Price { -// min: 8000, -// max: 8000, -// }, -// long_token_price: Price { -// min: 8000, -// max: 8000, -// }, -// short_token_price: Price { -// min: 1, -// max: 1, -// }, -// }; -// 'size tokens'.print(); -// first_position.size_in_tokens.print(); -// 'size in usd'.print(); -// first_position.size_in_usd.print(); - -// let second_swap_pool_value_info = market_utils::get_pool_value_info( -// data_store, -// market, -// Price { -// min: 5000, -// max: 5000, -// } -// , -// Price { -// min: 5000, -// max: 5000, -// }, -// Price { -// min: 1, -// max: 1, -// }, -// keys::max_pnl_factor_for_deposits(), -// true, -// ); - -// second_swap_pool_value_info.pool_value.mag.print(); -// second_swap_pool_value_info.long_token_amount.print(); -// second_swap_pool_value_info.short_token_amount.print(); -// // let (position_pnl_usd, uncapped_position_pnl_usd, size_delta_in_tokens) = -// // position_utils::get_position_pnl_usd( -// // data_store, market, market_prices, first_position, 5000 -// // ); -// // position_pnl_usd.mag.print(); - -// // ********************************************************************************************* -// // * TEARDOWN * -// // ********************************************************************************************* -// teardown(data_store, market_factory); -// } - fn create_market(market_factory: IMarketFactoryDispatcher) -> ContractAddress { // Create a market. let (index_token, short_token) = deploy_tokens(); diff --git a/tests/integration/test_long_integration.cairo b/tests/integration/test_long_integration.cairo new file mode 100644 index 00000000..f91ad5cb --- /dev/null +++ b/tests/integration/test_long_integration.cairo @@ -0,0 +1,969 @@ +// ************************************************************************* +// IMPORTS +// ************************************************************************* + +// Core lib imports. + +use result::ResultTrait; +use debug::PrintTrait; +use traits::{TryInto, Into}; +use starknet::{ + ContractAddress, get_caller_address, Felt252TryIntoContractAddress, contract_address_const, + ClassHash, +}; +use snforge_std::{declare, start_prank, stop_prank, start_roll, ContractClassTrait, ContractClass}; + + +// Local imports. +use satoru::data::data_store::{IDataStoreDispatcher, IDataStoreDispatcherTrait}; +use satoru::role::role_store::{IRoleStoreDispatcher, IRoleStoreDispatcherTrait}; +use satoru::market::market_factory::{IMarketFactoryDispatcher, IMarketFactoryDispatcherTrait}; +use satoru::event::event_emitter::{IEventEmitterDispatcher, IEventEmitterDispatcherTrait}; +use satoru::deposit::deposit_vault::{IDepositVaultDispatcher, IDepositVaultDispatcherTrait}; +use satoru::deposit::deposit::Deposit; +use satoru::withdrawal::withdrawal::Withdrawal; + +use satoru::exchange::withdrawal_handler::{ + IWithdrawalHandlerDispatcher, IWithdrawalHandlerDispatcherTrait +}; +use satoru::exchange::deposit_handler::{IDepositHandlerDispatcher, IDepositHandlerDispatcherTrait}; +use satoru::router::exchange_router::{IExchangeRouterDispatcher, IExchangeRouterDispatcherTrait}; +use satoru::mock::referral_storage::{IReferralStorageDispatcher, IReferralStorageDispatcherTrait}; +use satoru::reader::reader::{IReaderDispatcher, IReaderDispatcherTrait}; +use satoru::market::market::{Market, UniqueIdMarket}; +use satoru::market::market_token::{IMarketTokenDispatcher, IMarketTokenDispatcherTrait}; +use satoru::role::role; +use satoru::oracle::oracle_utils::SetPricesParams; +use satoru::tests_lib; +use satoru::deposit::deposit_utils::CreateDepositParams; +use satoru::utils::span32::{Span32, DefaultSpan32, Array32Trait}; +use satoru::deposit::deposit_utils; +use satoru::bank::bank::{IBankDispatcherTrait, IBankDispatcher}; +use satoru::bank::strict_bank::{IStrictBankDispatcher, IStrictBankDispatcherTrait}; +use satoru::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; +use satoru::oracle::oracle::{IOracleDispatcher, IOracleDispatcherTrait}; +use satoru::withdrawal::withdrawal_vault::{ + IWithdrawalVaultDispatcher, IWithdrawalVaultDispatcherTrait +}; +use satoru::data::keys; +use satoru::market::market_utils; +use satoru::price::price::{Price, PriceTrait}; +use satoru::position::position_utils; +use satoru::withdrawal::withdrawal_utils; + +use satoru::order::order::{Order, OrderType, SecondaryOrderType, DecreasePositionSwapType}; +use satoru::order::order_vault::{IOrderVaultDispatcher, IOrderVaultDispatcherTrait}; +use satoru::order::base_order_utils::{CreateOrderParams}; +use satoru::oracle::oracle_store::{IOracleStoreDispatcher, IOracleStoreDispatcherTrait}; +use satoru::swap::swap_handler::{ISwapHandlerDispatcher, ISwapHandlerDispatcherTrait}; +use satoru::market::{market::{UniqueIdMarketImpl},}; +use satoru::exchange::order_handler::{ + OrderHandler, IOrderHandlerDispatcher, IOrderHandlerDispatcherTrait +}; +const INITIAL_TOKENS_MINTED: felt252 = 1000; + +#[test] +fn test_long_market_integration() { + // ********************************************************************************************* + // * SETUP * + // ********************************************************************************************* + let ( + caller_address, + market_factory_address, + role_store_address, + data_store_address, + market_token_class_hash, + market_factory, + role_store, + data_store, + event_emitter, + exchange_router, + deposit_handler, + deposit_vault, + oracle, + order_handler, + order_vault, + reader, + referal_storage, + withdrawal_handler, + withdrawal_vault, + ) = + setup(); + + // ********************************************************************************************* + // * TEST LOGIC * + // ********************************************************************************************* + + // Create a market. + let market = data_store.get_market(create_market(market_factory)); + + // Set params in data_store + data_store.set_address(keys::fee_token(), market.index_token); + data_store.set_u256(keys::max_swap_path_length(), 5); + + // Set max pool amount. + data_store + .set_u256( + keys::max_pool_amount_key(market.market_token, market.long_token), 500000000000000000 + ); + data_store + .set_u256( + keys::max_pool_amount_key(market.market_token, market.short_token), 500000000000000000 + ); + + oracle.set_price_testing_eth(5000); + + // Fill the pool. + IERC20Dispatcher { contract_address: market.long_token }.mint(market.market_token, 50000000000); + IERC20Dispatcher { contract_address: market.short_token } + .mint(market.market_token, 50000000000); + // TODO Check why we don't need to set pool_amount_key + // // Set pool amount in data_store. + // let mut key = keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()); + // data_store.set_u256(key, 50000000000); + // key = keys::pool_amount_key(market.market_token, contract_address_const::<'USDC'>()); + // data_store.set_u256(key, 50000000000); + + // Send token to deposit in the deposit vault (this should be in a multi call with create_deposit) + IERC20Dispatcher { contract_address: market.long_token } + .mint(deposit_vault.contract_address, 50000000000); + IERC20Dispatcher { contract_address: market.short_token } + .mint(deposit_vault.contract_address, 50000000000); + + let balance_deposit_vault_before = IERC20Dispatcher { contract_address: market.short_token } + .balance_of(deposit_vault.contract_address); + + // Create Deposit + let user1: ContractAddress = contract_address_const::<'user1'>(); + let user2: ContractAddress = contract_address_const::<'user2'>(); + + let addresss_zero: ContractAddress = 0.try_into().unwrap(); + + let params = CreateDepositParams { + receiver: user1, + callback_contract: user2, + ui_fee_receiver: addresss_zero, + market: market.market_token, + initial_long_token: market.long_token, + initial_short_token: market.short_token, + long_token_swap_path: Array32Trait::::span32(@array![]), + short_token_swap_path: Array32Trait::::span32(@array![]), + min_market_tokens: 0, + execution_fee: 0, + callback_gas_limit: 0, + }; + + start_roll(deposit_handler.contract_address, 1910); + let key = deposit_handler.create_deposit(caller_address, params); + let first_deposit = data_store.get_deposit(key); + + assert(first_deposit.account == caller_address, 'Wrong account depositer'); + assert(first_deposit.receiver == user1, 'Wrong account receiver'); + assert(first_deposit.initial_long_token == market.long_token, 'Wrong initial long token'); + assert( + first_deposit.initial_long_token_amount == 50000000000, 'Wrong initial long token amount' + ); + assert( + first_deposit.initial_short_token_amount == 50000000000, 'Wrong init short token amount' + ); + + let price_params = SetPricesParams { // TODO + signer_info: 1, + tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], + compacted_min_oracle_block_numbers: array![1900, 1900], + compacted_max_oracle_block_numbers: array![1910, 1910], + compacted_oracle_timestamps: array![9999, 9999], + compacted_decimals: array![18, 18], + compacted_min_prices: array![4294967346000000], // 50000000, 1000000 compacted + compacted_min_prices_indexes: array![0], + compacted_max_prices: array![4294967346000000], // 50000000, 1000000 compacted + compacted_max_prices_indexes: array![0], + signatures: array![ + array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() + ], + price_feed_tokens: array![] + }; + + start_prank(role_store.contract_address, caller_address); + + role_store.grant_role(caller_address, role::ORDER_KEEPER); + role_store.grant_role(caller_address, role::ROLE_ADMIN); + role_store.grant_role(caller_address, role::CONTROLLER); + role_store.grant_role(caller_address, role::MARKET_KEEPER); + + // Execute Deposit + start_roll(deposit_handler.contract_address, 1915); + deposit_handler.execute_deposit(key, price_params); + + let pool_value_info = market_utils::get_pool_value_info( + data_store, + market, + Price { min: 1999, max: 2000 }, + Price { min: 1999, max: 2000 }, + Price { min: 1999, max: 2000 }, + keys::max_pnl_factor_for_deposits(), + true, + ); + + assert(pool_value_info.pool_value.mag == 200000000000000, 'wrong pool value amount'); + assert(pool_value_info.long_token_amount == 50000000000, 'wrong long token amount'); + assert(pool_value_info.short_token_amount == 50000000000, 'wrong short token amount'); + + let not_deposit = data_store.get_deposit(key); + let default_deposit: Deposit = Default::default(); + assert(not_deposit == default_deposit, 'Still existing deposit'); + + // let market_token_dispatcher = IMarketTokenDispatcher { contract_address: market.market_token }; + + // let balance = market_token_dispatcher.balance_of(user1); + + let balance_deposit_vault = IERC20Dispatcher { contract_address: market.short_token } + .balance_of(deposit_vault.contract_address); + + let pool_value_info = market_utils::get_pool_value_info( + data_store, + market, + Price { min: 5000, max: 5000, }, + Price { min: 5000, max: 5000, }, + Price { min: 1, max: 1, }, + keys::max_pnl_factor_for_deposits(), + true, + ); + + pool_value_info.pool_value.mag.print(); + pool_value_info.long_token_amount.print(); + pool_value_info.short_token_amount.print(); + + // ************************************* TEST LONG ********************************************* + + 'begining of LONG TEST'.print(); + + let key_open_interest = keys::open_interest_key( + market.market_token, contract_address_const::<'ETH'>(), true + ); + data_store.set_u256(key_open_interest, 1); + let max_key_open_interest = keys::max_open_interest_key(market.market_token, true); + data_store.set_u256(max_key_open_interest, 10000); + + start_prank(contract_address_const::<'ETH'>(), caller_address); + // Send token to order_vault in multicall with create_order + IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } + .transfer(order_vault.contract_address, 1); + + 'transfer made'.print(); + // Create order_params Struct + let contract_address = contract_address_const::<0>(); + start_prank(market.market_token, caller_address); + start_prank(market.long_token, caller_address); + let order_params_long = CreateOrderParams { + receiver: caller_address, + callback_contract: contract_address, + ui_fee_receiver: contract_address, + market: market.market_token, + initial_collateral_token: market.long_token, + swap_path: Array32Trait::::span32(@array![market.market_token]), + size_delta_usd: 5000, + initial_collateral_delta_amount: 1, // 10^18 + trigger_price: 5000, + acceptable_price: 5500, + execution_fee: 0, + callback_gas_limit: 0, + min_output_amount: 0, + order_type: OrderType::MarketIncrease(()), + decrease_position_swap_type: DecreasePositionSwapType::NoSwap(()), + is_long: true, + referral_code: 0 + }; + // Create the swap order. + start_roll(order_handler.contract_address, 1930); + 'try to create prder'.print(); + start_prank(order_handler.contract_address, caller_address); + let key_long = order_handler.create_order(caller_address, order_params_long); + 'long created'.print(); + let got_order_long = data_store.get_order(key_long); + // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'USDC'>()), ); + // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()), 1000000); + // Execute the swap order. + + let signatures: Span = array![0].span(); + let set_price_params = SetPricesParams { + signer_info: 2, + tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], + compacted_min_oracle_block_numbers: array![1910, 1910], + compacted_max_oracle_block_numbers: array![1920, 1920], + compacted_oracle_timestamps: array![9999, 9999], + compacted_decimals: array![1, 1], + compacted_min_prices: array![2147483648010000], // 500000, 10000 compacted + compacted_min_prices_indexes: array![0], + compacted_max_prices: array![2147483648010000], // 500000, 10000 compacted + compacted_max_prices_indexes: array![0], + signatures: array![ + array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() + ], + price_feed_tokens: array![] + }; + + let keeper_address = contract_address_const::<'keeper'>(); + role_store.grant_role(keeper_address, role::ORDER_KEEPER); + + stop_prank(order_handler.contract_address); + start_prank(order_handler.contract_address, keeper_address); + start_roll(order_handler.contract_address, 1935); + // TODO add real signatures check on Oracle Account + order_handler.execute_order_keeper(key_long, set_price_params, keeper_address); + 'long position SUCCEEDED'.print(); + let position_key = position_utils::get_position_key( + caller_address, market.market_token, contract_address_const::<'USDC'>(), true + ); + + let first_position = data_store.get_position(position_key); + let market_prices = market_utils::MarketPrices { + index_token_price: Price { min: 8000, max: 8000, }, + long_token_price: Price { min: 8000, max: 8000, }, + short_token_price: Price { min: 1, max: 1, }, + }; + 'size tokens'.print(); + first_position.size_in_tokens.print(); + 'size in usd'.print(); + first_position.size_in_usd.print(); + + oracle.set_price_testing_eth(7000); + let position_key_after_pump = position_utils::get_position_key( + caller_address, market.market_token, contract_address_const::<'USDC'>(), true + ); + let first_position_after_pump = data_store.get_position(position_key_after_pump); + 'size tokens after pump'.print(); + first_position_after_pump.size_in_tokens.print(); + 'size in usd after pump'.print(); + first_position_after_pump.size_in_usd.print(); + + let position_info = reader + .get_position_info( + data_store, + referal_storage, + position_key_after_pump, + market_prices, + 0, + contract_address, + true + ); + 'pnl'.print(); + position_info.base_pnl_usd.mag.print(); + + let second_swap_pool_value_info = market_utils::get_pool_value_info( + data_store, + market, + Price { min: 5000, max: 5000, }, + Price { min: 5000, max: 5000, }, + Price { min: 1, max: 1, }, + keys::max_pnl_factor_for_deposits(), + true, + ); + + second_swap_pool_value_info.pool_value.mag.print(); + second_swap_pool_value_info.long_token_amount.print(); + second_swap_pool_value_info.short_token_amount.print(); + // let (position_pnl_usd, uncapped_position_pnl_usd, size_delta_in_tokens) = + // position_utils::get_position_pnl_usd( + // data_store, market, market_prices, first_position, 5000 + // ); + // position_pnl_usd.mag.print(); + + // ********************************************************************************************* + // * TEARDOWN * + // ********************************************************************************************* + teardown(data_store, market_factory); +} + +fn create_market(market_factory: IMarketFactoryDispatcher) -> ContractAddress { + // Create a market. + let (index_token, short_token) = deploy_tokens(); + let market_type = 'market_type'; + + // Index token is the same as long token here. + market_factory.create_market(index_token, index_token, short_token, market_type) +} + +/// Utility functions to deploy tokens for a market. +fn deploy_tokens() -> (ContractAddress, ContractAddress) { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let contract = declare('ERC20'); + + let eth_address = contract_address_const::<'ETH'>(); + let constructor_calldata = array!['Ethereum', 'ETH', 1000000, 0, caller_address.into()]; + contract.deploy_at(@constructor_calldata, eth_address).unwrap(); + + let usdc_address = contract_address_const::<'USDC'>(); + let constructor_calldata = array!['usdc', 'USDC', 1000000, 0, caller_address.into()]; + contract.deploy_at(@constructor_calldata, usdc_address).unwrap(); + (eth_address, usdc_address) +} + +/// Utility function to setup the test environment. +fn setup() -> ( + // This caller address will be used with `start_prank` cheatcode to mock the caller address., + ContractAddress, + // Address of the `MarketFactory` contract. + ContractAddress, + // Address of the `RoleStore` contract. + ContractAddress, + // Address of the `DataStore` contract. + ContractAddress, + // The `MarketToken` class hash for the factory. + ContractClass, + // Interface to interact with the `MarketFactory` contract. + IMarketFactoryDispatcher, + // Interface to interact with the `RoleStore` contract. + IRoleStoreDispatcher, + // Interface to interact with the `DataStore` contract. + IDataStoreDispatcher, + // Interface to interact with the `EventEmitter` contract. + IEventEmitterDispatcher, + // Interface to interact with the `ExchangeRouter` contract. + IExchangeRouterDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositHandlerDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositVaultDispatcher, + IOracleDispatcher, + IOrderHandlerDispatcher, + IOrderVaultDispatcher, + IReaderDispatcher, + IReferralStorageDispatcher, + IWithdrawalHandlerDispatcher, + IWithdrawalVaultDispatcher, +) { + let ( + caller_address, + market_factory_address, + role_store_address, + data_store_address, + market_token_class_hash, + market_factory, + role_store, + data_store, + event_emitter, + exchange_router, + deposit_handler, + deposit_vault, + oracle, + order_handler, + order_vault, + reader, + referal_storage, + withdrawal_handler, + withdrawal_vault, + ) = + setup_contracts(); + grant_roles_and_prank(caller_address, role_store, data_store, market_factory); + ( + caller_address, + market_factory.contract_address, + role_store_address, + data_store_address, + market_token_class_hash, + market_factory, + role_store, + data_store, + event_emitter, + exchange_router, + deposit_handler, + deposit_vault, + oracle, + order_handler, + order_vault, + reader, + referal_storage, + withdrawal_handler, + withdrawal_vault, + ) +} + +// Utility function to grant roles and prank the caller address. +/// Grants roles and pranks the caller address. +/// +/// # Arguments +/// +/// * `caller_address` - The address of the caller. +/// * `role_store` - The interface to interact with the `RoleStore` contract. +/// * `data_store` - The interface to interact with the `DataStore` contract. +/// * `market_factory` - The interface to interact with the `MarketFactory` contract. +fn grant_roles_and_prank( + caller_address: ContractAddress, + role_store: IRoleStoreDispatcher, + data_store: IDataStoreDispatcher, + market_factory: IMarketFactoryDispatcher, +) { + start_prank(role_store.contract_address, caller_address); + + // Grant the caller the `CONTROLLER` role. + role_store.grant_role(caller_address, role::CONTROLLER); + + // Grant the call the `MARKET_KEEPER` role. + // This role is required to create a market. + role_store.grant_role(caller_address, role::MARKET_KEEPER); + + // Prank the caller address for calls to `DataStore` contract. + // We need this so that the caller has the CONTROLLER role. + start_prank(data_store.contract_address, caller_address); + + // Start pranking the `MarketFactory` contract. This is necessary to mock the behavior of the contract + // for testing purposes. + start_prank(market_factory.contract_address, caller_address); +} + +/// Utility function to teardown the test environment. +fn teardown(data_store: IDataStoreDispatcher, market_factory: IMarketFactoryDispatcher) { + stop_prank(data_store.contract_address); + stop_prank(market_factory.contract_address); +} + +/// Setup required contracts. +fn setup_contracts() -> ( + // This caller address will be used with `start_prank` cheatcode to mock the caller address., + ContractAddress, + // Address of the `MarketFactory` contract. + ContractAddress, + // Address of the `RoleStore` contract. + ContractAddress, + // Address of the `DataStore` contract. + ContractAddress, + // The `MarketToken` class hash for the factory. + ContractClass, + // Interface to interact with the `MarketFactory` contract. + IMarketFactoryDispatcher, + // Interface to interact with the `RoleStore` contract. + IRoleStoreDispatcher, + // Interface to interact with the `DataStore` contract. + IDataStoreDispatcher, + // Interface to interact with the `EventEmitter` contract. + IEventEmitterDispatcher, + // Interface to interact with the `ExchangeRouter` contract. + IExchangeRouterDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositHandlerDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositVaultDispatcher, + IOracleDispatcher, + IOrderHandlerDispatcher, + IOrderVaultDispatcher, + IReaderDispatcher, + IReferralStorageDispatcher, + IWithdrawalHandlerDispatcher, + IWithdrawalVaultDispatcher, +) { + // Deploy the role store contract. + let role_store_address = deploy_role_store(); + + // Create a role store dispatcher. + let role_store = IRoleStoreDispatcher { contract_address: role_store_address }; + + // Deploy the contract. + let data_store_address = deploy_data_store(role_store_address); + // Create a safe dispatcher to interact with the contract. + let data_store = IDataStoreDispatcher { contract_address: data_store_address }; + + // Declare the `MarketToken` contract. + let market_token_class_hash = declare_market_token(); + + // Deploy the event emitter contract. + let event_emitter_address = deploy_event_emitter(); + // Create a safe dispatcher to interact with the contract. + let event_emitter = IEventEmitterDispatcher { contract_address: event_emitter_address }; + + // Deploy the router contract. + let router_address = deploy_router(role_store_address); + + // Deploy the market factory. + let market_factory_address = deploy_market_factory( + data_store_address, role_store_address, event_emitter_address, market_token_class_hash + ); + // Create a safe dispatcher to interact with the contract. + let market_factory = IMarketFactoryDispatcher { contract_address: market_factory_address }; + + let oracle_store_address = deploy_oracle_store(role_store_address, event_emitter_address); + let oracle_address = deploy_oracle( + role_store_address, oracle_store_address, contract_address_const::<'pragma'>() + ); + + let oracle = IOracleDispatcher { contract_address: oracle_address }; + + let deposit_vault_address = deploy_deposit_vault(role_store_address, data_store_address); + + let deposit_vault = IDepositVaultDispatcher { contract_address: deposit_vault_address }; + let deposit_handler_address = deploy_deposit_handler( + data_store_address, + role_store_address, + event_emitter_address, + deposit_vault_address, + oracle_address + ); + let deposit_handler = IDepositHandlerDispatcher { contract_address: deposit_handler_address }; + + let withdrawal_vault_address = deploy_withdrawal_vault(data_store_address, role_store_address); + let withdrawal_handler_address = deploy_withdrawal_handler( + data_store_address, + role_store_address, + event_emitter_address, + withdrawal_vault_address, + oracle_address + ); + + let order_vault_address = deploy_order_vault( + data_store.contract_address, role_store.contract_address + ); + let order_vault = IOrderVaultDispatcher { contract_address: order_vault_address }; + + let swap_handler_address = deploy_swap_handler_address(role_store_address, data_store_address); + let referral_storage_address = deploy_referral_storage(event_emitter_address); + let order_handler_address = deploy_order_handler( + data_store_address, + role_store_address, + event_emitter_address, + order_vault_address, + oracle_address, + swap_handler_address, + referral_storage_address + ); + let order_handler = IOrderHandlerDispatcher { contract_address: order_handler_address }; + + let exchange_router_address = deploy_exchange_router( + router_address, + data_store_address, + role_store_address, + event_emitter_address, + deposit_handler_address, + withdrawal_handler_address, + order_handler_address + ); + let exchange_router = IExchangeRouterDispatcher { contract_address: exchange_router_address }; + + let bank_address = deploy_bank(data_store_address, role_store_address); + + //Create a safe dispatcher to interact with the Bank contract. + let bank = IBankDispatcher { contract_address: bank_address }; + + // Deploy the strict bank contract + let strict_bank_address = deploy_strict_bank(data_store_address, role_store_address); + + //Create a safe dispatcher to interact with the StrictBank contract. + let strict_bank = IStrictBankDispatcher { contract_address: strict_bank_address }; + + let reader_address = deploy_reader(); + let reader = IReaderDispatcher { contract_address: reader_address }; + + let referal_storage = IReferralStorageDispatcher { contract_address: referral_storage_address }; + + let withdrawal_handler = IWithdrawalHandlerDispatcher { + contract_address: withdrawal_handler_address + }; + let withdrawal_vault = IWithdrawalVaultDispatcher { + contract_address: withdrawal_vault_address + }; + ( + contract_address_const::<'caller'>(), + market_factory_address, + role_store_address, + data_store_address, + market_token_class_hash, + market_factory, + role_store, + data_store, + event_emitter, + exchange_router, + deposit_handler, + deposit_vault, + oracle, + order_handler, + order_vault, + reader, + referal_storage, + withdrawal_handler, + withdrawal_vault, + ) +} + +/// Utility function to declare a `MarketToken` contract. +fn declare_market_token() -> ContractClass { + declare('MarketToken') +} + +/// Utility function to deploy a market factory contract and return its address. +fn deploy_market_factory( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + market_token_class_hash: ContractClass, +) -> ContractAddress { + let contract = declare('MarketFactory'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'market_factory'>(); + start_prank(deployed_contract_address, caller_address); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + constructor_calldata.append(event_emitter_address.into()); + constructor_calldata.append(market_token_class_hash.class_hash.into()); + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + + +fn deploy_data_store(role_store_address: ContractAddress) -> ContractAddress { + let contract = declare('DataStore'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address: ContractAddress = 0x1.try_into().unwrap(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_role_store() -> ContractAddress { + let contract = declare('RoleStore'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'role_store'>(); + start_prank(deployed_contract_address, caller_address); + contract.deploy_at(@array![caller_address.into()], deployed_contract_address).unwrap() +} + +fn deploy_event_emitter() -> ContractAddress { + let contract = declare('EventEmitter'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'event_emitter'>(); + start_prank(deployed_contract_address, caller_address); + contract.deploy_at(@array![], deployed_contract_address).unwrap() +} + +fn deploy_router(role_store_address: ContractAddress) -> ContractAddress { + let contract = declare('Router'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'router'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_deposit_handler( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + deposit_vault_address: ContractAddress, + oracle_address: ContractAddress +) -> ContractAddress { + let contract = declare('DepositHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'deposit_handler'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![ + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + deposit_vault_address.into(), + oracle_address.into() + ], + deployed_contract_address + ) + .unwrap() +} + +fn deploy_oracle_store( + role_store_address: ContractAddress, event_emitter_address: ContractAddress, +) -> ContractAddress { + let contract = declare('OracleStore'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'oracle_store'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![role_store_address.into(), event_emitter_address.into()], + deployed_contract_address + ) + .unwrap() +} + +fn deploy_oracle( + role_store_address: ContractAddress, + oracle_store_address: ContractAddress, + pragma_address: ContractAddress +) -> ContractAddress { + let contract = declare('Oracle'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'oracle'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![role_store_address.into(), oracle_store_address.into(), pragma_address.into()], + deployed_contract_address + ) + .unwrap() +} + +fn deploy_deposit_vault( + role_store_address: ContractAddress, data_store_address: ContractAddress +) -> ContractAddress { + let contract = declare('DepositVault'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'deposit_vault'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![data_store_address.into(), role_store_address.into()], deployed_contract_address + ) + .unwrap() +} + +fn deploy_withdrawal_handler( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + withdrawal_vault_address: ContractAddress, + oracle_address: ContractAddress +) -> ContractAddress { + let contract = declare('WithdrawalHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'withdrawal_handler'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![ + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + withdrawal_vault_address.into(), + oracle_address.into() + ]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_withdrawal_vault( + data_store_address: ContractAddress, role_store_address: ContractAddress +) -> ContractAddress { + let contract = declare('WithdrawalVault'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'withdrawal_vault'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![data_store_address.into(), role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_order_handler( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + order_vault_address: ContractAddress, + oracle_address: ContractAddress, + swap_handler_address: ContractAddress, + referral_storage_address: ContractAddress +) -> ContractAddress { + let contract = declare('OrderHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'order_handler'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![ + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + order_vault_address.into(), + oracle_address.into(), + swap_handler_address.into(), + referral_storage_address.into() + ]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_swap_handler_address( + role_store_address: ContractAddress, data_store_address: ContractAddress +) -> ContractAddress { + let contract = declare('SwapHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'swap_handler'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_referral_storage(event_emitter_address: ContractAddress) -> ContractAddress { + let contract = declare('ReferralStorage'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'referral_storage'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![event_emitter_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_exchange_router( + router_address: ContractAddress, + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + deposit_handler_address: ContractAddress, + withdrawal_handler_address: ContractAddress, + order_handler_address: ContractAddress +) -> ContractAddress { + let contract = declare('ExchangeRouter'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'exchange_router'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![ + router_address.into(), + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + deposit_handler_address.into(), + withdrawal_handler_address.into(), + order_handler_address.into() + ]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_order_vault( + data_store_address: ContractAddress, role_store_address: ContractAddress, +) -> ContractAddress { + let contract = declare('OrderVault'); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + tests_lib::deploy_mock_contract(contract, @constructor_calldata) +} + +fn deploy_bank( + data_store_address: ContractAddress, role_store_address: ContractAddress, +) -> ContractAddress { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let bank_address: ContractAddress = contract_address_const::<'bank'>(); + let contract = declare('Bank'); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + start_prank(data_store_address, caller_address); + contract.deploy_at(@constructor_calldata, bank_address).unwrap() +} + +fn deploy_strict_bank( + data_store_address: ContractAddress, role_store_address: ContractAddress, +) -> ContractAddress { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let strict_bank_address: ContractAddress = contract_address_const::<'strict_bank'>(); + let contract = declare('StrictBank'); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + start_prank(strict_bank_address, caller_address); + contract.deploy_at(@constructor_calldata, strict_bank_address).unwrap() +} + +fn deploy_reader() -> ContractAddress { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let reader_address: ContractAddress = contract_address_const::<'reader'>(); + let contract = declare('Reader'); + let mut constructor_calldata = array![]; + start_prank(reader_address, caller_address); + contract.deploy_at(@constructor_calldata, reader_address).unwrap() +} + +fn deploy_erc20_token(deposit_vault_address: ContractAddress) -> ContractAddress { + let erc20_contract = declare('ERC20'); + let constructor_calldata3 = array![ + 'satoru', 'STU', INITIAL_TOKENS_MINTED, 0, deposit_vault_address.into() + ]; + erc20_contract.deploy(@constructor_calldata3).unwrap() +} diff --git a/tests/integration/test_short_integration.cairo b/tests/integration/test_short_integration.cairo new file mode 100644 index 00000000..1f8adcbc --- /dev/null +++ b/tests/integration/test_short_integration.cairo @@ -0,0 +1,965 @@ +// ************************************************************************* +// IMPORTS +// ************************************************************************* + +// Core lib imports. + +use result::ResultTrait; +use debug::PrintTrait; +use traits::{TryInto, Into}; +use starknet::{ + ContractAddress, get_caller_address, Felt252TryIntoContractAddress, contract_address_const, + ClassHash, +}; +use snforge_std::{declare, start_prank, stop_prank, start_roll, ContractClassTrait, ContractClass}; + + +// Local imports. +use satoru::data::data_store::{IDataStoreDispatcher, IDataStoreDispatcherTrait}; +use satoru::role::role_store::{IRoleStoreDispatcher, IRoleStoreDispatcherTrait}; +use satoru::market::market_factory::{IMarketFactoryDispatcher, IMarketFactoryDispatcherTrait}; +use satoru::event::event_emitter::{IEventEmitterDispatcher, IEventEmitterDispatcherTrait}; +use satoru::deposit::deposit_vault::{IDepositVaultDispatcher, IDepositVaultDispatcherTrait}; +use satoru::deposit::deposit::Deposit; +use satoru::withdrawal::withdrawal::Withdrawal; + +use satoru::exchange::withdrawal_handler::{ + IWithdrawalHandlerDispatcher, IWithdrawalHandlerDispatcherTrait +}; +use satoru::exchange::deposit_handler::{IDepositHandlerDispatcher, IDepositHandlerDispatcherTrait}; +use satoru::router::exchange_router::{IExchangeRouterDispatcher, IExchangeRouterDispatcherTrait}; +use satoru::mock::referral_storage::{IReferralStorageDispatcher, IReferralStorageDispatcherTrait}; +use satoru::reader::reader::{IReaderDispatcher, IReaderDispatcherTrait}; +use satoru::market::market::{Market, UniqueIdMarket}; +use satoru::market::market_token::{IMarketTokenDispatcher, IMarketTokenDispatcherTrait}; +use satoru::role::role; +use satoru::oracle::oracle_utils::SetPricesParams; +use satoru::tests_lib; +use satoru::deposit::deposit_utils::CreateDepositParams; +use satoru::utils::span32::{Span32, DefaultSpan32, Array32Trait}; +use satoru::deposit::deposit_utils; +use satoru::bank::bank::{IBankDispatcherTrait, IBankDispatcher}; +use satoru::bank::strict_bank::{IStrictBankDispatcher, IStrictBankDispatcherTrait}; +use satoru::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; +use satoru::oracle::oracle::{IOracleDispatcher, IOracleDispatcherTrait}; +use satoru::withdrawal::withdrawal_vault::{ + IWithdrawalVaultDispatcher, IWithdrawalVaultDispatcherTrait +}; +use satoru::data::keys; +use satoru::market::market_utils; +use satoru::price::price::{Price, PriceTrait}; +use satoru::position::position_utils; +use satoru::withdrawal::withdrawal_utils; + +use satoru::order::order::{Order, OrderType, SecondaryOrderType, DecreasePositionSwapType}; +use satoru::order::order_vault::{IOrderVaultDispatcher, IOrderVaultDispatcherTrait}; +use satoru::order::base_order_utils::{CreateOrderParams}; +use satoru::oracle::oracle_store::{IOracleStoreDispatcher, IOracleStoreDispatcherTrait}; +use satoru::swap::swap_handler::{ISwapHandlerDispatcher, ISwapHandlerDispatcherTrait}; +use satoru::market::{market::{UniqueIdMarketImpl},}; +use satoru::exchange::order_handler::{ + OrderHandler, IOrderHandlerDispatcher, IOrderHandlerDispatcherTrait +}; +const INITIAL_TOKENS_MINTED: felt252 = 1000; + + +// #[test] +// fn test_short_market_integration() { +// // ********************************************************************************************* +// // * SETUP * +// // ********************************************************************************************* +// let ( +// caller_address, +// market_factory_address, +// role_store_address, +// data_store_address, +// market_token_class_hash, +// market_factory, +// role_store, +// data_store, +// event_emitter, +// exchange_router, +// deposit_handler, +// deposit_vault, +// oracle, +// order_handler, +// order_vault, +// ) = +// setup(); + +// // ********************************************************************************************* +// // * TEST LOGIC * +// // ********************************************************************************************* + +// // Create a market. +// let market = data_store.get_market(create_market(market_factory)); + +// // Set params in data_store +// data_store.set_address(keys::fee_token(), market.index_token); +// data_store.set_u256(keys::max_swap_path_length(), 5); + +// // Set max pool amount. +// data_store +// .set_u256( +// keys::max_pool_amount_key(market.market_token, market.long_token), 500000000000000000 +// ); +// data_store +// .set_u256( +// keys::max_pool_amount_key(market.market_token, market.short_token), 500000000000000000 +// ); + +// // Fill the pool. +// IERC20Dispatcher { contract_address: market.long_token }.mint(market.market_token, 50000000000); +// IERC20Dispatcher { contract_address: market.short_token } +// .mint(market.market_token, 50000000000); +// // TODO Check why we don't need to set pool_amount_key +// // // Set pool amount in data_store. +// // let mut key = keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()); +// // data_store.set_u256(key, 50000000000); +// // key = keys::pool_amount_key(market.market_token, contract_address_const::<'USDC'>()); +// // data_store.set_u256(key, 50000000000); + +// // Send token to deposit in the deposit vault (this should be in a multi call with create_deposit) +// IERC20Dispatcher { contract_address: market.long_token } +// .mint(deposit_vault.contract_address, 50000000000); +// IERC20Dispatcher { contract_address: market.short_token } +// .mint(deposit_vault.contract_address, 50000000000); + +// let balance_deposit_vault_before = IERC20Dispatcher { contract_address: market.short_token } +// .balance_of(deposit_vault.contract_address); + +// // Create Deposit +// let user1: ContractAddress = contract_address_const::<'user1'>(); +// let user2: ContractAddress = contract_address_const::<'user2'>(); + +// let addresss_zero: ContractAddress = 0.try_into().unwrap(); + +// let params = CreateDepositParams { +// receiver: user1, +// callback_contract: user2, +// ui_fee_receiver: addresss_zero, +// market: market.market_token, +// initial_long_token: market.long_token, +// initial_short_token: market.short_token, +// long_token_swap_path: Array32Trait::::span32(@array![]), +// short_token_swap_path: Array32Trait::::span32(@array![]), +// min_market_tokens: 0, +// execution_fee: 0, +// callback_gas_limit: 0, +// }; + +// start_roll(deposit_handler.contract_address, 1910); +// let key = deposit_handler.create_deposit(caller_address, params); +// let first_deposit = data_store.get_deposit(key); + +// assert(first_deposit.account == caller_address, 'Wrong account depositer'); +// assert(first_deposit.receiver == user1, 'Wrong account receiver'); +// assert(first_deposit.initial_long_token == market.long_token, 'Wrong initial long token'); +// assert( +// first_deposit.initial_long_token_amount == 50000000000, 'Wrong initial long token amount' +// ); +// assert( +// first_deposit.initial_short_token_amount == 50000000000, 'Wrong init short token amount' +// ); + +// let price_params = SetPricesParams { // TODO +// signer_info: 1, +// tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], +// compacted_min_oracle_block_numbers: array![1900, 1900], +// compacted_max_oracle_block_numbers: array![1910, 1910], +// compacted_oracle_timestamps: array![9999, 9999], +// compacted_decimals: array![18, 18], +// compacted_min_prices: array![4294967346000000], // 50000000, 1000000 compacted +// compacted_min_prices_indexes: array![0], +// compacted_max_prices: array![4294967346000000], // 50000000, 1000000 compacted +// compacted_max_prices_indexes: array![0], +// signatures: array![ +// array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() +// ], +// price_feed_tokens: array![] +// }; + +// start_prank(role_store.contract_address, caller_address); + +// role_store.grant_role(caller_address, role::ORDER_KEEPER); +// role_store.grant_role(caller_address, role::ROLE_ADMIN); +// role_store.grant_role(caller_address, role::CONTROLLER); +// role_store.grant_role(caller_address, role::MARKET_KEEPER); + +// // Execute Deposit +// start_roll(deposit_handler.contract_address, 1915); +// deposit_handler.execute_deposit(key, price_params); + +// let pool_value_info = market_utils::get_pool_value_info( +// data_store, +// market, +// Price { min: 1999, max: 2000 }, +// Price { min: 1999, max: 2000 }, +// Price { min: 1999, max: 2000 }, +// keys::max_pnl_factor_for_deposits(), +// true, +// ); + +// assert(pool_value_info.pool_value.mag == 200000000000000, 'wrong pool value amount'); +// assert(pool_value_info.long_token_amount == 50000000000, 'wrong long token amount'); +// assert(pool_value_info.short_token_amount == 50000000000, 'wrong short token amount'); + +// let not_deposit = data_store.get_deposit(key); +// let default_deposit: Deposit = Default::default(); +// assert(not_deposit == default_deposit, 'Still existing deposit'); + +// // let market_token_dispatcher = IMarketTokenDispatcher { contract_address: market.market_token }; + +// // let balance = market_token_dispatcher.balance_of(user1); + +// let balance_deposit_vault = IERC20Dispatcher { contract_address: market.short_token } +// .balance_of(deposit_vault.contract_address); + +// let pool_value_info = market_utils::get_pool_value_info( +// data_store, +// market, +// Price { +// min: 5000, +// max: 5000, +// } +// , +// Price { +// min: 5000, +// max: 5000, +// }, +// Price { +// min: 1, +// max: 1, +// }, +// keys::max_pnl_factor_for_deposits(), +// true, +// ); + +// pool_value_info.pool_value.mag.print(); +// pool_value_info.long_token_amount.print(); +// pool_value_info.short_token_amount.print(); + +// // ************************************* TEST SHORT ********************************************* + +// 'begining of SHORT TEST'.print(); + +// let key_open_interest = keys::open_interest_key(market.market_token, contract_address_const::<'ETH'>(), true); +// data_store.set_u256(key_open_interest, 1); +// let max_key_open_interest = keys::max_open_interest_key(market.market_token, true); +// data_store.set_u256(max_key_open_interest, 10000); + +// start_prank(contract_address_const::<'ETH'>(), caller_address); +// // Send token to order_vault in multicall with create_order +// IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } +// .transfer(order_vault.contract_address, 1); + +// 'transfer made'.print(); +// // Create order_params Struct +// let contract_address = contract_address_const::<0>(); +// start_prank(market.market_token, caller_address); +// start_prank(market.long_token, caller_address); +// let order_params_short = CreateOrderParams { +// receiver: caller_address, +// callback_contract: contract_address, +// ui_fee_receiver: contract_address, +// market: market.market_token, +// initial_collateral_token: market.long_token, +// swap_path: Array32Trait::::span32(@array![market.market_token]), +// size_delta_usd: 5000, +// initial_collateral_delta_amount: 5000, // 10^18 +// trigger_price: 5000, +// acceptable_price: 5500, +// execution_fee: 0, +// callback_gas_limit: 0, +// min_output_amount: 0, +// order_type: OrderType::MarketDecrease(()), +// decrease_position_swap_type: DecreasePositionSwapType::NoSwap(()), +// is_long: false, +// referral_code: 0 +// }; +// // Create the swap order. +// start_roll(order_handler.contract_address, 1930); +// 'try to create prder'.print(); +// start_prank(order_handler.contract_address, caller_address); +// let key_short = order_handler.create_order(caller_address, order_params_short); +// 'short created'.print(); +// let got_order_short = data_store.get_order(key_short); +// // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'USDC'>()), ); +// // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()), 1000000); +// // Execute the swap order. + +// let signatures: Span = array![0].span(); +// let set_price_params = SetPricesParams { +// signer_info: 2, +// tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], +// compacted_min_oracle_block_numbers: array![1910, 1910], +// compacted_max_oracle_block_numbers: array![1920, 1920], +// compacted_oracle_timestamps: array![9999, 9999], +// compacted_decimals: array![1, 1], +// compacted_min_prices: array![2147483648010000], // 500000, 10000 compacted +// compacted_min_prices_indexes: array![0], +// compacted_max_prices: array![2147483648010000], // 500000, 10000 compacted +// compacted_max_prices_indexes: array![0], +// signatures: array![ +// array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() +// ], +// price_feed_tokens: array![] +// }; + +// let keeper_address = contract_address_const::<'keeper'>(); +// role_store.grant_role(keeper_address, role::ORDER_KEEPER); + +// stop_prank(order_handler.contract_address); +// start_prank(order_handler.contract_address, keeper_address); +// start_roll(order_handler.contract_address, 1935); +// // TODO add real signatures check on Oracle Account +// order_handler.execute_order_keeper(key_short, set_price_params, keeper_address); +// 'short position SUCCEEDED'.print(); +// let position_key = position_utils::get_position_key(caller_address, market.market_token, contract_address_const::<'USDC'>(), true); +// let first_position = data_store.get_position(position_key); +// let market_prices = market_utils::MarketPrices { +// index_token_price: Price { +// min: 8000, +// max: 8000, +// }, +// long_token_price: Price { +// min: 8000, +// max: 8000, +// }, +// short_token_price: Price { +// min: 1, +// max: 1, +// }, +// }; +// 'size tokens'.print(); +// first_position.size_in_tokens.print(); +// 'size in usd'.print(); +// first_position.size_in_usd.print(); + +// let second_swap_pool_value_info = market_utils::get_pool_value_info( +// data_store, +// market, +// Price { +// min: 5000, +// max: 5000, +// } +// , +// Price { +// min: 5000, +// max: 5000, +// }, +// Price { +// min: 1, +// max: 1, +// }, +// keys::max_pnl_factor_for_deposits(), +// true, +// ); + +// second_swap_pool_value_info.pool_value.mag.print(); +// second_swap_pool_value_info.long_token_amount.print(); +// second_swap_pool_value_info.short_token_amount.print(); +// // let (position_pnl_usd, uncapped_position_pnl_usd, size_delta_in_tokens) = +// // position_utils::get_position_pnl_usd( +// // data_store, market, market_prices, first_position, 5000 +// // ); +// // position_pnl_usd.mag.print(); + +// // ********************************************************************************************* +// // * TEARDOWN * +// // ********************************************************************************************* +// teardown(data_store, market_factory); +// } + +fn create_market(market_factory: IMarketFactoryDispatcher) -> ContractAddress { + // Create a market. + let (index_token, short_token) = deploy_tokens(); + let market_type = 'market_type'; + + // Index token is the same as long token here. + market_factory.create_market(index_token, index_token, short_token, market_type) +} + +/// Utility functions to deploy tokens for a market. +fn deploy_tokens() -> (ContractAddress, ContractAddress) { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let contract = declare('ERC20'); + + let eth_address = contract_address_const::<'ETH'>(); + let constructor_calldata = array!['Ethereum', 'ETH', 1000000, 0, caller_address.into()]; + contract.deploy_at(@constructor_calldata, eth_address).unwrap(); + + let usdc_address = contract_address_const::<'USDC'>(); + let constructor_calldata = array!['usdc', 'USDC', 1000000, 0, caller_address.into()]; + contract.deploy_at(@constructor_calldata, usdc_address).unwrap(); + (eth_address, usdc_address) +} + +/// Utility function to setup the test environment. +fn setup() -> ( + // This caller address will be used with `start_prank` cheatcode to mock the caller address., + ContractAddress, + // Address of the `MarketFactory` contract. + ContractAddress, + // Address of the `RoleStore` contract. + ContractAddress, + // Address of the `DataStore` contract. + ContractAddress, + // The `MarketToken` class hash for the factory. + ContractClass, + // Interface to interact with the `MarketFactory` contract. + IMarketFactoryDispatcher, + // Interface to interact with the `RoleStore` contract. + IRoleStoreDispatcher, + // Interface to interact with the `DataStore` contract. + IDataStoreDispatcher, + // Interface to interact with the `EventEmitter` contract. + IEventEmitterDispatcher, + // Interface to interact with the `ExchangeRouter` contract. + IExchangeRouterDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositHandlerDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositVaultDispatcher, + IOracleDispatcher, + IOrderHandlerDispatcher, + IOrderVaultDispatcher, + IReaderDispatcher, + IReferralStorageDispatcher, + IWithdrawalHandlerDispatcher, + IWithdrawalVaultDispatcher, +) { + let ( + caller_address, + market_factory_address, + role_store_address, + data_store_address, + market_token_class_hash, + market_factory, + role_store, + data_store, + event_emitter, + exchange_router, + deposit_handler, + deposit_vault, + oracle, + order_handler, + order_vault, + reader, + referal_storage, + withdrawal_handler, + withdrawal_vault, + ) = + setup_contracts(); + grant_roles_and_prank(caller_address, role_store, data_store, market_factory); + ( + caller_address, + market_factory.contract_address, + role_store_address, + data_store_address, + market_token_class_hash, + market_factory, + role_store, + data_store, + event_emitter, + exchange_router, + deposit_handler, + deposit_vault, + oracle, + order_handler, + order_vault, + reader, + referal_storage, + withdrawal_handler, + withdrawal_vault, + ) +} + +// Utility function to grant roles and prank the caller address. +/// Grants roles and pranks the caller address. +/// +/// # Arguments +/// +/// * `caller_address` - The address of the caller. +/// * `role_store` - The interface to interact with the `RoleStore` contract. +/// * `data_store` - The interface to interact with the `DataStore` contract. +/// * `market_factory` - The interface to interact with the `MarketFactory` contract. +fn grant_roles_and_prank( + caller_address: ContractAddress, + role_store: IRoleStoreDispatcher, + data_store: IDataStoreDispatcher, + market_factory: IMarketFactoryDispatcher, +) { + start_prank(role_store.contract_address, caller_address); + + // Grant the caller the `CONTROLLER` role. + role_store.grant_role(caller_address, role::CONTROLLER); + + // Grant the call the `MARKET_KEEPER` role. + // This role is required to create a market. + role_store.grant_role(caller_address, role::MARKET_KEEPER); + + // Prank the caller address for calls to `DataStore` contract. + // We need this so that the caller has the CONTROLLER role. + start_prank(data_store.contract_address, caller_address); + + // Start pranking the `MarketFactory` contract. This is necessary to mock the behavior of the contract + // for testing purposes. + start_prank(market_factory.contract_address, caller_address); +} + +/// Utility function to teardown the test environment. +fn teardown(data_store: IDataStoreDispatcher, market_factory: IMarketFactoryDispatcher) { + stop_prank(data_store.contract_address); + stop_prank(market_factory.contract_address); +} + +/// Setup required contracts. +fn setup_contracts() -> ( + // This caller address will be used with `start_prank` cheatcode to mock the caller address., + ContractAddress, + // Address of the `MarketFactory` contract. + ContractAddress, + // Address of the `RoleStore` contract. + ContractAddress, + // Address of the `DataStore` contract. + ContractAddress, + // The `MarketToken` class hash for the factory. + ContractClass, + // Interface to interact with the `MarketFactory` contract. + IMarketFactoryDispatcher, + // Interface to interact with the `RoleStore` contract. + IRoleStoreDispatcher, + // Interface to interact with the `DataStore` contract. + IDataStoreDispatcher, + // Interface to interact with the `EventEmitter` contract. + IEventEmitterDispatcher, + // Interface to interact with the `ExchangeRouter` contract. + IExchangeRouterDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositHandlerDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositVaultDispatcher, + IOracleDispatcher, + IOrderHandlerDispatcher, + IOrderVaultDispatcher, + IReaderDispatcher, + IReferralStorageDispatcher, + IWithdrawalHandlerDispatcher, + IWithdrawalVaultDispatcher, +) { + // Deploy the role store contract. + let role_store_address = deploy_role_store(); + + // Create a role store dispatcher. + let role_store = IRoleStoreDispatcher { contract_address: role_store_address }; + + // Deploy the contract. + let data_store_address = deploy_data_store(role_store_address); + // Create a safe dispatcher to interact with the contract. + let data_store = IDataStoreDispatcher { contract_address: data_store_address }; + + // Declare the `MarketToken` contract. + let market_token_class_hash = declare_market_token(); + + // Deploy the event emitter contract. + let event_emitter_address = deploy_event_emitter(); + // Create a safe dispatcher to interact with the contract. + let event_emitter = IEventEmitterDispatcher { contract_address: event_emitter_address }; + + // Deploy the router contract. + let router_address = deploy_router(role_store_address); + + // Deploy the market factory. + let market_factory_address = deploy_market_factory( + data_store_address, role_store_address, event_emitter_address, market_token_class_hash + ); + // Create a safe dispatcher to interact with the contract. + let market_factory = IMarketFactoryDispatcher { contract_address: market_factory_address }; + + let oracle_store_address = deploy_oracle_store(role_store_address, event_emitter_address); + let oracle_address = deploy_oracle( + role_store_address, oracle_store_address, contract_address_const::<'pragma'>() + ); + + let oracle = IOracleDispatcher { contract_address: oracle_address }; + + let deposit_vault_address = deploy_deposit_vault(role_store_address, data_store_address); + + let deposit_vault = IDepositVaultDispatcher { contract_address: deposit_vault_address }; + let deposit_handler_address = deploy_deposit_handler( + data_store_address, + role_store_address, + event_emitter_address, + deposit_vault_address, + oracle_address + ); + let deposit_handler = IDepositHandlerDispatcher { contract_address: deposit_handler_address }; + + let withdrawal_vault_address = deploy_withdrawal_vault(data_store_address, role_store_address); + let withdrawal_handler_address = deploy_withdrawal_handler( + data_store_address, + role_store_address, + event_emitter_address, + withdrawal_vault_address, + oracle_address + ); + + let order_vault_address = deploy_order_vault( + data_store.contract_address, role_store.contract_address + ); + let order_vault = IOrderVaultDispatcher { contract_address: order_vault_address }; + + let swap_handler_address = deploy_swap_handler_address(role_store_address, data_store_address); + let referral_storage_address = deploy_referral_storage(event_emitter_address); + let order_handler_address = deploy_order_handler( + data_store_address, + role_store_address, + event_emitter_address, + order_vault_address, + oracle_address, + swap_handler_address, + referral_storage_address + ); + let order_handler = IOrderHandlerDispatcher { contract_address: order_handler_address }; + + let exchange_router_address = deploy_exchange_router( + router_address, + data_store_address, + role_store_address, + event_emitter_address, + deposit_handler_address, + withdrawal_handler_address, + order_handler_address + ); + let exchange_router = IExchangeRouterDispatcher { contract_address: exchange_router_address }; + + let bank_address = deploy_bank(data_store_address, role_store_address); + + //Create a safe dispatcher to interact with the Bank contract. + let bank = IBankDispatcher { contract_address: bank_address }; + + // Deploy the strict bank contract + let strict_bank_address = deploy_strict_bank(data_store_address, role_store_address); + + //Create a safe dispatcher to interact with the StrictBank contract. + let strict_bank = IStrictBankDispatcher { contract_address: strict_bank_address }; + + let reader_address = deploy_reader(); + let reader = IReaderDispatcher { contract_address: reader_address }; + + let referal_storage = IReferralStorageDispatcher { contract_address: referral_storage_address }; + + let withdrawal_handler = IWithdrawalHandlerDispatcher { + contract_address: withdrawal_handler_address + }; + let withdrawal_vault = IWithdrawalVaultDispatcher { + contract_address: withdrawal_vault_address + }; + ( + contract_address_const::<'caller'>(), + market_factory_address, + role_store_address, + data_store_address, + market_token_class_hash, + market_factory, + role_store, + data_store, + event_emitter, + exchange_router, + deposit_handler, + deposit_vault, + oracle, + order_handler, + order_vault, + reader, + referal_storage, + withdrawal_handler, + withdrawal_vault, + ) +} + +/// Utility function to declare a `MarketToken` contract. +fn declare_market_token() -> ContractClass { + declare('MarketToken') +} + +/// Utility function to deploy a market factory contract and return its address. +fn deploy_market_factory( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + market_token_class_hash: ContractClass, +) -> ContractAddress { + let contract = declare('MarketFactory'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'market_factory'>(); + start_prank(deployed_contract_address, caller_address); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + constructor_calldata.append(event_emitter_address.into()); + constructor_calldata.append(market_token_class_hash.class_hash.into()); + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + + +fn deploy_data_store(role_store_address: ContractAddress) -> ContractAddress { + let contract = declare('DataStore'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address: ContractAddress = 0x1.try_into().unwrap(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_role_store() -> ContractAddress { + let contract = declare('RoleStore'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'role_store'>(); + start_prank(deployed_contract_address, caller_address); + contract.deploy_at(@array![caller_address.into()], deployed_contract_address).unwrap() +} + +fn deploy_event_emitter() -> ContractAddress { + let contract = declare('EventEmitter'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'event_emitter'>(); + start_prank(deployed_contract_address, caller_address); + contract.deploy_at(@array![], deployed_contract_address).unwrap() +} + +fn deploy_router(role_store_address: ContractAddress) -> ContractAddress { + let contract = declare('Router'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'router'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_deposit_handler( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + deposit_vault_address: ContractAddress, + oracle_address: ContractAddress +) -> ContractAddress { + let contract = declare('DepositHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'deposit_handler'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![ + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + deposit_vault_address.into(), + oracle_address.into() + ], + deployed_contract_address + ) + .unwrap() +} + +fn deploy_oracle_store( + role_store_address: ContractAddress, event_emitter_address: ContractAddress, +) -> ContractAddress { + let contract = declare('OracleStore'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'oracle_store'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![role_store_address.into(), event_emitter_address.into()], + deployed_contract_address + ) + .unwrap() +} + +fn deploy_oracle( + role_store_address: ContractAddress, + oracle_store_address: ContractAddress, + pragma_address: ContractAddress +) -> ContractAddress { + let contract = declare('Oracle'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'oracle'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![role_store_address.into(), oracle_store_address.into(), pragma_address.into()], + deployed_contract_address + ) + .unwrap() +} + +fn deploy_deposit_vault( + role_store_address: ContractAddress, data_store_address: ContractAddress +) -> ContractAddress { + let contract = declare('DepositVault'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'deposit_vault'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![data_store_address.into(), role_store_address.into()], deployed_contract_address + ) + .unwrap() +} + +fn deploy_withdrawal_handler( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + withdrawal_vault_address: ContractAddress, + oracle_address: ContractAddress +) -> ContractAddress { + let contract = declare('WithdrawalHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'withdrawal_handler'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![ + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + withdrawal_vault_address.into(), + oracle_address.into() + ]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_withdrawal_vault( + data_store_address: ContractAddress, role_store_address: ContractAddress +) -> ContractAddress { + let contract = declare('WithdrawalVault'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'withdrawal_vault'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![data_store_address.into(), role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_order_handler( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + order_vault_address: ContractAddress, + oracle_address: ContractAddress, + swap_handler_address: ContractAddress, + referral_storage_address: ContractAddress +) -> ContractAddress { + let contract = declare('OrderHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'order_handler'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![ + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + order_vault_address.into(), + oracle_address.into(), + swap_handler_address.into(), + referral_storage_address.into() + ]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_swap_handler_address( + role_store_address: ContractAddress, data_store_address: ContractAddress +) -> ContractAddress { + let contract = declare('SwapHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'swap_handler'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_referral_storage(event_emitter_address: ContractAddress) -> ContractAddress { + let contract = declare('ReferralStorage'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'referral_storage'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![event_emitter_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_exchange_router( + router_address: ContractAddress, + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + deposit_handler_address: ContractAddress, + withdrawal_handler_address: ContractAddress, + order_handler_address: ContractAddress +) -> ContractAddress { + let contract = declare('ExchangeRouter'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'exchange_router'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![ + router_address.into(), + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + deposit_handler_address.into(), + withdrawal_handler_address.into(), + order_handler_address.into() + ]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_order_vault( + data_store_address: ContractAddress, role_store_address: ContractAddress, +) -> ContractAddress { + let contract = declare('OrderVault'); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + tests_lib::deploy_mock_contract(contract, @constructor_calldata) +} + +fn deploy_bank( + data_store_address: ContractAddress, role_store_address: ContractAddress, +) -> ContractAddress { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let bank_address: ContractAddress = contract_address_const::<'bank'>(); + let contract = declare('Bank'); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + start_prank(data_store_address, caller_address); + contract.deploy_at(@constructor_calldata, bank_address).unwrap() +} + +fn deploy_strict_bank( + data_store_address: ContractAddress, role_store_address: ContractAddress, +) -> ContractAddress { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let strict_bank_address: ContractAddress = contract_address_const::<'strict_bank'>(); + let contract = declare('StrictBank'); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + start_prank(strict_bank_address, caller_address); + contract.deploy_at(@constructor_calldata, strict_bank_address).unwrap() +} + +fn deploy_reader() -> ContractAddress { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let reader_address: ContractAddress = contract_address_const::<'reader'>(); + let contract = declare('Reader'); + let mut constructor_calldata = array![]; + start_prank(reader_address, caller_address); + contract.deploy_at(@constructor_calldata, reader_address).unwrap() +} + +fn deploy_erc20_token(deposit_vault_address: ContractAddress) -> ContractAddress { + let erc20_contract = declare('ERC20'); + let constructor_calldata3 = array![ + 'satoru', 'STU', INITIAL_TOKENS_MINTED, 0, deposit_vault_address.into() + ]; + erc20_contract.deploy(@constructor_calldata3).unwrap() +} diff --git a/tests/integration/test_swap_integration.cairo b/tests/integration/test_swap_integration.cairo new file mode 100644 index 00000000..6cbc685f --- /dev/null +++ b/tests/integration/test_swap_integration.cairo @@ -0,0 +1,955 @@ +// ************************************************************************* +// IMPORTS +// ************************************************************************* + +// Core lib imports. + +use result::ResultTrait; +use debug::PrintTrait; +use traits::{TryInto, Into}; +use starknet::{ + ContractAddress, get_caller_address, Felt252TryIntoContractAddress, contract_address_const, + ClassHash, +}; +use snforge_std::{declare, start_prank, stop_prank, start_roll, ContractClassTrait, ContractClass}; + + +// Local imports. +use satoru::data::data_store::{IDataStoreDispatcher, IDataStoreDispatcherTrait}; +use satoru::role::role_store::{IRoleStoreDispatcher, IRoleStoreDispatcherTrait}; +use satoru::market::market_factory::{IMarketFactoryDispatcher, IMarketFactoryDispatcherTrait}; +use satoru::event::event_emitter::{IEventEmitterDispatcher, IEventEmitterDispatcherTrait}; +use satoru::deposit::deposit_vault::{IDepositVaultDispatcher, IDepositVaultDispatcherTrait}; +use satoru::deposit::deposit::Deposit; +use satoru::withdrawal::withdrawal::Withdrawal; + +use satoru::exchange::withdrawal_handler::{ + IWithdrawalHandlerDispatcher, IWithdrawalHandlerDispatcherTrait +}; +use satoru::exchange::deposit_handler::{IDepositHandlerDispatcher, IDepositHandlerDispatcherTrait}; +use satoru::router::exchange_router::{IExchangeRouterDispatcher, IExchangeRouterDispatcherTrait}; +use satoru::mock::referral_storage::{IReferralStorageDispatcher, IReferralStorageDispatcherTrait}; +use satoru::reader::reader::{IReaderDispatcher, IReaderDispatcherTrait}; +use satoru::market::market::{Market, UniqueIdMarket}; +use satoru::market::market_token::{IMarketTokenDispatcher, IMarketTokenDispatcherTrait}; +use satoru::role::role; +use satoru::oracle::oracle_utils::SetPricesParams; +use satoru::tests_lib; +use satoru::deposit::deposit_utils::CreateDepositParams; +use satoru::utils::span32::{Span32, DefaultSpan32, Array32Trait}; +use satoru::deposit::deposit_utils; +use satoru::bank::bank::{IBankDispatcherTrait, IBankDispatcher}; +use satoru::bank::strict_bank::{IStrictBankDispatcher, IStrictBankDispatcherTrait}; +use satoru::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; +use satoru::oracle::oracle::{IOracleDispatcher, IOracleDispatcherTrait}; +use satoru::withdrawal::withdrawal_vault::{ + IWithdrawalVaultDispatcher, IWithdrawalVaultDispatcherTrait +}; +use satoru::data::keys; +use satoru::market::market_utils; +use satoru::price::price::{Price, PriceTrait}; +use satoru::position::position_utils; +use satoru::withdrawal::withdrawal_utils; + +use satoru::order::order::{Order, OrderType, SecondaryOrderType, DecreasePositionSwapType}; +use satoru::order::order_vault::{IOrderVaultDispatcher, IOrderVaultDispatcherTrait}; +use satoru::order::base_order_utils::{CreateOrderParams}; +use satoru::oracle::oracle_store::{IOracleStoreDispatcher, IOracleStoreDispatcherTrait}; +use satoru::swap::swap_handler::{ISwapHandlerDispatcher, ISwapHandlerDispatcherTrait}; +use satoru::market::{market::{UniqueIdMarketImpl},}; +use satoru::exchange::order_handler::{ + OrderHandler, IOrderHandlerDispatcher, IOrderHandlerDispatcherTrait +}; +const INITIAL_TOKENS_MINTED: felt252 = 1000; + +// #[test] +// fn test_swap_market_integration() { +// // ********************************************************************************************* +// // * SETUP * +// // ********************************************************************************************* +// let ( +// caller_address, +// market_factory_address, +// role_store_address, +// data_store_address, +// market_token_class_hash, +// market_factory, +// role_store, +// data_store, +// event_emitter, +// exchange_router, +// deposit_handler, +// deposit_vault, +// oracle, +// order_handler, +// order_vault, +// reader, +// referal_storage, +// ) = +// setup(); + +// // ********************************************************************************************* +// // * TEST LOGIC * +// // ********************************************************************************************* + +// // Create a market. +// let market = data_store.get_market(create_market(market_factory)); + +// // Set params in data_store +// data_store.set_address(keys::fee_token(), market.index_token); +// data_store.set_u256(keys::max_swap_path_length(), 5); + +// // Set max pool amount. +// data_store +// .set_u256( +// keys::max_pool_amount_key(market.market_token, market.long_token), 500000000000000000 +// ); +// data_store +// .set_u256( +// keys::max_pool_amount_key(market.market_token, market.short_token), 500000000000000000 +// ); + +// oracle.set_price_testing_eth(5000); + +// // Fill the pool. +// IERC20Dispatcher { contract_address: market.long_token }.mint(market.market_token, 50000000000); +// IERC20Dispatcher { contract_address: market.short_token } +// .mint(market.market_token, 50000000000); +// // TODO Check why we don't need to set pool_amount_key +// // // Set pool amount in data_store. +// // let mut key = keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()); +// // data_store.set_u256(key, 50000000000); +// // key = keys::pool_amount_key(market.market_token, contract_address_const::<'USDC'>()); +// // data_store.set_u256(key, 50000000000); + +// // Send token to deposit in the deposit vault (this should be in a multi call with create_deposit) +// IERC20Dispatcher { contract_address: market.long_token } +// .mint(deposit_vault.contract_address, 50000000000); +// IERC20Dispatcher { contract_address: market.short_token } +// .mint(deposit_vault.contract_address, 50000000000); + +// let balance_deposit_vault_before = IERC20Dispatcher { contract_address: market.short_token } +// .balance_of(deposit_vault.contract_address); + +// // Create Deposit +// let user1: ContractAddress = contract_address_const::<'user1'>(); +// let user2: ContractAddress = contract_address_const::<'user2'>(); + +// let addresss_zero: ContractAddress = 0.try_into().unwrap(); + +// let params = CreateDepositParams { +// receiver: user1, +// callback_contract: user2, +// ui_fee_receiver: addresss_zero, +// market: market.market_token, +// initial_long_token: market.long_token, +// initial_short_token: market.short_token, +// long_token_swap_path: Array32Trait::::span32(@array![]), +// short_token_swap_path: Array32Trait::::span32(@array![]), +// min_market_tokens: 0, +// execution_fee: 0, +// callback_gas_limit: 0, +// }; + +// start_roll(deposit_handler.contract_address, 1910); +// let key = deposit_handler.create_deposit(caller_address, params); +// let first_deposit = data_store.get_deposit(key); + +// assert(first_deposit.account == caller_address, 'Wrong account depositer'); +// assert(first_deposit.receiver == user1, 'Wrong account receiver'); +// assert(first_deposit.initial_long_token == market.long_token, 'Wrong initial long token'); +// assert( +// first_deposit.initial_long_token_amount == 50000000000, 'Wrong initial long token amount' +// ); +// assert( +// first_deposit.initial_short_token_amount == 50000000000, 'Wrong init short token amount' +// ); + +// let price_params = SetPricesParams { // TODO +// signer_info: 1, +// tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], +// compacted_min_oracle_block_numbers: array![1900, 1900], +// compacted_max_oracle_block_numbers: array![1910, 1910], +// compacted_oracle_timestamps: array![9999, 9999], +// compacted_decimals: array![18, 18], +// compacted_min_prices: array![4294967346000000], // 50000000, 1000000 compacted +// compacted_min_prices_indexes: array![0], +// compacted_max_prices: array![4294967346000000], // 50000000, 1000000 compacted +// compacted_max_prices_indexes: array![0], +// signatures: array![ +// array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() +// ], +// price_feed_tokens: array![] +// }; + +// start_prank(role_store.contract_address, caller_address); + +// role_store.grant_role(caller_address, role::ORDER_KEEPER); +// role_store.grant_role(caller_address, role::ROLE_ADMIN); +// role_store.grant_role(caller_address, role::CONTROLLER); +// role_store.grant_role(caller_address, role::MARKET_KEEPER); + +// // Execute Deposit +// start_roll(deposit_handler.contract_address, 1915); +// deposit_handler.execute_deposit(key, price_params); + +// let pool_value_info = market_utils::get_pool_value_info( +// data_store, +// market, +// Price { min: 1999, max: 2000 }, +// Price { min: 1999, max: 2000 }, +// Price { min: 1999, max: 2000 }, +// keys::max_pnl_factor_for_deposits(), +// true, +// ); + +// assert(pool_value_info.pool_value.mag == 200000000000000, 'wrong pool value amount'); +// assert(pool_value_info.long_token_amount == 50000000000, 'wrong long token amount'); +// assert(pool_value_info.short_token_amount == 50000000000, 'wrong short token amount'); + +// let not_deposit = data_store.get_deposit(key); +// let default_deposit: Deposit = Default::default(); +// assert(not_deposit == default_deposit, 'Still existing deposit'); + +// // let market_token_dispatcher = IMarketTokenDispatcher { contract_address: market.market_token }; + +// // let balance = market_token_dispatcher.balance_of(user1); + +// let balance_deposit_vault = IERC20Dispatcher { contract_address: market.short_token } +// .balance_of(deposit_vault.contract_address); + +// let pool_value_info = market_utils::get_pool_value_info( +// data_store, +// market, +// Price { min: 5000, max: 5000, }, +// Price { min: 5000, max: 5000, }, +// Price { min: 1, max: 1, }, +// keys::max_pnl_factor_for_deposits(), +// true, +// ); + +// pool_value_info.pool_value.mag.print(); +// pool_value_info.long_token_amount.print(); +// pool_value_info.short_token_amount.print(); + +// // // --------------------SWAP TEST USDC->ETH -------------------- + +// let balance_ETH_before_swap = IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } +// .balance_of(caller_address); +// assert(balance_ETH_before_swap == 1000000, 'wrong balance ETH before swap'); + +// let balance_USDC_before_swap = IERC20Dispatcher { contract_address: contract_address_const::<'USDC'>() } +// .balance_of(caller_address); +// assert(balance_USDC_before_swap == 1000000, 'wrong balance USDC before swap'); + +// start_prank(contract_address_const::<'ETH'>(), caller_address); //change to switch swap +// // Send token to order_vault in multicall with create_order +// IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } //change to switch swap +// .transfer(order_vault.contract_address, 1); + +// let balance_ETH_before = IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } +// .balance_of(caller_address); +// let balance_USDC_before = IERC20Dispatcher { contract_address: contract_address_const::<'USDC'>() } +// .balance_of(caller_address); +// 'balance ETH: '.print(); +// balance_ETH_before.print(); +// 'balance USDC: '.print(); +// balance_USDC_before.print(); +// 'end first balances'.print(); +// // Create order_params Struct +// let contract_address = contract_address_const::<0>(); +// start_prank(market.long_token, caller_address); //change to switch swap +// let order_params = CreateOrderParams { +// receiver: caller_address, +// callback_contract: contract_address, +// ui_fee_receiver: contract_address, +// market: contract_address, +// initial_collateral_token: market.long_token, //change to switch swap +// swap_path: Array32Trait::::span32(@array![market.market_token]), +// size_delta_usd: 1, +// initial_collateral_delta_amount: 1, // 10^18 +// trigger_price: 0, +// acceptable_price: 0, +// execution_fee: 0, +// callback_gas_limit: 0, +// min_output_amount: 0, +// order_type: OrderType::MarketSwap(()), +// decrease_position_swap_type: DecreasePositionSwapType::NoSwap(()), +// is_long: false, +// referral_code: 0 +// }; +// // Create the swap order. +// start_roll(order_handler.contract_address, 1920); +// let key = order_handler.create_order(caller_address, order_params); + +// let got_order = data_store.get_order(key); +// // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'USDC'>()), ); +// // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()), 1000000); +// // Execute the swap order. +// let signatures: Span = array![0].span(); +// let set_price_params = SetPricesParams { +// signer_info: 2, +// tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], +// compacted_min_oracle_block_numbers: array![1910, 1910], +// compacted_max_oracle_block_numbers: array![1920, 1920], +// compacted_oracle_timestamps: array![9999, 9999], +// compacted_decimals: array![1, 1], +// compacted_min_prices: array![2147483648010000], // 500000, 10000 compacted +// compacted_min_prices_indexes: array![0], +// compacted_max_prices: array![2147483648010000], // 500000, 10000 compacted +// compacted_max_prices_indexes: array![0], +// signatures: array![ +// array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() +// ], +// price_feed_tokens: array![] +// }; + +// let balance_ETH_before_execute = IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } +// .balance_of(caller_address); +// let balance_USDC_before_execute = IERC20Dispatcher { contract_address: contract_address_const::<'USDC'>() } +// .balance_of(caller_address); + +// 'balance eth before execute'.print(); +// balance_ETH_before_execute.print(); +// // assert(balance_ETH_after == 999999, 'wrong balance ETH after swap'); +// 'balance usdc before execute'.print(); +// balance_USDC_before_execute.print(); + +// let keeper_address = contract_address_const::<'keeper'>(); +// role_store.grant_role(keeper_address, role::ORDER_KEEPER); + +// stop_prank(order_handler.contract_address); +// start_prank(order_handler.contract_address, keeper_address); +// start_roll(order_handler.contract_address, 1925); +// // TODO add real signatures check on Oracle Account +// order_handler.execute_order_keeper(key, set_price_params, keeper_address); + +// let balance_ETH_after = IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } +// .balance_of(caller_address); +// let balance_USDC_after = IERC20Dispatcher { contract_address: contract_address_const::<'USDC'>() } +// .balance_of(caller_address); + +// 'balance eth after'.print(); +// balance_ETH_after.print(); +// // assert(balance_ETH_after == 999999, 'wrong balance ETH after swap'); +// 'balance usdc after'.print(); +// balance_USDC_after.print(); +// // assert(balance_USDC_after == 995000, 'wrong balance USDC after swap'); + +// let first_swap_pool_value_info = market_utils::get_pool_value_info( +// data_store, +// market, +// Price { +// min: 5000, +// max: 5000, +// } +// , +// Price { +// min: 5000, +// max: 5000, +// }, +// Price { +// min: 1, +// max: 1, +// }, +// keys::max_pnl_factor_for_deposits(), +// true, +// ); + +// first_swap_pool_value_info.pool_value.mag.print(); +// first_swap_pool_value_info.long_token_amount.print(); +// first_swap_pool_value_info.short_token_amount.print(); +// } + +fn create_market(market_factory: IMarketFactoryDispatcher) -> ContractAddress { + // Create a market. + let (index_token, short_token) = deploy_tokens(); + let market_type = 'market_type'; + + // Index token is the same as long token here. + market_factory.create_market(index_token, index_token, short_token, market_type) +} + +/// Utility functions to deploy tokens for a market. +fn deploy_tokens() -> (ContractAddress, ContractAddress) { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let contract = declare('ERC20'); + + let eth_address = contract_address_const::<'ETH'>(); + let constructor_calldata = array!['Ethereum', 'ETH', 1000000, 0, caller_address.into()]; + contract.deploy_at(@constructor_calldata, eth_address).unwrap(); + + let usdc_address = contract_address_const::<'USDC'>(); + let constructor_calldata = array!['usdc', 'USDC', 1000000, 0, caller_address.into()]; + contract.deploy_at(@constructor_calldata, usdc_address).unwrap(); + (eth_address, usdc_address) +} + +/// Utility function to setup the test environment. +fn setup() -> ( + // This caller address will be used with `start_prank` cheatcode to mock the caller address., + ContractAddress, + // Address of the `MarketFactory` contract. + ContractAddress, + // Address of the `RoleStore` contract. + ContractAddress, + // Address of the `DataStore` contract. + ContractAddress, + // The `MarketToken` class hash for the factory. + ContractClass, + // Interface to interact with the `MarketFactory` contract. + IMarketFactoryDispatcher, + // Interface to interact with the `RoleStore` contract. + IRoleStoreDispatcher, + // Interface to interact with the `DataStore` contract. + IDataStoreDispatcher, + // Interface to interact with the `EventEmitter` contract. + IEventEmitterDispatcher, + // Interface to interact with the `ExchangeRouter` contract. + IExchangeRouterDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositHandlerDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositVaultDispatcher, + IOracleDispatcher, + IOrderHandlerDispatcher, + IOrderVaultDispatcher, + IReaderDispatcher, + IReferralStorageDispatcher, + IWithdrawalHandlerDispatcher, + IWithdrawalVaultDispatcher, +) { + let ( + caller_address, + market_factory_address, + role_store_address, + data_store_address, + market_token_class_hash, + market_factory, + role_store, + data_store, + event_emitter, + exchange_router, + deposit_handler, + deposit_vault, + oracle, + order_handler, + order_vault, + reader, + referal_storage, + withdrawal_handler, + withdrawal_vault, + ) = + setup_contracts(); + grant_roles_and_prank(caller_address, role_store, data_store, market_factory); + ( + caller_address, + market_factory.contract_address, + role_store_address, + data_store_address, + market_token_class_hash, + market_factory, + role_store, + data_store, + event_emitter, + exchange_router, + deposit_handler, + deposit_vault, + oracle, + order_handler, + order_vault, + reader, + referal_storage, + withdrawal_handler, + withdrawal_vault, + ) +} + +// Utility function to grant roles and prank the caller address. +/// Grants roles and pranks the caller address. +/// +/// # Arguments +/// +/// * `caller_address` - The address of the caller. +/// * `role_store` - The interface to interact with the `RoleStore` contract. +/// * `data_store` - The interface to interact with the `DataStore` contract. +/// * `market_factory` - The interface to interact with the `MarketFactory` contract. +fn grant_roles_and_prank( + caller_address: ContractAddress, + role_store: IRoleStoreDispatcher, + data_store: IDataStoreDispatcher, + market_factory: IMarketFactoryDispatcher, +) { + start_prank(role_store.contract_address, caller_address); + + // Grant the caller the `CONTROLLER` role. + role_store.grant_role(caller_address, role::CONTROLLER); + + // Grant the call the `MARKET_KEEPER` role. + // This role is required to create a market. + role_store.grant_role(caller_address, role::MARKET_KEEPER); + + // Prank the caller address for calls to `DataStore` contract. + // We need this so that the caller has the CONTROLLER role. + start_prank(data_store.contract_address, caller_address); + + // Start pranking the `MarketFactory` contract. This is necessary to mock the behavior of the contract + // for testing purposes. + start_prank(market_factory.contract_address, caller_address); +} + +/// Utility function to teardown the test environment. +fn teardown(data_store: IDataStoreDispatcher, market_factory: IMarketFactoryDispatcher) { + stop_prank(data_store.contract_address); + stop_prank(market_factory.contract_address); +} + +/// Setup required contracts. +fn setup_contracts() -> ( + // This caller address will be used with `start_prank` cheatcode to mock the caller address., + ContractAddress, + // Address of the `MarketFactory` contract. + ContractAddress, + // Address of the `RoleStore` contract. + ContractAddress, + // Address of the `DataStore` contract. + ContractAddress, + // The `MarketToken` class hash for the factory. + ContractClass, + // Interface to interact with the `MarketFactory` contract. + IMarketFactoryDispatcher, + // Interface to interact with the `RoleStore` contract. + IRoleStoreDispatcher, + // Interface to interact with the `DataStore` contract. + IDataStoreDispatcher, + // Interface to interact with the `EventEmitter` contract. + IEventEmitterDispatcher, + // Interface to interact with the `ExchangeRouter` contract. + IExchangeRouterDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositHandlerDispatcher, + // Interface to interact with the `DepositHandler` contract. + IDepositVaultDispatcher, + IOracleDispatcher, + IOrderHandlerDispatcher, + IOrderVaultDispatcher, + IReaderDispatcher, + IReferralStorageDispatcher, + IWithdrawalHandlerDispatcher, + IWithdrawalVaultDispatcher, +) { + // Deploy the role store contract. + let role_store_address = deploy_role_store(); + + // Create a role store dispatcher. + let role_store = IRoleStoreDispatcher { contract_address: role_store_address }; + + // Deploy the contract. + let data_store_address = deploy_data_store(role_store_address); + // Create a safe dispatcher to interact with the contract. + let data_store = IDataStoreDispatcher { contract_address: data_store_address }; + + // Declare the `MarketToken` contract. + let market_token_class_hash = declare_market_token(); + + // Deploy the event emitter contract. + let event_emitter_address = deploy_event_emitter(); + // Create a safe dispatcher to interact with the contract. + let event_emitter = IEventEmitterDispatcher { contract_address: event_emitter_address }; + + // Deploy the router contract. + let router_address = deploy_router(role_store_address); + + // Deploy the market factory. + let market_factory_address = deploy_market_factory( + data_store_address, role_store_address, event_emitter_address, market_token_class_hash + ); + // Create a safe dispatcher to interact with the contract. + let market_factory = IMarketFactoryDispatcher { contract_address: market_factory_address }; + + let oracle_store_address = deploy_oracle_store(role_store_address, event_emitter_address); + let oracle_address = deploy_oracle( + role_store_address, oracle_store_address, contract_address_const::<'pragma'>() + ); + + let oracle = IOracleDispatcher { contract_address: oracle_address }; + + let deposit_vault_address = deploy_deposit_vault(role_store_address, data_store_address); + + let deposit_vault = IDepositVaultDispatcher { contract_address: deposit_vault_address }; + let deposit_handler_address = deploy_deposit_handler( + data_store_address, + role_store_address, + event_emitter_address, + deposit_vault_address, + oracle_address + ); + let deposit_handler = IDepositHandlerDispatcher { contract_address: deposit_handler_address }; + + let withdrawal_vault_address = deploy_withdrawal_vault(data_store_address, role_store_address); + let withdrawal_handler_address = deploy_withdrawal_handler( + data_store_address, + role_store_address, + event_emitter_address, + withdrawal_vault_address, + oracle_address + ); + + let order_vault_address = deploy_order_vault( + data_store.contract_address, role_store.contract_address + ); + let order_vault = IOrderVaultDispatcher { contract_address: order_vault_address }; + + let swap_handler_address = deploy_swap_handler_address(role_store_address, data_store_address); + let referral_storage_address = deploy_referral_storage(event_emitter_address); + let order_handler_address = deploy_order_handler( + data_store_address, + role_store_address, + event_emitter_address, + order_vault_address, + oracle_address, + swap_handler_address, + referral_storage_address + ); + let order_handler = IOrderHandlerDispatcher { contract_address: order_handler_address }; + + let exchange_router_address = deploy_exchange_router( + router_address, + data_store_address, + role_store_address, + event_emitter_address, + deposit_handler_address, + withdrawal_handler_address, + order_handler_address + ); + let exchange_router = IExchangeRouterDispatcher { contract_address: exchange_router_address }; + + let bank_address = deploy_bank(data_store_address, role_store_address); + + //Create a safe dispatcher to interact with the Bank contract. + let bank = IBankDispatcher { contract_address: bank_address }; + + // Deploy the strict bank contract + let strict_bank_address = deploy_strict_bank(data_store_address, role_store_address); + + //Create a safe dispatcher to interact with the StrictBank contract. + let strict_bank = IStrictBankDispatcher { contract_address: strict_bank_address }; + + let reader_address = deploy_reader(); + let reader = IReaderDispatcher { contract_address: reader_address }; + + let referal_storage = IReferralStorageDispatcher { contract_address: referral_storage_address }; + + let withdrawal_handler = IWithdrawalHandlerDispatcher { + contract_address: withdrawal_handler_address + }; + let withdrawal_vault = IWithdrawalVaultDispatcher { + contract_address: withdrawal_vault_address + }; + ( + contract_address_const::<'caller'>(), + market_factory_address, + role_store_address, + data_store_address, + market_token_class_hash, + market_factory, + role_store, + data_store, + event_emitter, + exchange_router, + deposit_handler, + deposit_vault, + oracle, + order_handler, + order_vault, + reader, + referal_storage, + withdrawal_handler, + withdrawal_vault, + ) +} + +/// Utility function to declare a `MarketToken` contract. +fn declare_market_token() -> ContractClass { + declare('MarketToken') +} + +/// Utility function to deploy a market factory contract and return its address. +fn deploy_market_factory( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + market_token_class_hash: ContractClass, +) -> ContractAddress { + let contract = declare('MarketFactory'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'market_factory'>(); + start_prank(deployed_contract_address, caller_address); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + constructor_calldata.append(event_emitter_address.into()); + constructor_calldata.append(market_token_class_hash.class_hash.into()); + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + + +fn deploy_data_store(role_store_address: ContractAddress) -> ContractAddress { + let contract = declare('DataStore'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address: ContractAddress = 0x1.try_into().unwrap(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_role_store() -> ContractAddress { + let contract = declare('RoleStore'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'role_store'>(); + start_prank(deployed_contract_address, caller_address); + contract.deploy_at(@array![caller_address.into()], deployed_contract_address).unwrap() +} + +fn deploy_event_emitter() -> ContractAddress { + let contract = declare('EventEmitter'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'event_emitter'>(); + start_prank(deployed_contract_address, caller_address); + contract.deploy_at(@array![], deployed_contract_address).unwrap() +} + +fn deploy_router(role_store_address: ContractAddress) -> ContractAddress { + let contract = declare('Router'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'router'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_deposit_handler( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + deposit_vault_address: ContractAddress, + oracle_address: ContractAddress +) -> ContractAddress { + let contract = declare('DepositHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'deposit_handler'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![ + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + deposit_vault_address.into(), + oracle_address.into() + ], + deployed_contract_address + ) + .unwrap() +} + +fn deploy_oracle_store( + role_store_address: ContractAddress, event_emitter_address: ContractAddress, +) -> ContractAddress { + let contract = declare('OracleStore'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'oracle_store'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![role_store_address.into(), event_emitter_address.into()], + deployed_contract_address + ) + .unwrap() +} + +fn deploy_oracle( + role_store_address: ContractAddress, + oracle_store_address: ContractAddress, + pragma_address: ContractAddress +) -> ContractAddress { + let contract = declare('Oracle'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'oracle'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![role_store_address.into(), oracle_store_address.into(), pragma_address.into()], + deployed_contract_address + ) + .unwrap() +} + +fn deploy_deposit_vault( + role_store_address: ContractAddress, data_store_address: ContractAddress +) -> ContractAddress { + let contract = declare('DepositVault'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'deposit_vault'>(); + start_prank(deployed_contract_address, caller_address); + contract + .deploy_at( + @array![data_store_address.into(), role_store_address.into()], deployed_contract_address + ) + .unwrap() +} + +fn deploy_withdrawal_handler( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + withdrawal_vault_address: ContractAddress, + oracle_address: ContractAddress +) -> ContractAddress { + let contract = declare('WithdrawalHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'withdrawal_handler'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![ + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + withdrawal_vault_address.into(), + oracle_address.into() + ]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_withdrawal_vault( + data_store_address: ContractAddress, role_store_address: ContractAddress +) -> ContractAddress { + let contract = declare('WithdrawalVault'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'withdrawal_vault'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![data_store_address.into(), role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_order_handler( + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + order_vault_address: ContractAddress, + oracle_address: ContractAddress, + swap_handler_address: ContractAddress, + referral_storage_address: ContractAddress +) -> ContractAddress { + let contract = declare('OrderHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'order_handler'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![ + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + order_vault_address.into(), + oracle_address.into(), + swap_handler_address.into(), + referral_storage_address.into() + ]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_swap_handler_address( + role_store_address: ContractAddress, data_store_address: ContractAddress +) -> ContractAddress { + let contract = declare('SwapHandler'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'swap_handler'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![role_store_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_referral_storage(event_emitter_address: ContractAddress) -> ContractAddress { + let contract = declare('ReferralStorage'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'referral_storage'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![event_emitter_address.into()]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_exchange_router( + router_address: ContractAddress, + data_store_address: ContractAddress, + role_store_address: ContractAddress, + event_emitter_address: ContractAddress, + deposit_handler_address: ContractAddress, + withdrawal_handler_address: ContractAddress, + order_handler_address: ContractAddress +) -> ContractAddress { + let contract = declare('ExchangeRouter'); + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let deployed_contract_address = contract_address_const::<'exchange_router'>(); + start_prank(deployed_contract_address, caller_address); + let constructor_calldata = array![ + router_address.into(), + data_store_address.into(), + role_store_address.into(), + event_emitter_address.into(), + deposit_handler_address.into(), + withdrawal_handler_address.into(), + order_handler_address.into() + ]; + contract.deploy_at(@constructor_calldata, deployed_contract_address).unwrap() +} + +fn deploy_order_vault( + data_store_address: ContractAddress, role_store_address: ContractAddress, +) -> ContractAddress { + let contract = declare('OrderVault'); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + tests_lib::deploy_mock_contract(contract, @constructor_calldata) +} + +fn deploy_bank( + data_store_address: ContractAddress, role_store_address: ContractAddress, +) -> ContractAddress { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let bank_address: ContractAddress = contract_address_const::<'bank'>(); + let contract = declare('Bank'); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + start_prank(data_store_address, caller_address); + contract.deploy_at(@constructor_calldata, bank_address).unwrap() +} + +fn deploy_strict_bank( + data_store_address: ContractAddress, role_store_address: ContractAddress, +) -> ContractAddress { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let strict_bank_address: ContractAddress = contract_address_const::<'strict_bank'>(); + let contract = declare('StrictBank'); + let mut constructor_calldata = array![]; + constructor_calldata.append(data_store_address.into()); + constructor_calldata.append(role_store_address.into()); + start_prank(strict_bank_address, caller_address); + contract.deploy_at(@constructor_calldata, strict_bank_address).unwrap() +} + +fn deploy_reader() -> ContractAddress { + let caller_address: ContractAddress = contract_address_const::<'caller'>(); + let reader_address: ContractAddress = contract_address_const::<'reader'>(); + let contract = declare('Reader'); + let mut constructor_calldata = array![]; + start_prank(reader_address, caller_address); + contract.deploy_at(@constructor_calldata, reader_address).unwrap() +} + +fn deploy_erc20_token(deposit_vault_address: ContractAddress) -> ContractAddress { + let erc20_contract = declare('ERC20'); + let constructor_calldata3 = array![ + 'satoru', 'STU', INITIAL_TOKENS_MINTED, 0, deposit_vault_address.into() + ]; + erc20_contract.deploy(@constructor_calldata3).unwrap() +} diff --git a/tests/lib.cairo b/tests/lib.cairo index a03336a0..21a586ec 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -122,6 +122,9 @@ // mod test_create_and_execute_swap; mod integration { - mod create_market; + mod test_deposit_withdrawal; + mod test_long_integration; + mod test_short_integration; + mod test_swap_integration; //mod swap_test; } From 9fcdc0eae0c1e9f59e42480d8096ab833ed1df78 Mon Sep 17 00:00:00 2001 From: sparqet Date: Mon, 24 Jun 2024 17:36:04 +0200 Subject: [PATCH 2/2] trigger increase & decrease long --- tests/integration/test_long_integration.cairo | 649 +++++++++++++++++- 1 file changed, 648 insertions(+), 1 deletion(-) diff --git a/tests/integration/test_long_integration.cairo b/tests/integration/test_long_integration.cairo index 898e6fd4..ab6cf397 100644 --- a/tests/integration/test_long_integration.cairo +++ b/tests/integration/test_long_integration.cairo @@ -1097,7 +1097,7 @@ const INITIAL_TOKENS_MINTED: felt252 = 1000; // } #[test] -fn test_long_18_decrease_close_integration() { +fn test_long_increase_decrease_close() { // ********************************************************************************************* // * SETUP * // ********************************************************************************************* @@ -1740,6 +1740,653 @@ fn test_long_18_decrease_close_integration() { teardown(data_store, market_factory); } +#[test] +fn test_takeprofit_long() { + // ********************************************************************************************* + // * SETUP * + // ********************************************************************************************* + let ( + caller_address, + market_factory_address, + role_store_address, + data_store_address, + market_token_class_hash, + market_factory, + role_store, + data_store, + event_emitter, + exchange_router, + deposit_handler, + deposit_vault, + oracle, + order_handler, + order_vault, + reader, + referal_storage, + withdrawal_handler, + withdrawal_vault, + liquidation_handler, + ) = + setup(); + + // ********************************************************************************************* + // * TEST LOGIC * + // ********************************************************************************************* + + // Create a market. + let market = data_store.get_market(create_market(market_factory)); + + // Set params in data_store + data_store.set_address(keys::fee_token(), market.index_token); + data_store.set_u256(keys::max_swap_path_length(), 5); + + // Set max pool amount. + data_store + .set_u256( + keys::max_pool_amount_key(market.market_token, market.long_token), + 5000000000000000000000000000000000000000000 //500 000 ETH + ); + data_store + .set_u256( + keys::max_pool_amount_key(market.market_token, market.short_token), + 2500000000000000000000000000000000000000000000 //250 000 000 USDC + ); + + let factor_for_deposits: felt252 = keys::max_pnl_factor_for_deposits(); + data_store + .set_u256( + keys::max_pnl_factor_key(factor_for_deposits, market.market_token, true), + 50000000000000000000000000000000000000000000000 + ); + let factor_for_withdrawal: felt252 = keys::max_pnl_factor_for_withdrawals(); + data_store + .set_u256( + keys::max_pnl_factor_key(factor_for_withdrawal, market.market_token, true), + 50000000000000000000000000000000000000000000000 + ); + data_store.set_u256(keys::reserve_factor_key(market.market_token, true), 1000000000000000000); + data_store + .set_u256( + keys::open_interest_reserve_factor_key(market.market_token, true), 1000000000000000000 + ); + + data_store.set_bool('REENTRANCY_GUARD_STATUS', false); + + 'fill the pool'.print(); + // Fill the pool. + IERC20Dispatcher { contract_address: market.long_token } + .mint(market.market_token, 50000000000000000000000000000000000000); // 5 ETH + IERC20Dispatcher { contract_address: market.short_token } + .mint(market.market_token, 25000000000000000000000000000000000000000); // 25000 USDC + 'filled pool 1'.print(); + + IERC20Dispatcher { contract_address: market.long_token } + .mint(caller_address, 9999999999999000000); // 9.999 ETH + IERC20Dispatcher { contract_address: market.short_token } + .mint(caller_address, 49999999999999999000000); // 49.999 UDC + 'filled account'.print(); + + // INITIAL LONG TOKEN IN POOL : 5 ETH + // INITIAL SHORT TOKEN IN POOL : 25000 USDC + + // TODO Check why we don't need to set pool_amount_key + // // Set pool amount in data_store. + // let mut key = keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()); + + let balance_deposit_vault_before = IERC20Dispatcher { contract_address: market.short_token } + .balance_of(deposit_vault.contract_address); + let balance_caller_ETH = IERC20Dispatcher { contract_address: market.long_token } + .balance_of(caller_address); + let balance_caller_USDC = IERC20Dispatcher { contract_address: market.short_token } + .balance_of(caller_address); + + assert(balance_deposit_vault_before == 0, 'balance deposit should be 0'); + assert(balance_caller_ETH == 10000000000000000000, 'balanc ETH should be 10 ETH'); + assert(balance_caller_USDC == 50000000000000000000000, 'USDC be 50 000 USDC'); + + // Send token to deposit in the deposit vault (this should be in a multi call with create_deposit) + 'get balances'.print(); + // start_prank(market.long_token, caller_address); + // IERC20Dispatcher { contract_address: market.long_token } + // .transfer(deposit_vault.contract_address, 5000000000000000000); // 5 ETH + + // start_prank(market.short_token, caller_address); + // IERC20Dispatcher { contract_address: market.short_token } + // .transfer(deposit_vault.contract_address, 25000000000000000000000); // 25000 USDC + // 'make transfer'.print(); + + IERC20Dispatcher { contract_address: market.long_token } + .mint(deposit_vault.contract_address, 50000000000000000000000000000); // 50 000 000 000 + IERC20Dispatcher { contract_address: market.short_token } + .mint(deposit_vault.contract_address, 50000000000000000000000000000); // 50 000 000 000 + // Create Deposit + + let addresss_zero: ContractAddress = 0.try_into().unwrap(); + + let params = CreateDepositParams { + receiver: caller_address, + callback_contract: addresss_zero, + ui_fee_receiver: addresss_zero, + market: market.market_token, + initial_long_token: market.long_token, + initial_short_token: market.short_token, + long_token_swap_path: Array32Trait::::span32(@array![]), + short_token_swap_path: Array32Trait::::span32(@array![]), + min_market_tokens: 0, + execution_fee: 0, + callback_gas_limit: 0, + }; + 'create deposit'.print(); + + start_roll(deposit_handler.contract_address, 1910); + let key = deposit_handler.create_deposit(caller_address, params); + let first_deposit = data_store.get_deposit(key); + + 'created deposit'.print(); + + assert(first_deposit.account == caller_address, 'Wrong account depositer'); + assert(first_deposit.receiver == caller_address, 'Wrong account receiver'); + assert(first_deposit.initial_long_token == market.long_token, 'Wrong initial long token'); + assert( + first_deposit.initial_long_token_amount == 50000000000000000000000000000, + 'Wrong initial long token amount' + ); + assert( + first_deposit.initial_short_token_amount == 50000000000000000000000000000, + 'Wrong init short token amount' + ); + + let price_params = SetPricesParams { + signer_info: 0, + tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], + compacted_min_oracle_block_numbers: array![1910, 1910], + compacted_max_oracle_block_numbers: array![1920, 1920], + compacted_oracle_timestamps: array![9999, 9999], + compacted_decimals: array![1, 1], + compacted_min_prices: array![2147483648010000], // 500000, 10000 compacted + compacted_min_prices_indexes: array![0], + compacted_max_prices: array![4000, 1], // 500000, 10000 compacted + compacted_max_prices_indexes: array![0], + signatures: array![ + array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() + ], + price_feed_tokens: array![] + }; + + start_prank(role_store.contract_address, caller_address); + + role_store.grant_role(caller_address, role::ORDER_KEEPER); + role_store.grant_role(caller_address, role::ROLE_ADMIN); + role_store.grant_role(exchange_router.contract_address, role::CONTROLLER); + role_store.grant_role(caller_address, role::MARKET_KEEPER); + + 'execute deposit'.print(); + + // Execute Deposit + start_roll(deposit_handler.contract_address, 1915); + deposit_handler.execute_deposit(key, price_params); + + 'executed deposit'.print(); + + // let pool_value_info = market_utils::get_pool_value_info( + // data_store, + // market, + // Price { min: 2000, max: 2000 }, + // Price { min: 2000, max: 2000 }, + // Price { min: 2000, max: 2000 }, + // keys::max_pnl_factor_for_deposits(), + // true, + // ); + + // assert(pool_value_info.pool_value.mag == 42000000000000000000000, 'wrong pool value amount'); + // assert(pool_value_info.long_token_amount == 6000000000000000000, 'wrong long token amount'); + // assert(pool_value_info.short_token_amount == 30000000000000000000000, 'wrong short token amount'); + + let not_deposit = data_store.get_deposit(key); + let default_deposit: Deposit = Default::default(); + assert(not_deposit == default_deposit, 'Still existing deposit'); + + let market_token_dispatcher = IMarketTokenDispatcher { contract_address: market.market_token }; + let balance_market_token = market_token_dispatcher.balance_of(caller_address); + + assert(balance_market_token != 0, 'should receive market token'); + + let balance_deposit_vault_after = IERC20Dispatcher { contract_address: market.short_token } + .balance_of(deposit_vault.contract_address); + + // let pool_value_info = market_utils::get_pool_value_info( + // data_store, + // market, + // Price { min: 5000, max: 5000, }, + // Price { min: 5000, max: 5000, }, + // Price { min: 1, max: 1, }, + // keys::max_pnl_factor_for_deposits(), + // true, + // ); + + // pool_value_info.pool_value.mag.print(); // 10000 000000000000000000 + // pool_value_info.long_token_amount.print(); // 5 000000000000000000 + // pool_value_info.short_token_amount.print(); // 25000 000000000000000000 + + // ************************************* TEST LONG ********************************************* + + 'Begining of LONG TEST'.print(); + + let key_open_interest = keys::open_interest_key( + market.market_token, contract_address_const::<'ETH'>(), true + ); + data_store.set_u256(key_open_interest, 1); + let max_key_open_interest = keys::max_open_interest_key(market.market_token, true); + data_store + .set_u256( + max_key_open_interest, 1000000000000000000000000000000000000000000000000000 + ); // 1 000 000 + + // Send token to order_vault in multicall with create_order + start_prank(contract_address_const::<'ETH'>(), caller_address); + IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } + .transfer(order_vault.contract_address, 1000000000000000000); // 1ETH + + 'transfer made'.print(); + // Create order_params Struct + let contract_address = contract_address_const::<0>(); + start_prank(market.market_token, caller_address); + start_prank(market.long_token, caller_address); + let order_params_long = CreateOrderParams { + receiver: caller_address, + callback_contract: contract_address, + ui_fee_receiver: contract_address, + market: market.market_token, + initial_collateral_token: market.long_token, + swap_path: Array32Trait::::span32(@array![]), + size_delta_usd: 3500000000000000000000, + initial_collateral_delta_amount: 1000000000000000000, // 10^18 + trigger_price: 0, + acceptable_price: 3501, + execution_fee: 0, + callback_gas_limit: 0, + min_output_amount: 0, + order_type: OrderType::MarketIncrease(()), + decrease_position_swap_type: DecreasePositionSwapType::NoSwap(()), + is_long: true, + referral_code: 0 + }; + // Create the swap order. + role_store.grant_role(exchange_router.contract_address, role::CONTROLLER); + start_roll(exchange_router.contract_address, 1930); + 'try to create prder'.print(); + start_prank(exchange_router.contract_address, caller_address); + let key_long = exchange_router.create_order(order_params_long); + 'long created'.print(); + let got_order_long = data_store.get_order(key_long); + + // Execute the swap order. + + let signatures: Span = array![0].span(); + let set_price_params = SetPricesParams { + signer_info: 0, + tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], + compacted_min_oracle_block_numbers: array![1910, 1910], + compacted_max_oracle_block_numbers: array![1920, 1920], + compacted_oracle_timestamps: array![9999, 9999], + compacted_decimals: array![1, 1], + compacted_min_prices: array![2147483648010000], // 500000, 10000 compacted + compacted_min_prices_indexes: array![0], + compacted_max_prices: array![3500, 1], // 500000, 10000 compacted + compacted_max_prices_indexes: array![0], + signatures: array![ + array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() + ], + price_feed_tokens: array![] + }; + + let keeper_address = contract_address_const::<'keeper'>(); + role_store.grant_role(keeper_address, role::ORDER_KEEPER); + + stop_prank(order_handler.contract_address); + start_prank(order_handler.contract_address, keeper_address); + start_roll(order_handler.contract_address, 1935); + // TODO add real signatures check on Oracle Account + order_handler.execute_order(key_long, set_price_params); + 'long position SUCCEEDED'.print(); + + let position_key = data_store.get_account_position_keys(caller_address, 0, 10); + let position_key_1: felt252 = *position_key.at(0); + let first_position = data_store.get_position(position_key_1); + + assert(first_position.size_in_tokens == 1000000000000000000, 'Size token should be 1 ETH'); + assert(first_position.size_in_usd == 3500000000000000000000, 'Size should be 3500$'); + assert(first_position.borrowing_factor == 0, 'Borrow should be 0'); + assert(first_position.collateral_amount == 1000000000000000000, 'Collat should be 1 ETH'); + + let market_prices = market_utils::MarketPrices { + index_token_price: Price { min: 3850, max: 3850, }, + long_token_price: Price { min: 3850, max: 3850, }, + short_token_price: Price { min: 1, max: 1, }, + }; + + let position_info = reader + .get_position_info( + data_store, referal_storage, position_key_1, market_prices, 0, contract_address, true + ); + assert(position_info.base_pnl_usd.mag == 350000000000000000000, 'PnL should be 350$'); + + //////////////////////////////// TRIGGER INCREASE POSITION ////////////////////////////////// + + // Send token to order_vault in multicall with create_order + start_prank(contract_address_const::<'ETH'>(), caller_address); + IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } + .transfer(order_vault.contract_address, 2000000000000000000); // 2ETH + + start_prank(market.market_token, caller_address); + start_prank(market.long_token, caller_address); + let order_params_long_inc = CreateOrderParams { + receiver: caller_address, + callback_contract: contract_address, + ui_fee_receiver: contract_address, + market: market.market_token, + initial_collateral_token: market.long_token, + swap_path: Array32Trait::::span32(@array![]), + size_delta_usd: 7700000000000000000000, // 7700 + initial_collateral_delta_amount: 2000000000000000000, // 2 ETH 10^18 + trigger_price: 3850, + acceptable_price: 3851, + execution_fee: 0, + callback_gas_limit: 0, + min_output_amount: 0, + order_type: OrderType::LimitIncrease(()), + decrease_position_swap_type: DecreasePositionSwapType::NoSwap(()), + is_long: true, + referral_code: 0 + }; + + // Create the long order. + role_store.grant_role(exchange_router.contract_address, role::CONTROLLER); + start_roll(exchange_router.contract_address, 1940); + 'try to create order'.print(); + start_prank(exchange_router.contract_address, caller_address); + let key_long_inc = exchange_router.create_order(order_params_long_inc); + 'Long increase created'.print(); + + // Execute the swap order. + + let set_price_params_inc = SetPricesParams { + signer_info: 0, + tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], + compacted_min_oracle_block_numbers: array![1910, 1910], + compacted_max_oracle_block_numbers: array![1920, 1920], + compacted_oracle_timestamps: array![9999, 9999], + compacted_decimals: array![1, 1], + compacted_min_prices: array![2147483648010000], // 500000, 10000 compacted + compacted_min_prices_indexes: array![0], + compacted_max_prices: array![3850, 1], // 500000, 10000 compacted + compacted_max_prices_indexes: array![0], + signatures: array![ + array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() + ], + price_feed_tokens: array![] + }; + + let keeper_address = contract_address_const::<'keeper'>(); + role_store.grant_role(keeper_address, role::ORDER_KEEPER); + + stop_prank(order_handler.contract_address); + start_prank(order_handler.contract_address, keeper_address); + start_roll(order_handler.contract_address, 1945); + // TODO add real signatures check on Oracle Account + order_handler.execute_order(key_long_inc, set_price_params_inc); + 'long pos inc SUCCEEDED'.print(); + + let position_key = data_store.get_account_position_keys(caller_address, 0, 10); + let position_key_1: felt252 = *position_key.at(0); + let first_position_inc = data_store.get_position(position_key_1); + + first_position_inc.size_in_tokens.print(); + + assert(first_position_inc.size_in_tokens == 3000000000000000000, 'Size token should be 3 ETH'); + assert(first_position_inc.size_in_usd == 11200000000000000000000, 'Size should be 11200$'); + assert(first_position_inc.borrowing_factor == 0, 'borrow should be 0'); + assert(first_position_inc.collateral_amount == 3000000000000000000, 'Collat should be 3 ETH'); + + let market_prices = market_utils::MarketPrices { + index_token_price: Price { min: 3850, max: 3850, }, + long_token_price: Price { min: 3850, max: 3850, }, + short_token_price: Price { min: 1, max: 1, }, + }; + + let position_info = reader + .get_position_info( + data_store, referal_storage, position_key_1, market_prices, 0, contract_address, true + ); + 'pnl'.print(); + assert(position_info.base_pnl_usd.mag == 350000000000000000000, 'PnL should be 350$'); + + //////////////////////////////////// TRIGGER DECREASE POSITION ////////////////////////////////////// + 'DECREASE POSITION'.print(); + + let balance_USDC_before = IERC20Dispatcher { + contract_address: contract_address_const::<'USDC'>() + } + .balance_of(caller_address); + + let balance_ETH_before = IERC20Dispatcher { + contract_address: contract_address_const::<'ETH'>() + } + .balance_of(caller_address); + + // Decrease 25% of the position + // Size = 11200$ -----> 25% = 2800 + // Collateral token amount = 3 ETH -----> 25% = 0.75 ETH + + start_prank(market.market_token, caller_address); + start_prank(market.long_token, caller_address); + let order_params_long_dec = CreateOrderParams { + receiver: caller_address, + callback_contract: contract_address, + ui_fee_receiver: contract_address, + market: market.market_token, + initial_collateral_token: market.long_token, + swap_path: Array32Trait::::span32(@array![market.market_token]), + size_delta_usd: 2800000000000000000000, // 2800 + initial_collateral_delta_amount: 750000000000000000, // 0.75 ETH 10^18 + trigger_price: 0, + acceptable_price: 3950, + execution_fee: 0, + callback_gas_limit: 0, + min_output_amount: 0, + order_type: OrderType::LimitDecrease(()), + decrease_position_swap_type: DecreasePositionSwapType::NoSwap(()), + is_long: true, + referral_code: 0 + }; + // Create the long order. + role_store.grant_role(exchange_router.contract_address, role::CONTROLLER); + start_roll(exchange_router.contract_address, 1950); + 'try to create order'.print(); + start_prank(exchange_router.contract_address, caller_address); + let key_long_dec = exchange_router.create_order(order_params_long_dec); + 'long decrease created'.print(); + let got_order_long_dec = data_store.get_order(key_long_dec); + + // Execute the swap order. + let set_price_params_dec = SetPricesParams { + signer_info: 0, + tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], + compacted_min_oracle_block_numbers: array![1910, 1910], + compacted_max_oracle_block_numbers: array![1920, 1920], + compacted_oracle_timestamps: array![9999, 9999], + compacted_decimals: array![1, 1], + compacted_min_prices: array![2147483648010000], // 500000, 10000 compacted + compacted_min_prices_indexes: array![0], + compacted_max_prices: array![3950, 1], // 500000, 10000 compacted + compacted_max_prices_indexes: array![0], + signatures: array![ + array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() + ], + price_feed_tokens: array![] + }; + + let keeper_address = contract_address_const::<'keeper'>(); + role_store.grant_role(keeper_address, role::ORDER_KEEPER); + + stop_prank(order_handler.contract_address); + start_prank(order_handler.contract_address, keeper_address); + start_roll(order_handler.contract_address, 1955); + order_handler.execute_order(key_long_dec, set_price_params_dec); + 'long pos dec SUCCEEDED'.print(); + + // Recieved 2974.999 USDC + + let first_position_dec = data_store.get_position(position_key_1); + + assert( + first_position_dec.size_in_tokens == 2250000000000000000, 'Size token should be 2.25 ETH' + ); + assert(first_position_dec.size_in_usd == 8400000000000000000000, 'Size should be 8400'); + assert(first_position_dec.borrowing_factor == 0, 'Borrow should be 0'); + assert( + first_position_dec.collateral_amount == 2250000000000000000, 'Collat should be 2.25 ETH' + ); + + let market_prices = market_utils::MarketPrices { + index_token_price: Price { min: 3950, max: 3950, }, + long_token_price: Price { min: 3950, max: 3950, }, + short_token_price: Price { min: 1, max: 1, }, + }; + + let position_info = reader + .get_position_info( + data_store, referal_storage, position_key_1, market_prices, 0, contract_address, true + ); + assert(position_info.base_pnl_usd.mag == 487500000000000000000, 'PnL should be 487,5'); + + let balance_USDC_after = IERC20Dispatcher { + contract_address: contract_address_const::<'USDC'>() + } + .balance_of(caller_address); + + let balance_ETH_after = IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } + .balance_of(caller_address); + + assert(balance_USDC_before == 50000000000000000000000, 'balance USDC should be 50000$'); + // Balance USDC after = (0.75 ETH * 3950$) + 162.499 (PnL) + assert(balance_USDC_after == 53124999999999999996350, 'balance USDC shld be 53124.99$'); + assert(balance_ETH_before == 4000000000000000000, 'balance ETH should be 4'); + assert(balance_ETH_after == 4000000000000000000, 'balance ETH should be 4'); + + //////////////////////////////////// TRIGGER CLOSE POSITION ////////////////////////////////////// + 'CLOSE POSITION'.print(); + + let balance_USDC_bef_close = IERC20Dispatcher { + contract_address: contract_address_const::<'USDC'>() + } + .balance_of(caller_address); + + let balance_ETH_bef_close = IERC20Dispatcher { + contract_address: contract_address_const::<'ETH'>() + } + .balance_of(caller_address); + + let market_prices = market_utils::MarketPrices { + index_token_price: Price { min: 4000, max: 4000, }, + long_token_price: Price { min: 4000, max: 4000, }, + short_token_price: Price { min: 1, max: 1, }, + }; + + let position_info = reader + .get_position_info( + data_store, referal_storage, position_key_1, market_prices, 0, contract_address, true + ); + assert(position_info.base_pnl_usd.mag == 600000000000000000000, 'PnL should be 600$'); + + start_prank(market.market_token, caller_address); + start_prank(market.long_token, caller_address); + let order_params_long_dec_2 = CreateOrderParams { + receiver: caller_address, + callback_contract: contract_address, + ui_fee_receiver: contract_address, + market: market.market_token, + initial_collateral_token: market.long_token, + swap_path: Array32Trait::::span32(@array![market.market_token]), + size_delta_usd: 8400000000000000000000, // 8400 + initial_collateral_delta_amount: 2250000000000000000, // 2.25 ETH 10^18 + trigger_price: 4000, + acceptable_price: 4001, + execution_fee: 0, + callback_gas_limit: 0, + min_output_amount: 0, + order_type: OrderType::LimitDecrease(()), + decrease_position_swap_type: DecreasePositionSwapType::NoSwap(()), + is_long: true, + referral_code: 0 + }; + // Create the long order. + role_store.grant_role(exchange_router.contract_address, role::CONTROLLER); + start_roll(exchange_router.contract_address, 1960); + 'try to create order'.print(); + start_prank(exchange_router.contract_address, caller_address); + let key_long_dec_2 = exchange_router.create_order(order_params_long_dec_2); + 'long decrease created'.print(); + let got_order_long_dec = data_store.get_order(key_long_dec_2); + // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'USDC'>()), ); + // data_store.set_u256(keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()), 1000000); + // Execute the swap order. + + let keeper_address = contract_address_const::<'keeper'>(); + role_store.grant_role(keeper_address, role::ORDER_KEEPER); + + let set_price_params_dec2 = SetPricesParams { + signer_info: 0, + tokens: array![contract_address_const::<'ETH'>(), contract_address_const::<'USDC'>()], + compacted_min_oracle_block_numbers: array![1910, 1910], + compacted_max_oracle_block_numbers: array![1920, 1920], + compacted_oracle_timestamps: array![9999, 9999], + compacted_decimals: array![1, 1], + compacted_min_prices: array![2147483648010000], // 500000, 10000 compacted + compacted_min_prices_indexes: array![0], + compacted_max_prices: array![4000, 1], // 500000, 10000 compacted + compacted_max_prices_indexes: array![0], + signatures: array![ + array!['signatures1', 'signatures2'].span(), array!['signatures1', 'signatures2'].span() + ], + price_feed_tokens: array![] + }; + + stop_prank(order_handler.contract_address); + start_prank(order_handler.contract_address, keeper_address); + start_roll(order_handler.contract_address, 1965); + // TODO add real signatures check on Oracle Account + order_handler.execute_order(key_long_dec_2, set_price_params_dec2); + 'Long pos close SUCCEEDED'.print(); + + let first_position_close = data_store.get_position(position_key_1); + + assert(first_position_close.size_in_tokens == 0, 'Size token should be 0'); + assert(first_position_close.size_in_usd == 0, 'Size should be 0'); + assert(first_position_close.borrowing_factor == 0, 'Borrow should be 0'); + assert(first_position_close.collateral_amount == 0, 'Collat should be 0'); + + let balance_USDC_af_close = IERC20Dispatcher { + contract_address: contract_address_const::<'USDC'>() + } + .balance_of(caller_address); + + let balance_ETH_af_close = IERC20Dispatcher { + contract_address: contract_address_const::<'ETH'>() + } + .balance_of(caller_address); + + assert(balance_USDC_bef_close == 53124999999999999996350, 'balance USDC shld be 52974.99$'); + assert(balance_USDC_af_close == 62724999999999999996350, 'balance USDC shld be 62724.99$'); + assert(balance_ETH_af_close == 4000000000000000000, 'balance ETH should be 4'); + assert(balance_ETH_bef_close == 4000000000000000000, 'balance ETH should be 4'); + + // ********************************************************************************************* + // * TEARDOWN * + // ********************************************************************************************* + teardown(data_store, market_factory); +} + // #[test] // fn test_long_18_close_integration() { // // *********************************************************************************************