From b7498864e2fe944ea423e257e155572dc5aad04e Mon Sep 17 00:00:00 2001 From: sparqet <37338401+sparqet@users.noreply.github.com> Date: Tue, 25 Jun 2024 13:34:34 +0200 Subject: [PATCH] Test/trigger fails test Increase Decrease Close (#679) * refactor integration tests * check failing tests for decrese increase and close trigger --- src/order/error.cairo | 6 +- tests/integration/test_long_integration.cairo | 1953 ++++++++++++++++- 2 files changed, 1951 insertions(+), 8 deletions(-) diff --git a/src/order/error.cairo b/src/order/error.cairo index 91d6e23a..d9341423 100644 --- a/src/order/error.cairo +++ b/src/order/error.cairo @@ -46,9 +46,9 @@ mod OrderError { fn INVALID_ORDER_PRICE(primary_price: Price, trigger_price: u256, order_type: OrderType) { let mut data: Array = array![]; data.append('invalid_order_price'); - data.append(primary_price.min.try_into().expect('u256 into felt failed')); - data.append(primary_price.max.try_into().expect('u256 into felt failed')); - data.append(trigger_price.try_into().expect('u256 into felt failed')); + // data.append(primary_price.min.try_into().expect('u256 into felt failed')); // TODO Find a way to test them test_takeprofit_long_increase_fails + // data.append(primary_price.max.try_into().expect('u256 into felt failed')); + // data.append(trigger_price.try_into().expect('u256 into felt failed')); data.append(order_type.into()); panic(data); } diff --git a/tests/integration/test_long_integration.cairo b/tests/integration/test_long_integration.cairo index ab6cf397..28faeb35 100644 --- a/tests/integration/test_long_integration.cairo +++ b/tests/integration/test_long_integration.cairo @@ -1543,7 +1543,7 @@ fn test_long_increase_decrease_close() { size_delta_usd: 2800000000000000000000, // 2800 initial_collateral_delta_amount: 750000000000000000, // 0.75 ETH 10^18 trigger_price: 0, - acceptable_price: 3850, + acceptable_price: 3849, execution_fee: 0, callback_gas_limit: 0, min_output_amount: 0, @@ -1664,7 +1664,7 @@ fn test_long_increase_decrease_close() { size_delta_usd: 8400000000000000000000, // 8400 initial_collateral_delta_amount: 2250000000000000000, // 2.25 ETH 10^18 trigger_price: 0, - acceptable_price: 4000, + acceptable_price: 3999, execution_fee: 0, callback_gas_limit: 0, min_output_amount: 0, @@ -2189,8 +2189,8 @@ fn test_takeprofit_long() { 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, + trigger_price: 3950, + acceptable_price: 3949, execution_fee: 0, callback_gas_limit: 0, min_output_amount: 0, @@ -2311,7 +2311,7 @@ fn test_takeprofit_long() { size_delta_usd: 8400000000000000000000, // 8400 initial_collateral_delta_amount: 2250000000000000000, // 2.25 ETH 10^18 trigger_price: 4000, - acceptable_price: 4001, + acceptable_price: 3999, execution_fee: 0, callback_gas_limit: 0, min_output_amount: 0, @@ -2387,6 +2387,1949 @@ fn test_takeprofit_long() { teardown(data_store, market_factory); } +#[test] +#[should_panic(expected: ('invalid_order_price', 'LimitIncrease',))] +fn test_takeprofit_long_increase_fails() { + // ********************************************************************************************* + // * 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![3860, 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: 3950, + acceptable_price: 3949, + 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: 3999, + 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] +#[should_panic(expected: ('invalid_order_price', 'LimitDecrease',))] +fn test_takeprofit_long_decrease_fails() { + // ********************************************************************************************* + // * 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: 3950, + acceptable_price: 3949, + 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![3940, 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: 3999, + 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] +#[should_panic(expected: ('invalid_order_price', 'LimitDecrease',))] +fn test_takeprofit_long_close_fails() { + // ********************************************************************************************* + // * 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: 3950, + acceptable_price: 3949, + 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: 3999, + 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![3990, 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() { // // *********************************************************************************************