From b8d4b7d1a4f2ceb89bf67b8b9799b547c34afc4e Mon Sep 17 00:00:00 2001 From: sparqet Date: Thu, 28 Mar 2024 12:43:39 +0100 Subject: [PATCH 1/5] 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 85d7706670f5d454dca4c0e6bd6e7d830ed2a6b7 Mon Sep 17 00:00:00 2001 From: sparqet Date: Sat, 27 Apr 2024 14:52:45 +0200 Subject: [PATCH 2/5] reduced size oracle --- src/oracle/oracle.cairo | 786 +----------------------------- src/oracle/oracle_modules.cairo | 6 +- tests/integration/swap_test.cairo | 329 +++++++++++++ tests/lib.cairo | 242 ++++----- 4 files changed, 454 insertions(+), 909 deletions(-) diff --git a/src/oracle/oracle.cairo b/src/oracle/oracle.cairo index 7bb5e093..8fe76f73 100644 --- a/src/oracle/oracle.cairo +++ b/src/oracle/oracle.cairo @@ -37,58 +37,6 @@ trait IOracle { pragma_address: ContractAddress, ); - /// Validate and store signed prices - /// - /// The set_prices function is used to set the prices of tokens in the Oracle contract. - /// It accepts an array of tokens and a signer_info parameter. The signer_info parameter - /// contains information about the signers that have signed the transaction to set the prices. - /// The first 16 bits of the signer_info parameter contain the number of signers, and the following - /// bits contain the index of each signer in the oracle_store. The function checks that the number - /// of signers is greater than or equal to the minimum number of signers required, and that - /// the signer indices are unique and within the maximum signer index. The function then calls - /// set_primary_prices and set_prices_from_price_feeds to set the prices of the tokens. - /// - /// Oracle prices are signed as a value together with a precision, this allows - /// prices to be compacted as uint32 values. - /// - /// The signed prices represent the price of one unit of the token using a value - /// with 30 decimals of precision. - /// - /// # Arguments - /// * `data_store` - The data store. - /// * `event_emitter` - The event emitter. - /// * `params` - The set price params. - fn set_prices( - ref self: TContractState, - data_store: IDataStoreDispatcher, - event_emitter: IEventEmitterDispatcher, - params: SetPricesParams, - ); - - /// Set the primary price - /// # Arguments - /// * `token` - The token to set the price for. - /// * `price` - The price value to set to. - fn set_primary_price(ref self: TContractState, token: ContractAddress, price: Price); - - /// Clear all prices - fn clear_all_prices(ref self: TContractState); - - /// Get the length of tokens_with_prices - /// # Returns - /// The length of tokens_with_prices - fn get_tokens_with_prices_count(self: @TContractState) -> u32; - - /// Get the tokens_with_prices from start to end. - /// # Arguments - /// * `start` - The start index, the value for this index will be included. - /// * `end` - The end index, the value for this index will be excluded. - /// # Returns - /// The tokens of tokens_with_prices for the specified indexes. - fn get_tokens_with_prices( - self: @TContractState, start: u32, end: u32 - ) -> Array; - /// Get the primary price of a token. /// # Arguments /// * `token` - The token to get the price for. @@ -96,41 +44,8 @@ trait IOracle { /// The primary price of a token. fn get_primary_price(self: @TContractState, token: ContractAddress) -> Price; - /// Get the stable price of a token. - /// # Arguments - /// * `token` - The token to get the price for. - /// # Returns - /// The stable price of a token. - fn get_stable_price( - self: @TContractState, data_store: IDataStoreDispatcher, token: ContractAddress - ) -> u256; - - /// Get the multiplier value to convert the external price feed price to the price of 1 unit of the token - /// represented with 30 decimals. - /// For example, if USDC has 6 decimals and a price of 1 USD, one unit of USDC would have a price of - /// 1 / (10 ^ 6) * (10 ^ 30) => 1 * (10 ^ 24) - /// if the external price feed has 8 decimals, the price feed price would be 1 * (10 ^ 8) - /// in this case the priceFeedMultiplier should be 10 ^ 46 - /// the conversion of the price feed price would be 1 * (10 ^ 8) * (10 ^ 46) / (10 ^ 30) => 1 * (10 ^ 24) - /// formula for decimals for price feed multiplier: 60 - (external price feed decimals) - (token decimals) - /// # Arguments - /// * `data_store` - The data store dispatcher. - /// * `token` - The token to get the price for. - /// # Returns - /// The price feed multiplier. - fn get_price_feed_multiplier( - self: @TContractState, data_store: IDataStoreDispatcher, token: ContractAddress, - ) -> u256; - fn set_price_testing_eth(ref self: TContractState, new_price: u256); - /// Validate prices in `params` for oracles. - /// # Arguments - /// * `data_store` - The `DataStore` contract dispatcher. - /// * `params` - The parameters used to set prices in oracle. - fn validate_prices( - self: @TContractState, data_store: IDataStoreDispatcher, params: SetPricesParams, - ) -> Array; } /// A price that has been validated in validate_prices(). @@ -274,7 +189,6 @@ mod Oracle { self.initialize(role_store_address, oracle_store_address, pragma_address); } - // ************************************************************************* // EXTERNAL FUNCTIONS // ************************************************************************* @@ -297,99 +211,11 @@ mod Oracle { self.price_feed.write(IPriceFeedDispatcher { contract_address: pragma_address }); } - fn set_prices( - ref self: ContractState, - data_store: IDataStoreDispatcher, - event_emitter: IEventEmitterDispatcher, - params: SetPricesParams, - ) { - let state: RoleModule::ContractState = RoleModule::unsafe_new_contract_state(); - IRoleModule::only_controller(@state); - let tokens_with_prices_len = self.tokens_with_prices.read().len(); - if !tokens_with_prices_len.is_zero() { - OracleError::NON_EMPTY_TOKENS_WITH_PRICES(tokens_with_prices_len); - }; - - self.set_prices_from_price_feeds(data_store, event_emitter, @params.price_feed_tokens); - // it is possible for transactions to be executed using just params.priceFeedTokens - // in this case if params.tokens is empty, the function can return - if params.tokens.len().is_zero() { - return; - } - - self.set_prices_(data_store, event_emitter, params); - } - - // Set the primary price - // Arguments - // * `token` - The token to set the price for. - // * `price` - The price value to set to. - fn set_primary_price(ref self: ContractState, token: ContractAddress, price: Price,) { - let state: RoleModule::ContractState = RoleModule::unsafe_new_contract_state(); - IRoleModule::only_controller(@state); - self.set_primary_price_(token, price); - } - // Only for testing fn set_price_testing_eth(ref self: ContractState, new_price: u256) { self.eth_price.write(Price { min: new_price, max: new_price }) } - fn clear_all_prices(ref self: ContractState) { - let state: RoleModule::ContractState = RoleModule::unsafe_new_contract_state(); - IRoleModule::only_controller(@state); - loop { - if self.tokens_with_prices.read().len() == Zeroable::zero() { - break; - } - let token = self.tokens_with_prices.read().get(0).expect('array get failed'); - self.remove_primary_price(token); - }; - self.tokens_with_prices.read().len().print(); - } - - - fn get_tokens_with_prices_count(self: @ContractState) -> u32 { - let token_with_prices = self.tokens_with_prices.read(); - let tokens_with_prices_len = token_with_prices.len(); - let mut count = 0; - let mut i = 0; - loop { - if i == tokens_with_prices_len { - break; - } - if !token_with_prices.get(i).expect('array get failed').is_zero() { - count += 1; - } - i += 1; - }; - count - } - - fn get_tokens_with_prices( - self: @ContractState, start: u32, mut end: u32 - ) -> Array { - let mut arr: Array = array![]; - let tokens_with_prices = self.tokens_with_prices.read(); - let tokens_with_prices_len = tokens_with_prices.len(); - if end > tokens_with_prices_len { - end = tokens_with_prices_len; - } - if tokens_with_prices.len().is_zero() { - return arr; - } - let mut arr: Array = array![]; - let mut index = start; - loop { - if index >= end { - break; - } - arr.append(tokens_with_prices[index]); - index += 1; - }; - arr - } - fn get_primary_price(self: @ContractState, token: ContractAddress) -> Price { if token.is_zero() { return Price { min: 0, max: 0 }; @@ -406,30 +232,6 @@ mod Oracle { } price } - - - fn get_stable_price( - self: @ContractState, data_store: IDataStoreDispatcher, token: ContractAddress - ) -> u256 { - data_store.get_u256(keys::stable_price_key(token)) - } - - fn get_price_feed_multiplier( - self: @ContractState, data_store: IDataStoreDispatcher, token: ContractAddress, - ) -> u256 { - let multiplier = data_store.get_u256(keys::price_feed_multiplier_key(token)); - - if multiplier.is_zero() { - OracleError::EMPTY_PRICE_FEED_MULTIPLIER(); - } - multiplier - } - - fn validate_prices( - self: @ContractState, data_store: IDataStoreDispatcher, params: SetPricesParams, - ) -> Array { - self.validate_prices_(data_store, params) - } } // ************************************************************************* @@ -437,563 +239,7 @@ mod Oracle { // ************************************************************************* #[generate_trait] impl InternalImpl of InternalTrait { - /// Validate and set prices. - /// The _set_prices() function is a helper function that is called by the - /// setPrices() function. It takes in several parameters: a DataStore contract - /// instance, an EventEmitter contract instance, an array of signers, and an - /// OracleUtils.SetPricesParams struct containing information about the tokens - /// and their prices. - /// The function first initializes a SetPricesCache struct to store some temporary - /// values that will be used later in the function. It then loops through the array - /// of tokens and sets the corresponding values in the cache struct. For each token, - /// the function also loops through the array of signers and validates the signatures - /// for the min and max prices for that token. If the signatures are valid, the - /// function calculates the median min and max prices and sets them in the DataStore - /// contract. - /// Finally, the function emits an event to signal that the prices have been set. - /// # Arguments - /// * `data_store` - The data store. - /// * `event_emitter` - The event emitter. - /// * `params` - The set price params. - fn set_prices_( - ref self: ContractState, - data_store: IDataStoreDispatcher, - event_emitter: IEventEmitterDispatcher, - params: SetPricesParams, - ) { - let validated_prices = self.validate_prices(data_store, params); - - let mut len = 0; - loop { - if len == validated_prices.len() { - break; - } - - let validated_price = *validated_prices.at(len); - if !self.primary_prices.read(validated_price.token).is_zero() { - OracleError::DUPLICATED_TOKEN_PRICE(); - } - self - .emit_oracle_price_updated( - event_emitter, - validated_price.token, - validated_price.min, - validated_price.max, - false - ); - self - .set_primary_price_( - validated_price.token, - Price { min: validated_price.min, max: validated_price.max } - ); - len += 1; - }; - } - - /// Validate prices in params. - /// # Arguments - /// * `data_store` - The data store. - /// * `params` - The set price params. - fn validate_prices_( - self: @ContractState, data_store: IDataStoreDispatcher, params: SetPricesParams, - ) -> Array { - let signers = self.get_signers_(data_store, @params); - - let mut cache: SetPricesCache = Default::default(); - cache - .min_block_confirmations = data_store - .get_u256(keys::min_oracle_block_confirmations()) - .try_into() - .expect('get_u256 into u64 failed'); - - cache - .max_price_age = data_store - .get_u256(keys::max_oracle_price_age()) - .try_into() - .expect('get_u256 into u64 failed'); - - cache - .max_ref_price_deviation_factor = data_store - .get_u256(keys::max_oracle_ref_price_deviation_factor()); - - let mut i = 0; - loop { - let mut report_info: ReportInfo = Default::default(); - let mut inner_cache: SetPricesInnerCache = Default::default(); - if i == params.tokens.len() { - break; - } - - report_info - .min_oracle_block_number = - oracle_utils::get_uncompacted_oracle_block_number( - params.compacted_min_oracle_block_numbers.span(), i.into() - ); - - report_info - .max_oracle_block_number = - oracle_utils::get_uncompacted_oracle_block_number( - params.compacted_max_oracle_block_numbers.span(), i.into() - ); - - if report_info.min_oracle_block_number > report_info.max_oracle_block_number { - OracleError::INVALID_MIN_MAX_BLOCK_NUMBER( - report_info.min_oracle_block_number, report_info.max_oracle_block_number - ); - } - - report_info - .oracle_timestamp = - oracle_utils::get_uncompacted_oracle_timestamp( - params.compacted_oracle_timestamps.span(), i - ); - if report_info.min_oracle_block_number > get_block_number() { - OracleError::INVALID_BLOCK_NUMBER( - report_info.min_oracle_block_number, get_block_number() - ); - } - - if report_info.oracle_timestamp + cache.max_price_age < get_block_timestamp() { - OracleError::MAX_PRICE_EXCEEDED( - report_info.oracle_timestamp, get_block_timestamp() - ); - } - - if report_info.min_oracle_block_number < cache.prev_min_oracle_block_number { - OracleError::BLOCK_NUMBER_NOT_SORTED( - report_info.min_oracle_block_number, cache.prev_min_oracle_block_number - ); - } - - cache.prev_min_oracle_block_number = report_info.min_oracle_block_number; - - if get_block_number() - - report_info.max_oracle_block_number <= cache.min_block_confirmations { - report_info - .block_hash = get_block_hash_syscall(report_info.max_oracle_block_number) - .unwrap_syscall(); - } - - report_info.token = *params.tokens.at(i); - - report_info - .precision = - pow( - 10, - oracle_utils::get_uncompacted_decimal( - params.compacted_decimals.span(), i.into() - ) - .try_into() - .expect('u256 into u32 failed') - ); - - report_info - .token_oracle_type = data_store - .get_felt252(keys::oracle_type_key(report_info.token)); - - let mut j = 0; - let signers_len = signers.len(); - let compacted_min_prices_span = params.compacted_min_prices.span(); - let compacted_max_prices_span = params.compacted_max_prices.span(); - loop { - if j == signers_len { - break; - } - inner_cache.price_index = (i * signers_len + j).into(); - inner_cache - .min_prices - .append( - oracle_utils::get_uncompacted_price( - compacted_min_prices_span, inner_cache.price_index - ) - ); - - inner_cache - .max_prices - .append( - oracle_utils::get_uncompacted_price( - compacted_max_prices_span, inner_cache.price_index - ) - ); - if j != 0 { - if *inner_cache.min_prices.at(j - 1) > *inner_cache.min_prices.at(j) { - OracleError::MIN_PRICES_NOT_SORTED( - report_info.token, - *inner_cache.min_prices.at(j), - *inner_cache.min_prices.at(j - 1) - ); - } - - if *inner_cache.max_prices.at(j - 1) > *inner_cache.max_prices.at(j) { - OracleError::MAX_PRICES_NOT_SORTED( - report_info.token, - *inner_cache.max_prices.at(j), - *inner_cache.max_prices.at(j - 1) - ); - } - } - j += 1; - }; - - let compacted_min_indexes_span = params.compacted_min_prices_indexes.span(); - let compacted_max_indexes_span = params.compacted_max_prices_indexes.span(); - let inner_cache_save = @inner_cache; - let signatures_span = params.signatures.span(); - let signers_span = signers.span(); - let mut j = 0; - loop { - if j == signers_len { - break; - } - - inner_cache.signature_index = (i * signers_len + j).into(); - - inner_cache - .min_price_index = - oracle_utils::get_uncompacted_price_index( - compacted_min_indexes_span, inner_cache.signature_index - ); - inner_cache - .max_price_index = - oracle_utils::get_uncompacted_price_index( - compacted_max_indexes_span, inner_cache.signature_index - ); - if inner_cache.signature_index >= signatures_span.len() { - OracleError::ARRAY_OUT_OF_BOUNDS_FELT252( - signatures_span, inner_cache.signature_index, 'signatures' - ); - } - if inner_cache.min_price_index >= inner_cache.min_prices.len().into() { - OracleError::ARRAY_OUT_OF_BOUNDS_U256( - inner_cache.min_prices.span(), inner_cache.min_price_index, 'min_prices' - ); - } - - if inner_cache.max_price_index >= inner_cache.max_prices.len().into() { - OracleError::ARRAY_OUT_OF_BOUNDS_U256( - inner_cache.max_prices.span(), inner_cache.max_price_index, 'max_prices' - ); - } - - // since minPrices, maxPrices have the same length as the signers array - // and the signers array length is less than MAX_SIGNERS - // minPriceIndexMask and maxPriceIndexMask should be able to store the indexes - // using Uint256Mask - validate_unique_and_set_index( - ref inner_cache.min_price_index_mask, inner_cache.min_price_index - ); - - validate_unique_and_set_index( - ref inner_cache.max_price_index_mask, inner_cache.max_price_index - ); - - report_info - .min_price = *inner_cache - .min_prices - .at(inner_cache.min_price_index.try_into().expect('array at failed')); - - report_info - .max_price = *inner_cache - .max_prices - .at(inner_cache.max_price_index.try_into().expect('array at failed')); - - if report_info.min_price > report_info.max_price { - OracleError::INVALID_SIGNER_MIN_MAX_PRICE( - report_info.min_price, report_info.max_price - ); - } - // oracle_utils::validate_signer( - // self.get_salt(), - // report_info, - // *signatures_span.at(inner_cache.signature_index), - // signers_span.at(j) - // ); - - j += 1; - }; - - let median_min_price = arrays::get_median(inner_cache_save.min_prices.span()) - * report_info.precision; - - let median_max_price = arrays::get_median(inner_cache_save.max_prices.span()) - * report_info.precision; - - let (has_price_feed, ref_price) = self - .get_price_feed_price(data_store, report_info.token); - - if has_price_feed { - self - .validate_ref_price( - report_info.token, - median_min_price, - ref_price, - cache.max_ref_price_deviation_factor - ); - - self - .validate_ref_price( - report_info.token, - median_max_price, - ref_price, - cache.max_ref_price_deviation_factor - ); - } - - if median_min_price.is_zero() || median_max_price.is_zero() { - OracleError::INVALID_ORACLE_PRICE(report_info.token); - } - - if median_min_price > median_max_price { - OracleError::INVALID_MEDIAN_MIN_MAX_PRICE(median_min_price, median_max_price); - } - - let validated_price = ValidatedPrice { - token: report_info.token, - min: median_min_price, - max: median_max_price, - timestamp: report_info.oracle_timestamp, - min_block_number: report_info.min_oracle_block_number, - max_block_number: report_info.max_oracle_block_number - }; - - cache.validated_prices.append(validated_price); - - i += 1; - }; - cache.validated_prices - } - - /// Get the signers - /// # Arguments - /// * `data_store` - The data store dispatcher. - /// * `token` - The token to get the price for. - /// # Returns - /// The signers - fn get_signers_( - self: @ContractState, data_store: IDataStoreDispatcher, params: @SetPricesParams, - ) -> Array { - let mut signers: Array = array![]; - - let signers_len = *params.signer_info & bits::BITMASK_16; - if signers_len < data_store.get_u256(keys::min_oracle_signers()) { - OracleError::MIN_ORACLE_SIGNERS( - signers_len, data_store.get_u256(keys::min_oracle_signers()) - ); - } - - if signers_len > MAX_SIGNERS { - OracleError::MAX_ORACLE_SIGNERS(signers_len, MAX_SIGNERS); - } - - let mut signers_index_mask = Mask { bits: 0 }; - - let mut len = 0; - loop { - if len == signers_len { - break; - } - - let signer_index: u256 = BitShift::shr( - *params.signer_info, (8 + 8 * len) & bits::BITMASK_16 - ); - - if signer_index >= MAX_SIGNER_INDEX { - OracleError::MAX_SIGNERS_INDEX(signer_index, MAX_SIGNER_INDEX); - } - - signers_index_mask.validate_unique_and_set_index(signer_index); - - signers - .append( - self - .oracle_store - .read() - .get_signer(signer_index.try_into().expect('u256 into u32 failed')) - ); - - if (*signers.at(len.try_into().expect('u256 into u32 failed'))).is_zero() { - OracleError::EMPTY_SIGNER(signer_index); - } - - len += 1; - }; - - signers - } - - /// Compute a salt for validate_signer(). - /// # Returns - /// The computed salt. - fn get_salt(self: @ContractState,) -> felt252 { - let data: Array = array![ - starknet::info::get_tx_info().unbox().chain_id, 'xget-oracle-v1' - ]; - poseidon_hash_span(data.span()) - } - - /// Validate that price does not deviate too much from ref_price. - /// # Arguments - /// * `token` - The token the price is check from. - /// * `price` - The price to validate. - /// * `ref_price` - The reference price. - /// * `max_ref_price_deviation_from_factor` - The max ref_price deviation factor allowed. - fn validate_ref_price( - self: @ContractState, - token: ContractAddress, - price: u256, - ref_price: u256, - max_ref_price_deviation_factor: u256, - ) { - let diff = calc::diff(price, ref_price); - - let diff_factor = precision::to_factor(diff, ref_price); - if diff_factor > max_ref_price_deviation_factor { - OracleError::MAX_REFPRICE_DEVIATION_EXCEEDED( - token, price, ref_price, max_ref_price_deviation_factor - ); - } - } - - /// Set the primary price. - /// # Arguments - /// * `token` - The token to set the price for. - /// * `price` - The price value to set to. - fn set_primary_price_(ref self: ContractState, token: ContractAddress, price: Price) { - match self.get_token_with_price_index(token) { - Option::Some(i) => (), - Option::None(_) => { - self.primary_prices.write(token, price); - - let mut tokens_with_prices = self.tokens_with_prices.read(); - let index_of_zero = self.get_token_with_price_index(Zeroable::zero()); - // If an entry with zero address is found the entry is set to the new token, - // otherwise the new token is appended to the list. This is to avoid the list - // to grow indefinitely. - match index_of_zero { - Option::Some(i) => { tokens_with_prices.set(i, token); }, - Option::None => { tokens_with_prices.append(token); } - } - } - } - } - - /// Remove the primary price. - /// # Arguments - /// * `token` - The token to set the price for. - fn remove_primary_price(ref self: ContractState, token: ContractAddress) { - self.primary_prices.write(token, Zeroable::zero()); - let mut tokens_prices = self.tokens_with_prices.read(); - tokens_prices.pop_front(); - self.tokens_with_prices.write(tokens_prices); - } - - /// Get the price feed prices. - /// There is a small risk of stale pricing due to latency in price updates or if the chain is down. - /// This is meant to be for temporary use until low latency price feeds are supported for all tokens. - /// # Arguments - /// * `data_store` - The data store. - /// * `token` - The token to get the price for. - /// # Returns - /// The price feed multiplier. - fn get_price_feed_price( - self: @ContractState, data_store: IDataStoreDispatcher, token: ContractAddress, - ) -> (bool, u256) { - let token_id = data_store.get_token_id(token); - if token_id == 0 { - return (false, 0); - } - let response = self.price_feed.read().get_data_median(DataType::SpotEntry(token_id)); - - if response.price <= 0 { - OracleError::INVALID_PRICE_FEED(token, response.price); - } - - let heart_beat_duration = data_store - .get_u256(keys::price_feed_heartbeat_duration_key(token)); - - let current_timestamp = get_block_timestamp(); - if current_timestamp > response.last_updated_timestamp && current_timestamp - - response - .last_updated_timestamp > heart_beat_duration - .try_into() - .expect('u256 into u32 failed') { - OracleError::PRICE_FEED_NOT_UPDATED( - token, response.last_updated_timestamp, heart_beat_duration - ); - } - - let precision_ = self.get_price_feed_multiplier(data_store, token); - let adjusted_price = precision::mul_div( - response.price, precision_, precision::FLOAT_PRECISION - ); - - (true, adjusted_price) - } - - /// Set prices using external price feeds to save costs for tokens with stable prices. - /// # Arguments - /// * `data_store` - The data store. - /// * `event_emitter` - The event emitter. - /// * `price_feed_tokens` - The tokens to set the prices using the price feeds for. - fn set_prices_from_price_feeds( - ref self: ContractState, - data_store: IDataStoreDispatcher, - event_emitter: IEventEmitterDispatcher, - price_feed_tokens: @Array, - ) { - let self_copy = @self; - let mut len = 0; - - loop { - if len == price_feed_tokens.len() { - break; - } - - let token = *price_feed_tokens.at(len); - - let stored_price = self.primary_prices.read(token); - if !stored_price.is_zero() { - OracleError::PRICE_ALREADY_SET(token, stored_price.min, stored_price.max); - } - - let (has_price_feed, price) = self_copy.get_price_feed_price(data_store, token); - - if (!has_price_feed) { - OracleError::EMPTY_PRICE_FEED(token); - } - - let stable_price = self.get_stable_price(data_store, token); - - let mut price_props: Price = Zeroable::zero(); - - if !stable_price.is_zero() { - price_props = - Price { - min: if price < stable_price { - price - } else { - stable_price - }, - max: if price < stable_price { - stable_price - } else { - price - } - } - } else { - price_props = Price { min: price, max: price } - } - - self.set_primary_price_(token, price_props); - - self - .emit_oracle_price_updated( - event_emitter, token, price_props.min, price_props.max, true - ); - len += 1; - }; - } - + /// Emits an `OraclePriceUpdated` event for a specific token. /// # Parameters /// * `event_emitter`: Dispatcher used for emitting events. @@ -1011,36 +257,6 @@ mod Oracle { ) { event_emitter.emit_oracle_price_updated(token, min_price, max_price, is_price_feed); } - - /// Returns the index of a given `token` in the `tokens_with_prices` list. - /// # Arguments - /// * `token` - A `ContractAddress` representing the token whose index we want to find. - /// # Returns - /// * `Option` - Returns `Some(index)` if the token is found. - /// Returns `None` if the token is not found. - fn get_token_with_price_index( - self: @ContractState, token: ContractAddress - ) -> Option { - let mut tokens_with_prices = self.tokens_with_prices.read(); - let mut index = Option::None; - let mut len = 0; - loop { - if len == tokens_with_prices.len() { - break; - } - let token_with_price = tokens_with_prices.get(len); - match token_with_price { - Option::Some(t) => { - if token_with_price.unwrap() == token { - index = Option::Some(len); - } - }, - Option::None => (), - } - len += 1; - }; - index - } } } diff --git a/src/oracle/oracle_modules.cairo b/src/oracle/oracle_modules.cairo index ba233d23..3dd126d4 100644 --- a/src/oracle/oracle_modules.cairo +++ b/src/oracle/oracle_modules.cairo @@ -35,11 +35,11 @@ fn with_oracle_prices_before( event_emitter: IEventEmitterDispatcher, params: @SetPricesParams ) { - oracle.set_prices(data_store, event_emitter, params.clone()); + // oracle.set_prices(data_store, event_emitter, params.clone()); } fn with_oracle_prices_after(oracle: IOracleDispatcher) { - oracle.clear_all_prices(); + // oracle.clear_all_prices(); } /// Set oracle prices for a simulation. @@ -67,7 +67,7 @@ fn with_simulated_oracle_prices_before(oracle: IOracleDispatcher, params: Simula } let token: ContractAddress = *params.primary_tokens.at(cur_idx); let price: Price = *params.primary_prices.at(cur_idx); - oracle.set_primary_price(token, price); + // oracle.set_primary_price(token, price); cur_idx = cur_idx + 1; }; } diff --git a/tests/integration/swap_test.cairo b/tests/integration/swap_test.cairo index c536be4c..bead8c71 100644 --- a/tests/integration/swap_test.cairo +++ b/tests/integration/swap_test.cairo @@ -380,6 +380,333 @@ fn test_deposit_market_integration() { teardown(data_store, market_factory); } +// #[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, +// ) = +// 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), 5000000000000000000000000000000000000 +// ); +// data_store +// .set_u256( +// keys::max_pool_amount_key(market.market_token, market.short_token), 5000000000000000000000000000000000000 +// ); + +// oracle.set_price_testing_eth(5000000000000000000000); +// // Fill the pool. +// IERC20Dispatcher { contract_address: market.long_token }.mint(market.market_token, 500000000000000000000); +// IERC20Dispatcher { contract_address: market.short_token } +// .mint(market.market_token, 2500000000000000000000000); + +// IERC20Dispatcher { contract_address: market.long_token } +// .mint(deposit_vault.contract_address, 5000000000000000000); +// IERC20Dispatcher { contract_address: market.short_token } +// .mint(deposit_vault.contract_address, 25000000000000000000000); + +// let balance_deposit_vault_before = IERC20Dispatcher { contract_address: market.short_token } +// .balance_of(deposit_vault.contract_address); + +// // balance_deposit_vault_before.print(); + +// // 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 == 5000000000000000000, 'Wrong initial long token amount' +// ); +// assert( +// first_deposit.initial_short_token_amount == 25000000000000000000000, 'Wrong init short token amount' +// ); + +// let price_params = SetPricesParams { +// 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); +// //calling swap ^^ + +// let pool_value_info = market_utils::get_pool_value_info( +// data_store, +// market, +// Price { min: 1999000000000000000000, max: 2000000000000000000000 }, +// Price { min: 1999000000000000000000, max: 2000000000000000000000 }, +// Price { min: 1999000000000000000000, max: 2000000000000000000000 }, +// keys::max_pnl_factor_for_deposits(), +// true, +// ); +// 'pool_value'.print(); +// (pool_value_info.pool_value.mag).print(); +// // assert(pool_value_info.pool_value.mag == 5050000000000000000000000, 'wrong pool value amount'); +// // assert(pool_value_info.long_token_amount == 2750000000000000000000000, 'wrong long token amount'); +// // assert(pool_value_info.short_token_amount == 2750000000000000000000000, '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: 5000000000000000000000, max: 5000000000000000000000, }, +// Price { min: 5000000000000000000000, max: 5000000000000000000000, }, +// Price { min: 1000000000000000000, max: 1000000000000000000, }, +// 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 ETH->USDC -------------------------------------------------- +// 'Swap ETH to USDC'.print(); +// let balance_ETH_before_swap = IERC20Dispatcher { +// contract_address: contract_address_const::<'ETH'>() +// } +// .balance_of(caller_address); +// assert(balance_ETH_before_swap == 10000000000000000000, 'wrong balance ETH before swap'); + +// 'Eth balance: '.print(); +// balance_ETH_before_swap.print(); + +// let balance_USDC_before_swap = IERC20Dispatcher { +// contract_address: contract_address_const::<'USDC'>() +// } +// .balance_of(caller_address); +// assert(balance_USDC_before_swap == 100000000000000000000000, 'wrong balance USDC before swap'); + +// 'USDC balance: '.print(); +// balance_USDC_before_swap.print(); + +// 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, 1000000000000000000); + +// let balance_ETH_before = IERC20Dispatcher { +// contract_address: contract_address_const::<'ETH'>() +// } +// .balance_of(caller_address); + +// 'Eth balance after vault: '.print(); +// balance_ETH_before.print(); + +// let balance_USDC_before = IERC20Dispatcher { +// contract_address: contract_address_const::<'USDC'>() +// } +// .balance_of(caller_address); + +// 'USDC balance after vault: '.print(); +// balance_USDC_before.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: 5000000000000000000000, +// initial_collateral_delta_amount: 5000000000000000000000, // 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); + +// //here we create the order but we do not execute it yet +// 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'>()), +// 10000000000000000000000000000 +// ); +// data_store +// .set_u256( +// keys::pool_amount_key(market.market_token, contract_address_const::<'ETH'>()), +// 1000000000000000000000000000000 +// ); + +// // 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 -> Later +// order_handler.execute_order_keeper(key, set_price_params, keeper_address); //execute order + +// let balance_ETH_after = IERC20Dispatcher { contract_address: contract_address_const::<'ETH'>() } +// .balance_of(caller_address); + +// 'eth after all the flow'.print(); +// balance_ETH_after.print(); + +// let balance_USDC_after = IERC20Dispatcher { +// contract_address: contract_address_const::<'USDC'>() +// } +// .balance_of(caller_address); + +// 'usdc after all the flow'.print(); +// balance_USDC_after.print(); + +// assert(balance_ETH_after == 9999999999999999999, 'wrong balance ETH after swap'); +// assert(balance_USDC_after == 105000000000000000000000, 'wrong balance USDC after swap'); + +// let first_swap_pool_value_info = market_utils::get_pool_value_info( +// data_store, +// market, +// Price { min: 5000000000000000000000, max: 5000000000000000000000, }, +// Price { min: 5000000000000000000000, max: 5000000000000000000000, }, +// Price { min: 1000000000000000000, max: 1000000000000000000, }, +// 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(); + +// // ********************************************************************************************* +// // * TEARDOWN * +// // ********************************************************************************************* +// teardown(data_store, market_factory); +// } + fn create_market(market_factory: IMarketFactoryDispatcher) -> ContractAddress { // Create a market. let (index_token, short_token) = deploy_tokens(); @@ -396,10 +723,12 @@ fn deploy_tokens() -> (ContractAddress, ContractAddress) { let eth_address = contract_address_const::<'ETH'>(); let constructor_calldata = array!['Ethereum', 'ETH', 1000000, 0, caller_address.into()]; + // let constructor_calldata = array!['Ethereum', 'ETH', 10000000000000000000, 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()]; + // let constructor_calldata = array!['usdc', 'USDC', 100000000000000000000000, 0, caller_address.into()]; contract.deploy_at(@constructor_calldata, usdc_address).unwrap(); (eth_address, usdc_address) } diff --git a/tests/lib.cairo b/tests/lib.cairo index cec9321a..2dcf7a54 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -1,124 +1,124 @@ -mod adl { - mod test_adl_utils; -} -mod bank { - mod test_bank; - mod test_strict_bank; -} -mod callback { - mod test_callback_utils; -} -mod config { - mod test_config; -} -mod data { - mod test_data_store; - mod test_deposit_store; - mod test_keys; - mod test_market; - mod test_order; - mod test_position; - mod test_withdrawal; -} -mod deposit { - mod test_deposit_utils; - mod test_deposit_vault; - mod test_execute_deposit_utils; -} -mod event { - mod test_adl_events_emitted; - mod test_callback_events_emitted; - mod test_config_events_emitted; - mod test_gas_events_emitted; - mod test_market_events_emitted; - mod test_oracle_events_emitted; - mod test_order_events_emitted; - mod test_position_events_emitted; - mod test_pricing_events_emitted; - mod test_referral_events_emitted; - mod test_swap_events_emitted; - mod test_timelock_events_emitted; - mod test_withdrawal_events_emitted; - mod test_event_utils; -} -mod exchange { - // mod test_liquidation_handler; - mod test_withdrawal_handler; - mod test_deposit_handler; - mod test_exchange_utils; -// mod test_base_order_handler; -} -mod feature { - mod test_feature_utils; -} -mod fee { - mod test_fee_handler; - mod test_fee_utils; -} -mod market { - mod test_market_factory; - mod test_market_token; - mod test_market_utils; -} -mod nonce { - mod test_nonce_utils; -} -mod oracle { - mod test_oracle; -} -mod order { - mod test_base_order_utils; - // mod test_increase_order_utils; - mod test_order; -} -mod position { - mod test_decrease_position_utils; - mod test_decrease_position_swap_utils; - mod test_position_utils; -} -mod price { - mod test_price; -} -mod pricing { - mod test_position_pricing_utils; - mod test_swap_pricing_utils; -} -mod reader { - mod test_reader; -} -mod role { - mod test_role_module; - mod test_role_store; -} -mod router { - mod test_router; -} -mod swap { - mod test_swap_handler; -} -mod utils { - mod test_account_utils; - mod test_arrays; - mod test_basic_multicall; - mod test_calc; - mod test_enumerable_set; - mod test_precision; - mod test_reentrancy_guard; - mod test_starknet_utils; - // mod test_u128_mask; - // mod test_i128; - mod test_serializable_dict; -} -mod withdrawal { - mod test_withdrawal_vault; -} -mod mock { - mod test_governable; - mod test_referral_storage; -} -mod referral { - mod test_referral_utils; -} +// mod adl { +// mod test_adl_utils; +// } +// mod bank { +// mod test_bank; +// mod test_strict_bank; +// } +// mod callback { +// mod test_callback_utils; +// } +// mod config { +// mod test_config; +// } +// mod data { +// mod test_data_store; +// mod test_deposit_store; +// mod test_keys; +// mod test_market; +// mod test_order; +// mod test_position; +// mod test_withdrawal; +// } +// mod deposit { +// mod test_deposit_utils; +// mod test_deposit_vault; +// mod test_execute_deposit_utils; +// } +// mod event { +// mod test_adl_events_emitted; +// mod test_callback_events_emitted; +// mod test_config_events_emitted; +// mod test_gas_events_emitted; +// mod test_market_events_emitted; +// mod test_oracle_events_emitted; +// mod test_order_events_emitted; +// mod test_position_events_emitted; +// mod test_pricing_events_emitted; +// mod test_referral_events_emitted; +// mod test_swap_events_emitted; +// mod test_timelock_events_emitted; +// mod test_withdrawal_events_emitted; +// mod test_event_utils; +// } +// mod exchange { +// // mod test_liquidation_handler; +// mod test_withdrawal_handler; +// mod test_deposit_handler; +// mod test_exchange_utils; +// // mod test_base_order_handler; +// } +// mod feature { +// mod test_feature_utils; +// } +// mod fee { +// mod test_fee_handler; +// mod test_fee_utils; +// } +// mod market { +// mod test_market_factory; +// mod test_market_token; +// mod test_market_utils; +// } +// mod nonce { +// mod test_nonce_utils; +// } +// mod oracle { +// mod test_oracle; +// } +// mod order { +// mod test_base_order_utils; +// // mod test_increase_order_utils; +// mod test_order; +// } +// mod position { +// mod test_decrease_position_utils; +// mod test_decrease_position_swap_utils; +// mod test_position_utils; +// } +// mod price { +// mod test_price; +// } +// mod pricing { +// mod test_position_pricing_utils; +// mod test_swap_pricing_utils; +// } +// mod reader { +// mod test_reader; +// } +// mod role { +// mod test_role_module; +// mod test_role_store; +// } +// mod router { +// mod test_router; +// } +// mod swap { +// mod test_swap_handler; +// } +// mod utils { +// mod test_account_utils; +// mod test_arrays; +// mod test_basic_multicall; +// mod test_calc; +// mod test_enumerable_set; +// mod test_precision; +// mod test_reentrancy_guard; +// mod test_starknet_utils; +// // mod test_u128_mask; +// // mod test_i128; +// mod test_serializable_dict; +// } +// mod withdrawal { +// mod test_withdrawal_vault; +// } +// mod mock { +// mod test_governable; +// mod test_referral_storage; +// } +// mod referral { +// mod test_referral_utils; +// } mod integration { mod test_deposit_withdrawal; From b595f85974aa1f955fca464990481e7555e8bdd4 Mon Sep 17 00:00:00 2001 From: sparqet Date: Mon, 29 Apr 2024 10:37:58 +0200 Subject: [PATCH 3/5] fix add set_primary_prices --- src/oracle/oracle.cairo | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/oracle/oracle.cairo b/src/oracle/oracle.cairo index 8fe76f73..8b4f64df 100644 --- a/src/oracle/oracle.cairo +++ b/src/oracle/oracle.cairo @@ -37,6 +37,8 @@ trait IOracle { pragma_address: ContractAddress, ); + fn set_primary_prices(ref self: TContractState, token: ContractAddress, price: u256); + /// Get the primary price of a token. /// # Arguments /// * `token` - The token to get the price for. @@ -216,6 +218,10 @@ mod Oracle { self.eth_price.write(Price { min: new_price, max: new_price }) } + fn set_primary_prices(ref self: ContractState, token: ContractAddress, price: u256) { + self.primary_prices.write(token, Price { min: price, max: price }); + } + fn get_primary_price(self: @ContractState, token: ContractAddress) -> Price { if token.is_zero() { return Price { min: 0, max: 0 }; @@ -227,6 +233,7 @@ mod Oracle { if token == contract_address_const::<'USDC'>() { return Price { min: 1, max: 1 }; } + if price.is_zero() { OracleError::EMPTY_PRIMARY_PRICE(); } From c2fec17d1407dd742bb598dd9d41862133900f91 Mon Sep 17 00:00:00 2001 From: sparqet Date: Sat, 4 May 2024 22:57:02 +0400 Subject: [PATCH 4/5] size oracle reduced --- src/oracle/oracle.cairo | 679 +--------------------------------------- 1 file changed, 2 insertions(+), 677 deletions(-) diff --git a/src/oracle/oracle.cairo b/src/oracle/oracle.cairo index ead61447..768f59b5 100644 --- a/src/oracle/oracle.cairo +++ b/src/oracle/oracle.cairo @@ -46,40 +46,8 @@ trait IOracle { /// The primary price of a token. fn get_primary_price(self: @TContractState, token: ContractAddress) -> Price; - /// Get the stable price of a token. - /// # Arguments - /// * `token` - The token to get the price for. - /// # Returns - /// The stable price of a token. - fn get_stable_price( - self: @TContractState, data_store: IDataStoreDispatcher, token: ContractAddress - ) -> u256; - - /// Get the multiplier value to convert the external price feed price to the price of 1 unit of the token - /// represented with 30 decimals. - /// For example, if USDC has 6 decimals and a price of 1 USD, one unit of USDC would have a price of - /// 1 / (10 ^ 6) * (10 ^ 30) => 1 * (10 ^ 24) - /// if the external price feed has 8 decimals, the price feed price would be 1 * (10 ^ 8) - /// in this case the priceFeedMultiplier should be 10 ^ 46 - /// the conversion of the price feed price would be 1 * (10 ^ 8) * (10 ^ 46) / (10 ^ 30) => 1 * (10 ^ 24) - /// formula for decimals for price feed multiplier: 60 - (external price feed decimals) - (token decimals) - /// # Arguments - /// * `data_store` - The data store dispatcher. - /// * `token` - The token to get the price for. - /// # Returns - /// The price feed multiplier. - fn get_price_feed_multiplier( - self: @TContractState, data_store: IDataStoreDispatcher, token: ContractAddress, - ) -> u256; - fn set_price_testing_eth(ref self: TContractState, new_price: u256); - /// Validate prices in `params` for oracles. - /// # Arguments - /// * `data_store` - The `DataStore` contract dispatcher. - /// * `params` - The parameters used to set prices in oracle. - fn validate_prices( - self: @TContractState, data_store: IDataStoreDispatcher, params: SetPricesParams, - ) -> Array; + } /// A price that has been validated in validate_prices(). @@ -223,7 +191,6 @@ mod Oracle { self.initialize(role_store_address, oracle_store_address, pragma_address); } - // ************************************************************************* // EXTERNAL FUNCTIONS // ************************************************************************* @@ -246,39 +213,6 @@ mod Oracle { self.price_feed.write(IPriceFeedDispatcher { contract_address: pragma_address }); } - fn set_prices( - ref self: ContractState, - data_store: IDataStoreDispatcher, - event_emitter: IEventEmitterDispatcher, - params: SetPricesParams, - ) { - let state: RoleModule::ContractState = RoleModule::unsafe_new_contract_state(); - IRoleModule::only_controller(@state); - let tokens_with_prices_len = self.tokens_with_prices.read().len(); - if !tokens_with_prices_len.is_zero() { - OracleError::NON_EMPTY_TOKENS_WITH_PRICES(tokens_with_prices_len); - }; - - self.set_prices_from_price_feeds(data_store, event_emitter, @params.price_feed_tokens); - // it is possible for transactions to be executed using just params.priceFeedTokens - // in this case if params.tokens is empty, the function can return - if params.tokens.len().is_zero() { - return; - } - - self.set_prices_(data_store, event_emitter, params); - } - - // Set the primary price - // Arguments - // * `token` - The token to set the price for. - // * `price` - The price value to set to. - fn set_primary_price(ref self: ContractState, token: ContractAddress, price: Price,) { - let state: RoleModule::ContractState = RoleModule::unsafe_new_contract_state(); - IRoleModule::only_controller(@state); - self.set_primary_price_(token, price); - } - // Only for testing fn set_price_testing_eth(ref self: ContractState, new_price: u256) { self.eth_price.write(Price { min: new_price, max: new_price }) @@ -299,30 +233,6 @@ mod Oracle { } price } - - - fn get_stable_price( - self: @ContractState, data_store: IDataStoreDispatcher, token: ContractAddress - ) -> u256 { - data_store.get_u256(keys::stable_price_key(token)) - } - - fn get_price_feed_multiplier( - self: @ContractState, data_store: IDataStoreDispatcher, token: ContractAddress, - ) -> u256 { - let multiplier = data_store.get_u256(keys::price_feed_multiplier_key(token)); - - if multiplier.is_zero() { - OracleError::EMPTY_PRICE_FEED_MULTIPLIER(); - } - multiplier - } - - fn validate_prices( - self: @ContractState, data_store: IDataStoreDispatcher, params: SetPricesParams, - ) -> Array { - self.validate_prices_(data_store, params) - } } // ************************************************************************* @@ -330,562 +240,7 @@ mod Oracle { // ************************************************************************* #[generate_trait] impl InternalImpl of InternalTrait { - /// Validate and set prices. - /// The _set_prices() function is a helper function that is called by the - /// setPrices() function. It takes in several parameters: a DataStore contract - /// instance, an EventEmitter contract instance, an array of signers, and an - /// OracleUtils.SetPricesParams struct containing information about the tokens - /// and their prices. - /// The function first initializes a SetPricesCache struct to store some temporary - /// values that will be used later in the function. It then loops through the array - /// of tokens and sets the corresponding values in the cache struct. For each token, - /// the function also loops through the array of signers and validates the signatures - /// for the min and max prices for that token. If the signatures are valid, the - /// function calculates the median min and max prices and sets them in the DataStore - /// contract. - /// Finally, the function emits an event to signal that the prices have been set. - /// # Arguments - /// * `data_store` - The data store. - /// * `event_emitter` - The event emitter. - /// * `params` - The set price params. - fn set_prices_( - ref self: ContractState, - data_store: IDataStoreDispatcher, - event_emitter: IEventEmitterDispatcher, - params: SetPricesParams, - ) { - let validated_prices = self.validate_prices(data_store, params); - - let mut len = 0; - loop { - if len == validated_prices.len() { - break; - } - - let validated_price = *validated_prices.at(len); - if !self.primary_prices.read(validated_price.token).is_zero() { - OracleError::DUPLICATED_TOKEN_PRICE(); - } - self - .emit_oracle_price_updated( - event_emitter, - validated_price.token, - validated_price.min, - validated_price.max, - false - ); - self - .set_primary_price_( - validated_price.token, - Price { min: validated_price.min, max: validated_price.max } - ); - len += 1; - }; - } - - /// Validate prices in params. - /// # Arguments - /// * `data_store` - The data store. - /// * `params` - The set price params. - fn validate_prices_( - self: @ContractState, data_store: IDataStoreDispatcher, params: SetPricesParams, - ) -> Array { - let signers = self.get_signers_(data_store, @params); - - let mut cache: SetPricesCache = Default::default(); - cache - .min_block_confirmations = data_store - .get_u256(keys::min_oracle_block_confirmations()) - .try_into() - .expect('get_u256 into u64 failed'); - - cache - .max_price_age = data_store - .get_u256(keys::max_oracle_price_age()) - .try_into() - .expect('get_u256 into u64 failed'); - - cache - .max_ref_price_deviation_factor = data_store - .get_u256(keys::max_oracle_ref_price_deviation_factor()); - - let mut i = 0; - loop { - let mut report_info: ReportInfo = Default::default(); - let mut inner_cache: SetPricesInnerCache = Default::default(); - if i == params.tokens.len() { - break; - } - - report_info - .min_oracle_block_number = - oracle_utils::get_uncompacted_oracle_block_number( - params.compacted_min_oracle_block_numbers.span(), i.into() - ); - - report_info - .max_oracle_block_number = - oracle_utils::get_uncompacted_oracle_block_number( - params.compacted_max_oracle_block_numbers.span(), i.into() - ); - - if report_info.min_oracle_block_number > report_info.max_oracle_block_number { - OracleError::INVALID_MIN_MAX_BLOCK_NUMBER( - report_info.min_oracle_block_number, report_info.max_oracle_block_number - ); - } - - report_info - .oracle_timestamp = - oracle_utils::get_uncompacted_oracle_timestamp( - params.compacted_oracle_timestamps.span(), i - ); - if report_info.min_oracle_block_number > get_block_number() { - OracleError::INVALID_BLOCK_NUMBER( - report_info.min_oracle_block_number, get_block_number() - ); - } - - if report_info.oracle_timestamp + cache.max_price_age < get_block_timestamp() { - OracleError::MAX_PRICE_EXCEEDED( - report_info.oracle_timestamp, get_block_timestamp() - ); - } - - if report_info.min_oracle_block_number < cache.prev_min_oracle_block_number { - OracleError::BLOCK_NUMBER_NOT_SORTED( - report_info.min_oracle_block_number, cache.prev_min_oracle_block_number - ); - } - - cache.prev_min_oracle_block_number = report_info.min_oracle_block_number; - - if get_block_number() - - report_info.max_oracle_block_number <= cache.min_block_confirmations { - report_info - .block_hash = get_block_hash_syscall(report_info.max_oracle_block_number) - .unwrap_syscall(); - } - - report_info.token = *params.tokens.at(i); - - report_info - .precision = - pow( - 10, - oracle_utils::get_uncompacted_decimal( - params.compacted_decimals.span(), i.into() - ) - .try_into() - .expect('u256 into u32 failed') - ); - - report_info - .token_oracle_type = data_store - .get_felt252(keys::oracle_type_key(report_info.token)); - - let mut j = 0; - let signers_len = signers.len(); - let compacted_min_prices_span = params.compacted_min_prices.span(); - let compacted_max_prices_span = params.compacted_max_prices.span(); - loop { - if j == signers_len { - break; - } - inner_cache.price_index = (i * signers_len + j).into(); - inner_cache - .min_prices - .append( - oracle_utils::get_uncompacted_price( - compacted_min_prices_span, inner_cache.price_index - ) - ); - - inner_cache - .max_prices - .append( - oracle_utils::get_uncompacted_price( - compacted_max_prices_span, inner_cache.price_index - ) - ); - if j != 0 { - if *inner_cache.min_prices.at(j - 1) > *inner_cache.min_prices.at(j) { - OracleError::MIN_PRICES_NOT_SORTED( - report_info.token, - *inner_cache.min_prices.at(j), - *inner_cache.min_prices.at(j - 1) - ); - } - - if *inner_cache.max_prices.at(j - 1) > *inner_cache.max_prices.at(j) { - OracleError::MAX_PRICES_NOT_SORTED( - report_info.token, - *inner_cache.max_prices.at(j), - *inner_cache.max_prices.at(j - 1) - ); - } - } - j += 1; - }; - - let compacted_min_indexes_span = params.compacted_min_prices_indexes.span(); - let compacted_max_indexes_span = params.compacted_max_prices_indexes.span(); - let inner_cache_save = @inner_cache; - let signatures_span = params.signatures.span(); - let signers_span = signers.span(); - let mut j = 0; - loop { - if j == signers_len { - break; - } - - inner_cache.signature_index = (i * signers_len + j).into(); - - inner_cache - .min_price_index = - oracle_utils::get_uncompacted_price_index( - compacted_min_indexes_span, inner_cache.signature_index - ); - inner_cache - .max_price_index = - oracle_utils::get_uncompacted_price_index( - compacted_max_indexes_span, inner_cache.signature_index - ); - if inner_cache.signature_index >= signatures_span.len() { - OracleError::ARRAY_OUT_OF_BOUNDS_FELT252( - signatures_span, inner_cache.signature_index, 'signatures' - ); - } - if inner_cache.min_price_index >= inner_cache.min_prices.len().into() { - OracleError::ARRAY_OUT_OF_BOUNDS_U256( - inner_cache.min_prices.span(), inner_cache.min_price_index, 'min_prices' - ); - } - - if inner_cache.max_price_index >= inner_cache.max_prices.len().into() { - OracleError::ARRAY_OUT_OF_BOUNDS_U256( - inner_cache.max_prices.span(), inner_cache.max_price_index, 'max_prices' - ); - } - - // since minPrices, maxPrices have the same length as the signers array - // and the signers array length is less than MAX_SIGNERS - // minPriceIndexMask and maxPriceIndexMask should be able to store the indexes - // using Uint256Mask - validate_unique_and_set_index( - ref inner_cache.min_price_index_mask, inner_cache.min_price_index - ); - - validate_unique_and_set_index( - ref inner_cache.max_price_index_mask, inner_cache.max_price_index - ); - - report_info - .min_price = *inner_cache - .min_prices - .at(inner_cache.min_price_index.try_into().expect('array at failed')); - - report_info - .max_price = *inner_cache - .max_prices - .at(inner_cache.max_price_index.try_into().expect('array at failed')); - - if report_info.min_price > report_info.max_price { - OracleError::INVALID_SIGNER_MIN_MAX_PRICE( - report_info.min_price, report_info.max_price - ); - } - // oracle_utils::validate_signer( - // self.get_salt(), - // report_info, - // *signatures_span.at(inner_cache.signature_index), - // signers_span.at(j) - // ); - - j += 1; - }; - - let median_min_price = arrays::get_median(inner_cache_save.min_prices.span()) - * report_info.precision; - - let median_max_price = arrays::get_median(inner_cache_save.max_prices.span()) - * report_info.precision; - - let (has_price_feed, ref_price) = self - .get_price_feed_price(data_store, report_info.token); - - if has_price_feed { - self - .validate_ref_price( - report_info.token, - median_min_price, - ref_price, - cache.max_ref_price_deviation_factor - ); - - self - .validate_ref_price( - report_info.token, - median_max_price, - ref_price, - cache.max_ref_price_deviation_factor - ); - } - - if median_min_price.is_zero() || median_max_price.is_zero() { - OracleError::INVALID_ORACLE_PRICE(report_info.token); - } - - if median_min_price > median_max_price { - OracleError::INVALID_MEDIAN_MIN_MAX_PRICE(median_min_price, median_max_price); - } - - let validated_price = ValidatedPrice { - token: report_info.token, - min: median_min_price, - max: median_max_price, - timestamp: report_info.oracle_timestamp, - min_block_number: report_info.min_oracle_block_number, - max_block_number: report_info.max_oracle_block_number - }; - - cache.validated_prices.append(validated_price); - - i += 1; - }; - cache.validated_prices - } - - /// Get the signers - /// # Arguments - /// * `data_store` - The data store dispatcher. - /// * `token` - The token to get the price for. - /// # Returns - /// The signers - fn get_signers_( - self: @ContractState, data_store: IDataStoreDispatcher, params: @SetPricesParams, - ) -> Array { - let mut signers: Array = array![]; - - let signers_len = *params.signer_info & bits::BITMASK_16; - if signers_len < data_store.get_u256(keys::min_oracle_signers()) { - OracleError::MIN_ORACLE_SIGNERS( - signers_len, data_store.get_u256(keys::min_oracle_signers()) - ); - } - - if signers_len > MAX_SIGNERS { - OracleError::MAX_ORACLE_SIGNERS(signers_len, MAX_SIGNERS); - } - - let mut signers_index_mask = Mask { bits: 0 }; - - let mut len = 0; - loop { - if len == signers_len { - break; - } - - let signer_index: u256 = BitShift::shr( - *params.signer_info, (8 + 8 * len) & bits::BITMASK_16 - ); - - if signer_index >= MAX_SIGNER_INDEX { - OracleError::MAX_SIGNERS_INDEX(signer_index, MAX_SIGNER_INDEX); - } - - signers_index_mask.validate_unique_and_set_index(signer_index); - - signers - .append( - self - .oracle_store - .read() - .get_signer(signer_index.try_into().expect('u256 into u32 failed')) - ); - - if (*signers.at(len.try_into().expect('u256 into u32 failed'))).is_zero() { - OracleError::EMPTY_SIGNER(signer_index); - } - - len += 1; - }; - - signers - } - - /// Compute a salt for validate_signer(). - /// # Returns - /// The computed salt. - fn get_salt(self: @ContractState,) -> felt252 { - let data: Array = array![ - starknet::info::get_tx_info().unbox().chain_id, 'xget-oracle-v1' - ]; - poseidon_hash_span(data.span()) - } - - /// Validate that price does not deviate too much from ref_price. - /// # Arguments - /// * `token` - The token the price is check from. - /// * `price` - The price to validate. - /// * `ref_price` - The reference price. - /// * `max_ref_price_deviation_from_factor` - The max ref_price deviation factor allowed. - fn validate_ref_price( - self: @ContractState, - token: ContractAddress, - price: u256, - ref_price: u256, - max_ref_price_deviation_factor: u256, - ) { - let diff = calc::diff(price, ref_price); - - let diff_factor = precision::to_factor(diff, ref_price); - if diff_factor > max_ref_price_deviation_factor { - OracleError::MAX_REFPRICE_DEVIATION_EXCEEDED( - token, price, ref_price, max_ref_price_deviation_factor - ); - } - } - - /// Set the primary price. - /// # Arguments - /// * `token` - The token to set the price for. - /// * `price` - The price value to set to. - fn set_primary_price_(ref self: ContractState, token: ContractAddress, price: Price) { - match self.get_token_with_price_index(token) { - Option::Some(i) => (), - Option::None(_) => { - self.primary_prices.write(token, price); - - let mut tokens_with_prices = self.tokens_with_prices.read(); - let index_of_zero = self.get_token_with_price_index(Zeroable::zero()); - // If an entry with zero address is found the entry is set to the new token, - // otherwise the new token is appended to the list. This is to avoid the list - // to grow indefinitely. - match index_of_zero { - Option::Some(i) => { tokens_with_prices.set(i, token); }, - Option::None => { tokens_with_prices.append(token); } - } - } - } - } - - /// Remove the primary price. - /// # Arguments - /// * `token` - The token to set the price for. - fn remove_primary_price(ref self: ContractState, token: ContractAddress) { - self.primary_prices.write(token, Zeroable::zero()); - let mut tokens_prices = self.tokens_with_prices.read(); - tokens_prices.pop_front(); - self.tokens_with_prices.write(tokens_prices); - } - - /// Get the price feed prices. - /// There is a small risk of stale pricing due to latency in price updates or if the chain is down. - /// This is meant to be for temporary use until low latency price feeds are supported for all tokens. - /// # Arguments - /// * `data_store` - The data store. - /// * `token` - The token to get the price for. - /// # Returns - /// The price feed multiplier. - fn get_price_feed_price( - self: @ContractState, data_store: IDataStoreDispatcher, token: ContractAddress, - ) -> (bool, u256) { - let token_id = data_store.get_token_id(token); - if token_id == 0 { - return (false, 0); - } - let response = self.price_feed.read().get_data_median(DataType::SpotEntry(token_id)); - - if response.price <= 0 { - OracleError::INVALID_PRICE_FEED(token, response.price); - } - - let heart_beat_duration = data_store - .get_u256(keys::price_feed_heartbeat_duration_key(token)); - - let current_timestamp = get_block_timestamp(); - if current_timestamp > response.last_updated_timestamp && current_timestamp - - response - .last_updated_timestamp > heart_beat_duration - .try_into() - .expect('u256 into u32 failed') { - OracleError::PRICE_FEED_NOT_UPDATED( - token, response.last_updated_timestamp, heart_beat_duration - ); - } - - let precision_ = self.get_price_feed_multiplier(data_store, token); - let adjusted_price = precision::mul_div( - response.price, precision_, precision::FLOAT_PRECISION - ); - - (true, adjusted_price) - } - - /// Set prices using external price feeds to save costs for tokens with stable prices. - /// # Arguments - /// * `data_store` - The data store. - /// * `event_emitter` - The event emitter. - /// * `price_feed_tokens` - The tokens to set the prices using the price feeds for. - fn set_prices_from_price_feeds( - ref self: ContractState, - data_store: IDataStoreDispatcher, - event_emitter: IEventEmitterDispatcher, - price_feed_tokens: @Array, - ) { - let self_copy = @self; - let mut len = 0; - - loop { - if len == price_feed_tokens.len() { - break; - } - - let token = *price_feed_tokens.at(len); - - let stored_price = self.primary_prices.read(token); - if !stored_price.is_zero() { - OracleError::PRICE_ALREADY_SET(token, stored_price.min, stored_price.max); - } - - let (has_price_feed, price) = self_copy.get_price_feed_price(data_store, token); - - if (!has_price_feed) { - OracleError::EMPTY_PRICE_FEED(token); - } - - let stable_price = self.get_stable_price(data_store, token); - - let mut price_props: Price = Zeroable::zero(); - - if !stable_price.is_zero() { - price_props = - Price { - min: if price < stable_price { - price - } else { - stable_price - }, - max: if price < stable_price { - stable_price - } else { - price - } - } - } else { - price_props = Price { min: price, max: price } - } - - self.set_primary_price_(token, price_props); - - self - .emit_oracle_price_updated( - event_emitter, token, price_props.min, price_props.max, true - ); - len += 1; - }; - } + /// Emits an `OraclePriceUpdated` event for a specific token. /// # Parameters /// * `event_emitter`: Dispatcher used for emitting events. @@ -903,36 +258,6 @@ mod Oracle { ) { event_emitter.emit_oracle_price_updated(token, min_price, max_price, is_price_feed); } - - /// Returns the index of a given `token` in the `tokens_with_prices` list. - /// # Arguments - /// * `token` - A `ContractAddress` representing the token whose index we want to find. - /// # Returns - /// * `Option` - Returns `Some(index)` if the token is found. - /// Returns `None` if the token is not found. - fn get_token_with_price_index( - self: @ContractState, token: ContractAddress - ) -> Option { - let mut tokens_with_prices = self.tokens_with_prices.read(); - let mut index = Option::None; - let mut len = 0; - loop { - if len == tokens_with_prices.len() { - break; - } - let token_with_price = tokens_with_prices.get(len); - match token_with_price { - Option::Some(t) => { - if token_with_price.unwrap() == token { - index = Option::Some(len); - } - }, - Option::None => (), - } - len += 1; - }; - index - } } } From 756a3d55947c9674b188c960f9199c4b1ca27e41 Mon Sep 17 00:00:00 2001 From: sparqet Date: Sat, 4 May 2024 23:15:39 +0400 Subject: [PATCH 5/5] fix set prices in tests --- tests/integration/swap_test.cairo | 7 +++++-- tests/integration/test_deposit_withdrawal.cairo | 6 ++++-- tests/integration/test_long_integration.cairo | 10 ++++++---- tests/integration/test_short_integration.cairo | 3 ++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/integration/swap_test.cairo b/tests/integration/swap_test.cairo index c03b5dc7..9f6de9f8 100644 --- a/tests/integration/swap_test.cairo +++ b/tests/integration/swap_test.cairo @@ -99,7 +99,9 @@ fn test_deposit_market_integration() { keys::max_pool_amount_key(market.market_token, market.short_token), 500000000000000000 ); - oracle.set_price_testing_eth(5000); + oracle.set_primary_prices(market.long_token, 5000); + oracle.set_primary_prices(market.short_token, 1); + // Fill the pool. IERC20Dispatcher { contract_address: market.long_token }.mint(market.market_token, 50000000000); IERC20Dispatcher { contract_address: market.short_token } @@ -427,7 +429,8 @@ fn test_swap_18_deposit_market_integration() { 2500000000000000000000000000000000000000000000 ); - oracle.set_price_testing_eth(5000); + oracle.set_primary_prices(market.long_token, 5000); + oracle.set_primary_prices(market.short_token, 1); 'fill the pool'.print(); // Fill the pool. diff --git a/tests/integration/test_deposit_withdrawal.cairo b/tests/integration/test_deposit_withdrawal.cairo index 9d1b6ac4..76e35b7f 100644 --- a/tests/integration/test_deposit_withdrawal.cairo +++ b/tests/integration/test_deposit_withdrawal.cairo @@ -507,7 +507,8 @@ fn test_deposit_market_integration() { keys::max_pool_amount_key(market.market_token, market.short_token), 500000000000000000 ); - oracle.set_price_testing_eth(5000); + oracle.set_primary_prices(market.long_token, 5000); + oracle.set_primary_prices(market.short_token, 1); // Fill the pool. IERC20Dispatcher { contract_address: market.long_token }.mint(market.market_token, 50000000000); @@ -685,7 +686,8 @@ fn test_deposit_withdraw_integration() { keys::max_pool_amount_key(market.market_token, market.short_token), 500000000000000000 ); - oracle.set_price_testing_eth(5000); + oracle.set_primary_prices(market.long_token, 5000); + oracle.set_primary_prices(market.short_token, 1); // Fill the pool. IERC20Dispatcher { contract_address: market.long_token }.mint(market.market_token, 50000000000); diff --git a/tests/integration/test_long_integration.cairo b/tests/integration/test_long_integration.cairo index 40d33e36..1982e44a 100644 --- a/tests/integration/test_long_integration.cairo +++ b/tests/integration/test_long_integration.cairo @@ -112,7 +112,7 @@ const INITIAL_TOKENS_MINTED: felt252 = 1000; // keys::max_pool_amount_key(market.market_token, market.short_token), 500000000000000000 // ); -// oracle.set_price_testing_eth(5000); +// oracle.set_primary_prices(market.long_token, 5000); // // Fill the pool. // IERC20Dispatcher { contract_address: market.long_token }.mint(market.market_token, 50000000000); @@ -382,7 +382,8 @@ const INITIAL_TOKENS_MINTED: felt252 = 1000; // .balance_of(caller_address); // 'balance of mkt before'.print(); // balance_of_mkt_before.print(); -// oracle.set_price_testing_eth(6000); +// oracle.set_primary_prices(market.long_token, 6000); + // start_prank(market.market_token, caller_address); // start_prank(market.long_token, caller_address); @@ -527,7 +528,8 @@ fn test_long_decimals_market_integration() { 50000000000000000000000000000000000000000000000 ); - oracle.set_price_testing_eth(5000); + oracle.set_primary_prices(market.long_token, 5000); + oracle.set_primary_prices(market.short_token, 1); 'fill the pool'.print(); // Fill the pool. @@ -788,7 +790,7 @@ fn test_long_decimals_market_integration() { 'size in usd'.print(); first_position.size_in_usd.print(); 'OKAAAAAYYYYYY'.print(); - oracle.set_price_testing_eth(6000); + oracle.set_primary_prices(market.long_token, 6000); let first_position_after_pump = data_store.get_position(position_key_1); 'size tokens after pump'.print(); first_position_after_pump.size_in_tokens.print(); diff --git a/tests/integration/test_short_integration.cairo b/tests/integration/test_short_integration.cairo index dff36b8b..b5e6c6c6 100644 --- a/tests/integration/test_short_integration.cairo +++ b/tests/integration/test_short_integration.cairo @@ -113,7 +113,8 @@ fn test_short_market_integration() { keys::max_pool_amount_key(market.market_token, market.short_token), 500000000000000000 ); - oracle.set_price_testing_eth(5000); + oracle.set_primary_prices(market.long_token, 5000); + oracle.set_primary_prices(market.short_token, 1); // Fill the pool. IERC20Dispatcher { contract_address: market.long_token }.mint(market.market_token, 50000000000);