diff --git a/.all-contributorsrc b/.all-contributorsrc
index dd7dae92..59270959 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -315,8 +315,35 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "JeanWoked",
+ "name": "JeanWoked",
+ "avatar_url": "https://avatars.githubusercontent.com/u/149107619?v=4",
+ "profile": "https://github.com/JeanWoked",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "vuittont60",
+ "name": "vuittont60",
+ "avatar_url": "https://avatars.githubusercontent.com/u/81072379?v=4",
+ "profile": "https://github.com/vuittont60",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "MavericksFive",
+ "name": "Arnaud Berger",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95299145?v=4",
+ "profile": "https://github.com/MavericksFive",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
"linkToUsage": false
-}
\ No newline at end of file
+}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index bca716e1..4485dcfb 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -4,7 +4,6 @@ on: [push, pull_request]
env:
SCARB_VERSION: 0.7.0
- STARKNET_FOUNDRY_VERSION: 0.8.3
jobs:
check:
@@ -14,6 +13,11 @@ jobs:
- uses: software-mansion/setup-scarb@v1
with:
scarb-version: "0.7.0"
+ - uses: foundry-rs/setup-snfoundry@v1
+ with:
+ starknet-foundry-version: 0.8.3
+ - name: Run cairo tests
+ run: snforge test
# - name: Set up Scarb
#ses: software-mansion/setup-scarb@v1
# Install Scarb from a nightly release
@@ -22,7 +26,3 @@ jobs:
# wget https://github.com/software-mansion/scarb-nightlies/releases/download/${NIGHTLY_DATE}/scarb-${NIGHTLY_DATE}-x86_64-unknown-linux-gnu.tar.gz
# tar -xvf scarb-${NIGHTLY_DATE}-x86_64-unknown-linux-gnu.tar.gz
# sudo mv scarb-v${SCARB_VERSION}-x86_64-unknown-linux-gnu/bin/scarb /usr/local/bin
- - name: Install starknet foundry
- run: curl -L https://raw.githubusercontent.com/foundry-rs/starknet-foundry/master/scripts/install.sh | sh -s -- -v ${STARKNET_FOUNDRY_VERSION}
- - name: Run cairo tests
- run: snforge
diff --git a/README.md b/README.md
index 2f820537..eabb5abd 100644
--- a/README.md
+++ b/README.md
@@ -150,6 +150,11 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
VictorONN 💻 |
kasteph 💻 |
Khaeljy 💻 |
+ JeanWoked 💻 |
+
+
+ vuittont60 💻 |
+ Arnaud Berger 💻 |
diff --git a/book/src/continuous-integration/workflows.md b/book/src/continuous-integration/workflows.md
index aaa66638..6cf0cfe5 100644
--- a/book/src/continuous-integration/workflows.md
+++ b/book/src/continuous-integration/workflows.md
@@ -198,18 +198,18 @@ on:
- main
env:
SCARB_VERSION: 0.7.0
- STARKNET_FOUNDRY_VERSION: 0.8.3
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
+ - uses: foundry-rs/setup-snfoundry@v1
+ with:
+ starknet-foundry-version: 0.9.0
- uses: software-mansion/setup-scarb@v1
with:
scarb-version: "0.7.0"
- - name: Install starknet foundry
- run: curl -L https://raw.githubusercontent.com/foundry-rs/starknet-foundry/master/scripts/install.sh | sh -s -- -v ${STARKNET_FOUNDRY_VERSION}
- name: Run cairo tests
run: snforge
```
\ No newline at end of file
diff --git a/src/adl/adl_utils.cairo b/src/adl/adl_utils.cairo
index 27fecd61..d2d71dfa 100644
--- a/src/adl/adl_utils.cairo
+++ b/src/adl/adl_utils.cairo
@@ -250,15 +250,7 @@ fn set_latest_adl_block(
fn get_adl_enabled(
data_store: IDataStoreDispatcher, market: ContractAddress, is_long: bool
) -> bool { // TODO
- let result = data_store.get_bool(keys::is_adl_enabled_key(market, is_long));
- match result {
- Option::Some(data) => {
- return data;
- },
- Option::None => {
- return false;
- }
- }
+ data_store.get_bool(keys::is_adl_enabled_key(market, is_long))
}
/// Set whether ADL is enabled.
diff --git a/src/adl/error.cairo b/src/adl/error.cairo
index 78adb5a5..30ca0f29 100644
--- a/src/adl/error.cairo
+++ b/src/adl/error.cairo
@@ -3,5 +3,5 @@ mod AdlError {
'block_no_smaller_than_required';
const INVALID_SIZE_DELTA_FOR_ADL: felt252 = 'invalid_size_delta_for_adl';
const ADL_NOT_ENABLED: felt252 = 'adl_not_enabled';
- const POSTION_NOT_VALID: felt252 = 'position_not_valid';
+ const POSITION_NOT_VALID: felt252 = 'position_not_valid';
}
diff --git a/src/data/data_store.cairo b/src/data/data_store.cairo
index 3da4e1a6..f2e2263e 100644
--- a/src/data/data_store.cairo
+++ b/src/data/data_store.cairo
@@ -161,7 +161,7 @@ trait IDataStore {
/// * `key` - The key to get the value for.
/// # Returns
/// The value for the given key.
- fn get_bool(self: @TContractState, key: felt252) -> Option;
+ fn get_bool(self: @TContractState, key: felt252) -> bool;
/// Set a bool value for the given key.
/// # Arguments
@@ -511,7 +511,7 @@ mod DataStore {
u128_values: LegacyMap::,
i128_values: LegacyMap::,
address_values: LegacyMap::,
- bool_values: LegacyMap::>,
+ bool_values: LegacyMap::,
/// Market storage
market_values: LegacyMap::,
markets: List,
@@ -800,7 +800,7 @@ mod DataStore {
// *************************************************************************
// Bool related functions.
// *************************************************************************
- fn get_bool(self: @ContractState, key: felt252) -> Option {
+ fn get_bool(self: @ContractState, key: felt252) -> bool {
self.bool_values.read(key)
}
@@ -808,14 +808,14 @@ mod DataStore {
// Check that the caller has permission to set the value.
self.role_store.read().assert_only_role(get_caller_address(), role::CONTROLLER);
// Set the value.
- self.bool_values.write(key, Option::Some(value));
+ self.bool_values.write(key, value);
}
fn remove_bool(ref self: ContractState, key: felt252) {
// Check that the caller has permission to delete the value.
self.role_store.read().assert_only_role(get_caller_address(), role::CONTROLLER);
// Delete the value.
- self.bool_values.write(key, Option::None);
+ self.bool_values.write(key, false);
}
// *************************************************************************
diff --git a/src/exchange/withdrawal_handler.cairo b/src/exchange/withdrawal_handler.cairo
index e6924a84..7b22fc42 100644
--- a/src/exchange/withdrawal_handler.cairo
+++ b/src/exchange/withdrawal_handler.cairo
@@ -174,7 +174,7 @@ mod WithdrawalHandler {
global_reentrancy_guard::non_reentrant_before(data_store); // Initiates re-entrancy
- let starting_gas = starknet_utils::sn_gasleft(array![100]); // Returns 100 for now,
+ let starting_gas = starknet_utils::sn_gasleft(array![200]); // Returns 200 for now,
let withdrawal = data_store.get_withdrawal(key);
feature_utils::validate_feature(
diff --git a/src/feature/feature_utils.cairo b/src/feature/feature_utils.cairo
index f7b52440..c2a1e84f 100644
--- a/src/feature/feature_utils.cairo
+++ b/src/feature/feature_utils.cairo
@@ -16,10 +16,7 @@ use satoru::feature::error::FeatureError;
/// # Returns
/// whether the feature is disabled.
fn is_feature_disabled(data_store: IDataStoreDispatcher, key: felt252) -> bool {
- match data_store.get_bool(key) {
- Option::Some(value) => value,
- Option::None => false
- }
+ data_store.get_bool(key)
}
/// Validate whether a feature is enabled, reverts if the feature is disabled.
diff --git a/src/market/error.cairo b/src/market/error.cairo
index e5ae3fa0..9305eb77 100644
--- a/src/market/error.cairo
+++ b/src/market/error.cairo
@@ -11,7 +11,6 @@ mod MarketError {
'empty_addr_market_balance_val';
const EMPTY_ADDRESS_TOKEN_BALANCE_VAL: felt252 = 'empty_addr_token_balance_val';
const INVALID_MARKET_TOKEN_BALANCE: felt252 = 'invalid_market_token_balance';
- const EmptyAddressInMarketTokenBalanceValidation: felt252 = 'EmptyAddressMarketBalanceVal';
const INVALID_POSITION_MARKET: felt252 = 'invalid_position_market';
const INVALID_COLLATERAL_TOKEN_FOR_MARKET: felt252 = 'invalid_coll_token_for_market';
const UNABLE_TO_GET_OPPOSITE_TOKEN: felt252 = 'unable_to_get_opposite_token';
diff --git a/src/market/market_utils.cairo b/src/market/market_utils.cairo
index bcf26d7f..a31f1d11 100644
--- a/src/market/market_utils.cairo
+++ b/src/market/market_utils.cairo
@@ -30,7 +30,7 @@ use satoru::utils::precision;
use satoru::utils::calc::{roundup_division, to_signed, sum_return_int_128, to_unsigned};
use satoru::position::position::Position;
use integer::u128_to_felt252;
-use satoru::utils::{i128::i128, error_utils};
+use satoru::utils::{i128::{i128, i128_neg}, error_utils};
use satoru::utils::precision::{apply_exponent_factor, float_to_wei, mul_div};
use satoru::data::keys::{skip_borrowing_fee_for_smaller_side, max_swap_path_length};
@@ -923,7 +923,7 @@ fn apply_delta_to_open_interest(
if is_long {
apply_delta_to_virtual_inventory_for_positions(
- data_store, event_emitter, *market.index_token, -delta
+ data_store, event_emitter, *market.index_token, i128_neg(delta)
);
} else {
apply_delta_to_virtual_inventory_for_positions(
@@ -1455,7 +1455,9 @@ fn apply_swap_impact_with_cap(
// if there is a positive impact, the impact pool amount should be reduced
// if there is a negative impact, the impact pool amount should be increased
- apply_delta_to_swap_impact_pool(data_store, event_emitter, market, token, -impact_amount);
+ apply_delta_to_swap_impact_pool(
+ data_store, event_emitter, market, token, i128_neg(impact_amount)
+ );
return impact_amount;
}
@@ -1799,6 +1801,7 @@ fn get_borrowing_fees(data_store: IDataStoreDispatcher, position: @Position) ->
let cumulative_borrowing_factor: u128 = get_cumulative_borrowing_factor(
@data_store, *position.market, *position.is_long
);
+
if (cumulative_borrowing_factor < *position.borrowing_factor) {
MarketError::UNEXCEPTED_BORROWING_FACTOR(
*position.borrowing_factor, cumulative_borrowing_factor
@@ -2427,8 +2430,7 @@ fn get_borrowing_factor_per_second(
// if skipBorrowingFeeForSmallerSide is true, and the longOpenInterest is exactly the same as the shortOpenInterest
// then the borrowing fee would be charged for both sides, this should be very rare
let skip_borrowing_fee_for_smaller_side: bool = data_store
- .get_bool(keys::skip_borrowing_fee_for_smaller_side())
- .unwrap();
+ .get_bool(keys::skip_borrowing_fee_for_smaller_side());
let market_snap = @market;
if (skip_borrowing_fee_for_smaller_side) {
@@ -2569,8 +2571,7 @@ fn validate_enabled_market(data_store: IDataStoreDispatcher, market: Market) {
assert(market.market_token != 0.try_into().unwrap(), MarketError::EMPTY_MARKET);
let is_market_disabled: bool = data_store
- .get_bool(keys::is_market_disabled_key(market.market_token))
- .unwrap();
+ .get_bool(keys::is_market_disabled_key(market.market_token));
if (is_market_disabled) {
MarketError::DISABLED_MARKET(is_market_disabled);
diff --git a/src/order/base_order_utils.cairo b/src/order/base_order_utils.cairo
index ebbe5528..f222495e 100644
--- a/src/order/base_order_utils.cairo
+++ b/src/order/base_order_utils.cairo
@@ -412,7 +412,7 @@ fn get_execution_price_for_decrease(
let adjusted_price_impact_usd = if is_long {
price_impact_usd
} else {
- -price_impact_usd
+ i128_neg(price_impact_usd)
};
if adjusted_price_impact_usd < Zeroable::zero()
diff --git a/src/order/error.cairo b/src/order/error.cairo
index 4e459faf..a311f480 100644
--- a/src/order/error.cairo
+++ b/src/order/error.cairo
@@ -14,7 +14,7 @@ mod OrderError {
const EMPTY_SIZE_DELTA_IN_TOKENS: felt252 = 'empty_size_delta_in_tokens';
const UNEXPECTED_MARKET: felt252 = 'unexpected market';
const INVALID_SIZE_DELTA_FOR_ADL: felt252 = 'invalid_size_delta_for_adl';
- const POSTION_NOT_VALID: felt252 = 'position_not_valid';
+ const POSITION_NOT_VALID: felt252 = 'position_not_valid';
const ORDER_ALREADY_FROZEN: felt252 = 'order_already_frozen';
diff --git a/src/position/increase_position_utils.cairo b/src/position/increase_position_utils.cairo
index 8c40a092..3d5680bb 100644
--- a/src/position/increase_position_utils.cairo
+++ b/src/position/increase_position_utils.cairo
@@ -144,7 +144,7 @@ fn increase_position(mut params: UpdatePositionParams, collateral_increment_amou
params.contracts.data_store,
params.contracts.event_emitter,
params.market.market_token,
- -cache.price_impact_amount
+ i128_neg(cache.price_impact_amount)
);
cache.next_position_size_in_usd = params.position.size_in_usd + params.order.size_delta_usd;
diff --git a/src/position/position_utils.cairo b/src/position/position_utils.cairo
index 9213d4ac..e1b2215c 100644
--- a/src/position/position_utils.cairo
+++ b/src/position/position_utils.cairo
@@ -460,6 +460,7 @@ fn is_position_liquiditable(
market_utils::get_cached_token_price(position.collateral_token, market, prices);
cache.collateral_usd = position.collateral_amount * cache.collateral_token_price.min;
+
// calculate the usdDeltaForPriceImpact for fully closing the position
cache.usd_delta_for_price_impact = calc::to_signed(position.size_in_usd, false);
cache
@@ -543,6 +544,7 @@ fn is_position_liquiditable(
precision::apply_factor_u128(position.size_in_usd, cache.min_collateral_factor),
true
);
+
if cache.remaining_collateral_usd <= cache.min_collateral_usd_for_leverage {
return (true, 'min collateral for leverage');
}
@@ -728,10 +730,10 @@ fn update_open_interest(
size_delta_usd
);
- market_utils::apply_delta_to_open_interest(
+ market_utils::apply_delta_to_open_interest_in_tokens(
params.contracts.data_store,
params.contracts.event_emitter,
- @params.market,
+ params.market,
params.position.collateral_token,
params.position.is_long,
size_delta_in_tokens
diff --git a/src/reader/reader.cairo b/src/reader/reader.cairo
index ebcb32b3..4d95f1f1 100644
--- a/src/reader/reader.cairo
+++ b/src/reader/reader.cairo
@@ -690,8 +690,7 @@ mod Reader {
let virtual_inventory = self.get_virtual_inventory(data_store, market);
let is_disabled = data_store
- .get_bool(keys::is_market_disabled_key(market.market_token))
- .expect('get_bool failed');
+ .get_bool(keys::is_market_disabled_key(market.market_token));
MarketInfo {
market,
borrowing_factor_per_second_for_longs,
diff --git a/src/reader/reader_pricing_utils.cairo b/src/reader/reader_pricing_utils.cairo
index 25124097..b78989cb 100644
--- a/src/reader/reader_pricing_utils.cairo
+++ b/src/reader/reader_pricing_utils.cairo
@@ -189,7 +189,7 @@ fn get_execution_price(
let size_delta_usd_abs = if size_delta_usd > Zeroable::zero() {
size_delta_usd
} else {
- -size_delta_usd
+ i128_neg(size_delta_usd)
};
params.order.size_delta_usd = calc::to_unsigned(size_delta_usd_abs);
params.order.is_long = is_long;
diff --git a/src/swap/swap_utils.cairo b/src/swap/swap_utils.cairo
index 4af3c035..54fea1d7 100644
--- a/src/swap/swap_utils.cairo
+++ b/src/swap/swap_utils.cairo
@@ -130,8 +130,7 @@ fn swap(params: @SwapParams) -> (ContractAddress, u128) {
}
let market: Market = *params.swap_path_markets[i];
let flag_exists = (*params.data_store)
- .get_bool(keys::swap_path_market_flag_key(market.market_token))
- .expect('get_bool failed');
+ .get_bool(keys::swap_path_market_flag_key(market.market_token));
if (flag_exists) {
SwapError::DUPLICATED_MARKET_IN_SWAP_PATH(market.market_token);
}
diff --git a/src/utils/global_reentrancy_guard.cairo b/src/utils/global_reentrancy_guard.cairo
index 5a5ca086..ad674418 100644
--- a/src/utils/global_reentrancy_guard.cairo
+++ b/src/utils/global_reentrancy_guard.cairo
@@ -14,10 +14,7 @@ const REENTRANCY_GUARD_STATUS: felt252 = 'REENTRANCY_GUARD_STATUS';
/// Modifier to avoid reentrancy.
fn non_reentrant_before(data_store: IDataStoreDispatcher) {
// Read key from Data Store.
- let status = match data_store.get_bool(REENTRANCY_GUARD_STATUS) {
- Option::Some(v) => v,
- Option::None => false,
- };
+ let status = data_store.get_bool(REENTRANCY_GUARD_STATUS);
// Check if reentrancy is happening.
assert(!status, ReentrancyGuardError::REENTRANT_CALL);
diff --git a/src/utils/precision.cairo b/src/utils/precision.cairo
index da19ba88..2a2e6df4 100644
--- a/src/utils/precision.cairo
+++ b/src/utils/precision.cairo
@@ -6,7 +6,7 @@ use alexandria_math::pow;
use integer::{
u128_to_felt252, u256_wide_mul, u512_safe_div_rem_by_u256, BoundedU256, u256_try_as_non_zero
};
-use satoru::utils::i128::i128;
+use satoru::utils::i128::{i128, i128_neg};
use core::traits::TryInto;
use core::option::Option;
use satoru::utils::calc::{roundup_division, roundup_magnitude_division};
@@ -82,7 +82,7 @@ fn mul_div_ival(value: i128, numerator: u128, denominator: u128) -> i128 {
/// * `divisor` - The denominator that divides value.
fn mul_div_inum(value: u128, numerator: i128, denominator: u128) -> i128 {
let numerator_abs = if numerator < Zeroable::zero() {
- -numerator
+ i128_neg(numerator)
} else {
numerator
};
@@ -94,7 +94,7 @@ fn mul_div_inum(value: u128, numerator: i128, denominator: u128) -> i128 {
if numerator > Zeroable::zero() {
return i128_result;
} else {
- return -i128_result;
+ return i128_neg(i128_result);
}
}
@@ -107,7 +107,7 @@ fn mul_div_inum_roundup(
value: u128, numerator: i128, denominator: u128, roundup_magnitude: bool
) -> i128 {
let numerator_abs = if numerator < Zeroable::zero() {
- -numerator
+ i128_neg(numerator)
} else {
numerator
};
@@ -119,7 +119,7 @@ fn mul_div_inum_roundup(
if numerator > Zeroable::zero() {
return i128_result;
} else {
- return -i128_result;
+ return i128_neg(i128_result);
}
}
@@ -205,7 +205,7 @@ fn to_factor(value: u128, divisor: u128) -> u128 {
/// The factor between value and divisor.
fn to_factor_ival(value: i128, divisor: u128) -> i128 {
let value_abs = if value < Zeroable::zero() {
- -value
+ i128_neg(value)
} else {
value
};
@@ -217,7 +217,7 @@ fn to_factor_ival(value: i128, divisor: u128) -> i128 {
if value > Zeroable::zero() {
i128_result
} else {
- -i128_result
+ i128_neg(i128_result)
}
}
diff --git a/src/withdrawal/withdrawal_utils.cairo b/src/withdrawal/withdrawal_utils.cairo
index daa278a9..ac3c4c4e 100644
--- a/src/withdrawal/withdrawal_utils.cairo
+++ b/src/withdrawal/withdrawal_utils.cairo
@@ -134,11 +134,7 @@ fn create_withdrawal(
account_utils::validate_receiver(params.receiver);
let market_token_amount = withdrawal_vault.record_transfer_in(params.market);
-
- if market_token_amount.is_zero() {
- WithdrawalError::EMPTY_WITHDRAWAL_AMOUNT;
- }
-
+ assert(market_token_amount.is_non_zero(), WithdrawalError::EMPTY_WITHDRAWAL_AMOUNT);
params.execution_fee = fee_token_amount.into();
market_utils::validate_enabled_market_check(data_store, params.market);
@@ -187,7 +183,9 @@ fn create_withdrawal(
gas_utils::validate_execution_fee(data_store, estimated_gas_limit, params.execution_fee);
let key = nonce_utils::get_next_key(data_store);
-
+ // assign generated key to withdrawal
+ withdrawal.key = key;
+ // store withdrawal
data_store.set_withdrawal(key, withdrawal);
event_emitter.emit_withdrawal_created(key, withdrawal);
diff --git a/tests/data/test_data_store.cairo b/tests/data/test_data_store.cairo
index e1983e01..ac541379 100644
--- a/tests/data/test_data_store.cairo
+++ b/tests/data/test_data_store.cairo
@@ -67,12 +67,12 @@ fn given_normal_conditions_when_bool_functions_then_expected_results() {
// Safe to unwrap because we know that the key exists and if it doesn't the test should fail.
let value = data_store.get_bool(1);
// Check that the value read is true.
- assert(value.unwrap() == true, 'Invalid value');
+ assert(value == true, 'Invalid value');
// Remove key 1.
data_store.remove_bool(1);
// Check that the key was removed.
- assert(data_store.get_bool(1) == Option::None, 'Key was not deleted');
+ assert(data_store.get_bool(1) == false, 'Key was not deleted');
// *********************************************************************************************
// * TEARDOWN *
diff --git a/tests/exchange/test_withdrawal_handler.cairo b/tests/exchange/test_withdrawal_handler.cairo
index 733193bb..e05d528d 100644
--- a/tests/exchange/test_withdrawal_handler.cairo
+++ b/tests/exchange/test_withdrawal_handler.cairo
@@ -1,8 +1,10 @@
use starknet::{
ContractAddress, get_caller_address, Felt252TryIntoContractAddress, contract_address_const
};
-use snforge_std::{declare, start_prank, stop_prank, ContractClassTrait};
-
+use snforge_std::{
+ declare, start_prank, stop_prank, start_mock_call, stop_mock_call, ContractClassTrait
+};
+use satoru::utils::span32::{Span32, Span32Trait};
use satoru::event::event_emitter::{IEventEmitterDispatcher, IEventEmitterDispatcherTrait};
use satoru::exchange::withdrawal_handler::{
IWithdrawalHandlerDispatcher, IWithdrawalHandlerDispatcherTrait
@@ -22,35 +24,80 @@ use satoru::withdrawal::withdrawal::Withdrawal;
use satoru::market::market::Market;
use traits::Default;
-// TODO: Add more tests after withdraw_utils implementation done.
+// This tests check withdrawal creation under normal condition
+// It calls withdrawal_handler.create_withdrawal
+// The test expects the call to succeed without error
#[test]
fn given_normal_conditions_when_create_withdrawal_then_works() {
- let (caller_address, data_store, event_emitter, withdrawal_handler) = setup();
+ let (caller_address, data_store, event_emitter, withdrawal_handler, withdrawal_vault_address) =
+ setup();
start_prank(withdrawal_handler.contract_address, caller_address);
let account = contract_address_const::<'account'>();
+ let market_token = contract_address_const::<'market_token'>();
+ let params = create_withrawal_params(market_token);
let address_zero = contract_address_const::<0>();
- let key = contract_address_const::<123456789>();
let mut market = Market {
- market_token: key,
+ market_token: market_token,
index_token: address_zero,
long_token: address_zero,
short_token: address_zero,
};
- data_store.set_market(key, 0, market);
+ data_store.set_market(market_token, 0, market);
+ start_mock_call(withdrawal_vault_address, 'record_transfer_in', 1);
+ let key = withdrawal_handler.create_withdrawal(account, params);
- let params = create_withrawal_params(key);
-//withdrawal_handler.create_withdrawal(account, params); TODO fix create_withdrawal
+ //check withdrawal datas created
+ let withdrawal = data_store.get_withdrawal(key);
+ assert(withdrawal.key == key, 'Invalid withdrawal key');
+ assert(withdrawal.account == account, 'Invalid withdrawal account');
}
+// This tests check withdrawal creation when market_token_amount is 0
+// It calls withdrawal_handler.create_withdrawal
+// The test expects the call to panic with error empty withdrawal amount
+#[test]
+#[should_panic(expected: ('empty withdrawal amount',))]
+fn given_market_token_amount_equal_zero_when_create_withdrawal_then_fails() {
+ let (caller_address, data_store, event_emitter, withdrawal_handler, withdrawal_vault_address) =
+ setup();
+ start_prank(withdrawal_handler.contract_address, caller_address);
+
+ let account = contract_address_const::<'account'>();
+ let market_token = contract_address_const::<'market_token'>();
+ let params = create_withrawal_params(market_token);
+
+ withdrawal_handler.create_withdrawal(account, params);
+}
+
+// This tests check withdrawal creation when fee token amount is lower than execution fee
+// It calls withdrawal_handler.create_withdrawal
+// The test expects the call to panic with the error 'insufficient fee token amout'
+#[test]
+#[should_panic(expected: ('insufficient fee token amout', 0, 1))]
+fn given_fee_token_lower_than_execution_fee_conditions_when_create_withdrawal_then_fails() {
+ let (caller_address, data_store, event_emitter, withdrawal_handler, _) = setup();
+ start_prank(withdrawal_handler.contract_address, caller_address);
+
+ let account = contract_address_const::<'account'>();
+ let market_token = contract_address_const::<'market_token'>();
+ let mut params = create_withrawal_params(market_token);
+ params.execution_fee = 1;
+
+ withdrawal_handler.create_withdrawal(account, params);
+}
+
+// This tests check withdrawal creation when caller address doesn't meet controller role
+// It calls withdrawal_handler.create_withdrawal
+// The test expects the call to panic with the error 'unauthorized_access'.
#[test]
#[should_panic(expected: ('unauthorized_access',))]
fn given_caller_not_controller_when_create_withdrawal_then_fails() {
// Should revert, call from anyone else then controller.
- let (caller_address, data_store, event_emitter, withdrawal_handler) = setup();
+ let (caller_address, data_store, event_emitter, withdrawal_handler, _) = setup();
let caller: ContractAddress = 0x847.try_into().unwrap();
start_prank(withdrawal_handler.contract_address, caller);
@@ -61,9 +108,13 @@ fn given_caller_not_controller_when_create_withdrawal_then_fails() {
withdrawal_handler.create_withdrawal(caller, params);
}
+// This test checks withdrawal cancellation under normal condition
+// It calls withdrawal_handler.cancel_withdrawal
+// The test expects the call to succeed without error
#[test]
fn given_normal_conditions_when_cancel_withdrawal_then_works() {
- let (caller_address, data_store, event_emitter, withdrawal_handler) = setup();
+ let (caller_address, data_store, event_emitter, withdrawal_handler, withdrawal_vault_address) =
+ setup();
start_prank(withdrawal_handler.contract_address, caller_address);
let account = contract_address_const::<'account'>();
@@ -79,16 +130,44 @@ fn given_normal_conditions_when_cancel_withdrawal_then_works() {
data_store.set_market(key, 0, market);
let params = create_withrawal_params(key);
-//let withdrawal_key = withdrawal_handler.create_withdrawal(account, params); TODO fix create_withdrawal
+ start_mock_call(withdrawal_vault_address, 'record_transfer_in', 1);
-// Key cleaning should be done in withdrawal_utils. We only check call here.
-//withdrawal_handler.cancel_withdrawal(withdrawal_key);
+ let withdrawal_key = withdrawal_handler.create_withdrawal(account, params);
+
+ start_mock_call(
+ withdrawal_vault_address,
+ 'transfer_out',
+ array![contract_address_const::<'market_token'>().into(), account.into(), '1']
+ );
+ // Key cleaning should be done in withdrawal_utils. We only check call here.
+ withdrawal_handler.cancel_withdrawal(withdrawal_key);
+
+ //check withdrawal correctly removed
+ let address_zero = contract_address_const::<0>();
+ let withdrawal = data_store.get_withdrawal(withdrawal_key);
+
+ assert(withdrawal.key == 0, 'Invalid key');
+ assert(withdrawal.account == address_zero, 'Invalid account');
+ assert(withdrawal.receiver == address_zero, 'Invalid receiver');
+ assert(withdrawal.callback_contract == address_zero, 'Invalid callback after');
+ assert(withdrawal.ui_fee_receiver == address_zero, 'Invalid ui_fee_receiver');
+ assert(withdrawal.long_token_swap_path.len() == 0, 'Invalid long_swap_path');
+ assert(withdrawal.short_token_swap_path.len() == 0, 'Invalid short_swap_path');
+ assert(withdrawal.market_token_amount == 0, 'Invalid market_token_amount');
+ assert(withdrawal.min_long_token_amount == 0, 'Invalid long_token_amount');
+ assert(withdrawal.min_short_token_amount == 0, 'Invalid short_token_amount');
+ assert(withdrawal.updated_at_block == 0, 'Invalid block');
+ assert(withdrawal.execution_fee == 0, 'Invalid execution_fee');
+ assert(withdrawal.callback_gas_limit == 0, 'Invalid callback_gas_limit');
}
+// This tests check withdrawal cancellation when key doesn't exist in store
+// It calls withdrawal_handler.cancel_withdrawal
+// The test expects the call to panic with the error 'get_withdrawal failed'.
#[test]
#[should_panic(expected: ('empty withdrawal',))]
fn given_unexisting_key_when_cancel_withdrawal_then_fails() {
- let (caller_address, data_store, event_emitter, withdrawal_handler) = setup();
+ let (caller_address, data_store, event_emitter, withdrawal_handler, _) = setup();
start_prank(withdrawal_handler.contract_address, caller_address);
let withdrawal_key = 'SAMPLE_WITHDRAW';
@@ -97,9 +176,114 @@ fn given_unexisting_key_when_cancel_withdrawal_then_fails() {
withdrawal_handler.cancel_withdrawal(withdrawal_key);
}
+// This tests check withdrawal cancellation when account address is 0
+// It calls withdrawal_handler.cancel_withdrawal
+// The test expects the call to panic with the error 'empty withdrawal'.
+#[test]
+#[should_panic(expected: ('empty withdrawal',))]
+fn given_account_address_zero_when_cancel_withdrawal_then_fails() {
+ let mut withdrawal = Withdrawal {
+ key: 0,
+ account: contract_address_const::<0>(),
+ receiver: contract_address_const::<0>(),
+ callback_contract: contract_address_const::<0>(),
+ ui_fee_receiver: contract_address_const::<0>(),
+ market: contract_address_const::<0>(),
+ long_token_swap_path: Default::default(),
+ short_token_swap_path: Default::default(),
+ market_token_amount: 0,
+ min_long_token_amount: 0,
+ min_short_token_amount: 0,
+ updated_at_block: 0,
+ execution_fee: 0,
+ callback_gas_limit: 0,
+ };
+
+ let (caller_address, data_store, event_emitter, withdrawal_handler, withdrawal_vault_address) =
+ setup();
+ start_prank(withdrawal_handler.contract_address, caller_address);
+
+ let account = contract_address_const::<'account'>();
+ let key = contract_address_const::<'market'>();
+ let mut params = create_withrawal_params(key);
+
+ let market = Market {
+ market_token: key,
+ index_token: contract_address_const::<'index_token'>(),
+ long_token: contract_address_const::<'long_token'>(),
+ short_token: contract_address_const::<'short_token'>(),
+ };
+
+ data_store.set_market(key, 0, market);
+
+ start_mock_call(withdrawal_vault_address, 'record_transfer_in', 1);
+
+ let withdrawal_key = withdrawal_handler.create_withdrawal(account, params);
+
+ start_mock_call(data_store.contract_address, 'get_withdrawal', withdrawal);
+
+ // Key cleaning should be done in withdrawal_utils. We only check call here.
+ withdrawal_handler.cancel_withdrawal(withdrawal_key);
+}
+
+
+// This tests check withdrawal cancellation when market token amount is 0
+// It calls withdrawal_handler.cancel_withdrawal
+// The test expects the call to panic with the error 'empty withdrawal'.
+#[test]
+#[should_panic(expected: ('empty withdrawal amount',))]
+fn given_market_token_equals_zero_when_cancel_withdrawal_then_fails() {
+ let mut withdrawal = Withdrawal {
+ key: 0,
+ account: contract_address_const::<'account'>(),
+ receiver: contract_address_const::<0>(),
+ callback_contract: contract_address_const::<0>(),
+ ui_fee_receiver: contract_address_const::<0>(),
+ market: contract_address_const::<0>(),
+ long_token_swap_path: Default::default(),
+ short_token_swap_path: Default::default(),
+ market_token_amount: 0,
+ min_long_token_amount: 0,
+ min_short_token_amount: 0,
+ updated_at_block: 0,
+ execution_fee: 0,
+ callback_gas_limit: 0,
+ };
+
+ let (caller_address, data_store, event_emitter, withdrawal_handler, withdrawal_vault_address) =
+ setup();
+ start_prank(withdrawal_handler.contract_address, caller_address);
+
+ let account = contract_address_const::<'account'>();
+ let key = contract_address_const::<'market'>();
+ let mut params = create_withrawal_params(key);
+
+ let market = Market {
+ market_token: key,
+ index_token: contract_address_const::<'index_token'>(),
+ long_token: contract_address_const::<'long_token'>(),
+ short_token: contract_address_const::<'short_token'>(),
+ };
+
+ data_store.set_market(key, 0, market);
+
+ start_mock_call(withdrawal_vault_address, 'record_transfer_in', 1);
+
+ let withdrawal_key = withdrawal_handler.create_withdrawal(account, params);
+
+ stop_mock_call(withdrawal_vault_address, 'record_transfer_in');
+ start_mock_call(data_store.contract_address, 'get_withdrawal', withdrawal);
+
+ // Key cleaning should be done in withdrawal_utils. We only check call here.
+ withdrawal_handler.cancel_withdrawal(withdrawal_key);
+}
+
+// This tests check withdrawal execution when caller address doesn't meet controller role
+// It calls withdrawal_handler.execute_withdrawal
+// The test expects the call to panic with the error 'unauthorized_access'.
#[test]
#[should_panic(expected: ('unauthorized_access',))]
-fn given_caller_not_controller_when_execute_withdrawal_then_fails() {
+fn given_caller_not_keeper_when_execute_withdrawal_then_fails() {
let oracle_params = SetPricesParams {
signer_info: Default::default(),
tokens: Default::default(),
@@ -115,7 +299,7 @@ fn given_caller_not_controller_when_execute_withdrawal_then_fails() {
price_feed_tokens: Default::default(),
};
- let (caller_address, data_store, event_emitter, withdrawal_handler) = setup();
+ let (caller_address, data_store, event_emitter, withdrawal_handler, _) = setup();
let withdrawal_key = 'SAMPLE_WITHDRAW';
@@ -141,7 +325,7 @@ fn given_caller_not_controller_when_execute_withdrawal_then_fails() {
// price_feed_tokens: Default::default(),
// };
-// let (caller_address, data_store, event_emitter, withdrawal_handler) = setup();
+// let (caller_address, data_store, event_emitter, withdrawal_handler,_) = setup();
// let order_keeper = contract_address_const::<0x2233>();
// start_prank(withdrawal_handler.contract_address, order_keeper);
@@ -150,10 +334,13 @@ fn given_caller_not_controller_when_execute_withdrawal_then_fails() {
// withdrawal_handler.execute_withdrawal(withdrawal_key, oracle_params);
// }
+// This tests check withdrawal simulation when when caller address doesn't meet controller role
+// It calls withdrawal_handler.simulate_execute_withdrawal
+// The test expects the call to panic with the error 'unauthorized_access'.
#[test]
#[should_panic(expected: ('unauthorized_access',))]
fn given_caller_not_controller_when_simulate_execute_withdrawal_then_fails() {
- let (caller_address, data_store, event_emitter, withdrawal_handler) = setup();
+ let (caller_address, data_store, event_emitter, withdrawal_handler, _) = setup();
let caller: ContractAddress = contract_address_const::<0x847>();
start_prank(withdrawal_handler.contract_address, caller);
@@ -166,11 +353,13 @@ fn given_caller_not_controller_when_simulate_execute_withdrawal_then_fails() {
withdrawal_handler.simulate_execute_withdrawal(withdrawal_key, oracle_params);
}
-// Panics due to the absence of a mocked withdrawal, resulting in 'withdrawal not found'.
+// This tests check withdrawal simulation when key is unknown
+// It calls withdrawal_handler.simulate_execute_withdrawal
+// The test expects the call to panic with the error 'invalid withdrawal key','SAMPLE_WITHDRAW'.
#[test]
#[should_panic(expected: ('withdrawal not found',))]
fn given_invalid_withdrawal_key_when_simulate_execute_withdrawal_then_fails() {
- let (caller_address, data_store, event_emitter, withdrawal_handler) = setup();
+ let (caller_address, data_store, event_emitter, withdrawal_handler, _) = setup();
let oracle_params = SimulatePricesParams {
primary_tokens: Default::default(), primary_prices: Default::default(),
};
@@ -302,7 +491,11 @@ fn deploy_event_emitter() -> ContractAddress {
}
fn setup() -> (
- ContractAddress, IDataStoreDispatcher, IEventEmitterDispatcher, IWithdrawalHandlerDispatcher
+ ContractAddress,
+ IDataStoreDispatcher,
+ IEventEmitterDispatcher,
+ IWithdrawalHandlerDispatcher,
+ ContractAddress
) {
let caller_address: ContractAddress = contract_address_const::<'caller'>();
let order_keeper: ContractAddress = 0x2233.try_into().unwrap();
@@ -330,13 +523,14 @@ fn setup() -> (
contract_address: withdrawal_handler_address
};
start_prank(role_store_address, caller_address);
- role_store.grant_role(caller_address, role::MARKET_KEEPER);
role_store.grant_role(caller_address, role::CONTROLLER);
role_store.grant_role(order_keeper, role::ORDER_KEEPER);
+ role_store.grant_role(caller_address, role::MARKET_KEEPER);
role_store.grant_role(withdrawal_handler_address, role::CONTROLLER);
start_prank(data_store_address, caller_address);
data_store.set_address(keys::fee_token(), fee_token_address);
+ //let market_token = IMarketTokenDispatcher { contract_address: market_token_address };
- (caller_address, data_store, event_emitter, withdrawal_handler)
+ (caller_address, data_store, event_emitter, withdrawal_handler, withdrawal_vault_address)
}
diff --git a/tests/position/test_position_utils.cairo b/tests/position/test_position_utils.cairo
index c259620f..28625c32 100644
--- a/tests/position/test_position_utils.cairo
+++ b/tests/position/test_position_utils.cairo
@@ -5,6 +5,7 @@
// Core lib imports.
use result::ResultTrait;
+
use traits::{TryInto, Into};
use starknet::{
ContractAddress, get_caller_address, Felt252TryIntoContractAddress, contract_address_const,
@@ -29,7 +30,7 @@ use satoru::price::price::{Price, PriceTrait};
use satoru::position::{position::Position, position_utils::UpdatePositionParams, position_utils};
use satoru::tests_lib::{setup, setup_event_emitter, teardown};
use satoru::mock::referral_storage::{IReferralStorageDispatcher, IReferralStorageDispatcherTrait};
-use satoru::pricing::{position_pricing_utils::PositionFees};
+use satoru::pricing::position_pricing_utils::{PositionFees, PositionReferralFees};
use satoru::order::{
order::{Order, SecondaryOrderType, OrderType, DecreasePositionSwapType},
order_vault::{IOrderVaultDispatcher, IOrderVaultDispatcherTrait}
@@ -37,15 +38,15 @@ use satoru::order::{
use satoru::oracle::oracle::{IOracleDispatcher, IOracleDispatcherTrait};
use satoru::swap::swap_handler::{ISwapHandlerDispatcher, ISwapHandlerDispatcherTrait};
use satoru::order::base_order_utils::ExecuteOrderParamsContracts;
-
+use satoru::utils::i128::{i128};
#[test]
fn given_normal_conditions_when_get_position_key_then_works() {
//
// Setup
//
- let account: ContractAddress = 'account'.try_into().unwrap();
- let market: ContractAddress = 'market'.try_into().unwrap();
- let token: ContractAddress = 'token'.try_into().unwrap();
+ let account: ContractAddress = contract_address_const::<'account'>();
+ let market: ContractAddress = contract_address_const::<'market'>();
+ let token: ContractAddress = contract_address_const::<'token'>();
let mut data = array![account.into(), market.into(), token.into(), false.into()];
let mut data2 = array![account.into(), market.into(), token.into(), true.into()];
let key_1 = poseidon_hash_span(data.span());
@@ -99,7 +100,7 @@ fn given_invalid_position_size_when_validate_position_then_fails() {
let (caller_address, role_store, data_store) = setup();
let referral_storage = IReferralStorageDispatcher {
- contract_address: '12345'.try_into().unwrap()
+ contract_address: contract_address_const::<'12345'>()
};
let position: Position = Default::default();
@@ -124,7 +125,7 @@ fn given_empty_market_when_validate_position_then_fails() {
let (caller_address, role_store, data_store) = setup();
let referral_storage = IReferralStorageDispatcher {
- contract_address: '12345'.try_into().unwrap()
+ contract_address: contract_address_const::<'12345'>()
};
let mut position: Position = Default::default();
@@ -156,9 +157,9 @@ fn given_minimum_position_size_when_validate_position_then_fails() {
let (caller_address, role_store, data_store) = setup();
let referral_storage = IReferralStorageDispatcher {
- contract_address: '12345'.try_into().unwrap()
+ contract_address: contract_address_const::<'12345'>()
};
- let token: ContractAddress = 'token'.try_into().unwrap();
+ let token: ContractAddress = contract_address_const::<'token'>();
let mut position: Position = Default::default();
let mut market: Market = Default::default();
@@ -169,7 +170,7 @@ fn given_minimum_position_size_when_validate_position_then_fails() {
// Set valid market colleteral tokens (positon.collateral_token == market.long_token || token == market.short_token;)
position.collateral_token = token;
market.long_token = token;
- market.market_token = 'market_token'.try_into().unwrap();
+ market.market_token = contract_address_const::<'market_token'>();
let price = Price { min: 0, max: 0 };
let prices: MarketPrices = MarketPrices {
@@ -207,10 +208,10 @@ fn given_normal_conditions_when_increment_claimable_funding_amount_then_works()
let (caller_address, role_store, data_store) = setup();
let (event_emitter_address, event_emitter) = setup_event_emitter();
- let market_token: ContractAddress = 'market_token'.try_into().unwrap();
- let long_token: ContractAddress = 'long_token'.try_into().unwrap();
- let short_token: ContractAddress = 'short_token'.try_into().unwrap();
- let account: ContractAddress = 'account'.try_into().unwrap();
+ let market_token: ContractAddress = contract_address_const::<'market_token'>();
+ let long_token: ContractAddress = contract_address_const::<'long_token'>();
+ let short_token: ContractAddress = contract_address_const::<'short_token'>();
+ let account: ContractAddress = contract_address_const::<'account'>();
let long_token_amount: u128 = 10000;
let short_token_amount: u128 = 20000;
@@ -248,7 +249,7 @@ fn given_normal_conditions_when_increment_claimable_funding_amount_then_works()
market_token, short_token, account
);
- // Check funding amounts increased for long and short tokens
+ // Check funding amounts increased for long and short tokens
let retrieved_claimable_long = data_store.get_u128(claimable_fund_long_key);
let retrieved_claimable_short = data_store.get_u128(claimable_fund_short_key);
assert(retrieved_claimable_long == long_token_amount, 'Invalid claimable for long');
@@ -267,34 +268,426 @@ fn given_normal_conditions_when_increment_claimable_funding_amount_then_works()
teardown(data_store.contract_address);
}
-// TODO
-// Missing libraries
-//fn test_is_position_liquiditable() {
-//
-
-// TODO
-// Missing libraries
-//fn test_will_position_collateral_be_sufficient() {
-//
-
-// TODO
-// Missing libraries
-//fn test_update_funding_and_borrowing_state() {
-//
-
-// TODO
-// Missing libraries
-//fn test_update_total_borrowing() {
-//
-
-// TODO
-// Missing libraries
-//fn test_update_open_interest() {
-//
-
-// TODO
-// Missing libraries
-//fn test_handle_referral() {
-//
+
+
+/// Utility function to deploy a `ReferralStorage` contract and return its dispatcher.
+fn deploy_referral_storage(event_emitter_address: ContractAddress) -> ContractAddress {
+ let contract = declare('ReferralStorage');
+ let constructor_calldata = array![event_emitter_address.into()];
+ contract.deploy(@constructor_calldata).unwrap()
+}
+
+
+#[test]
+fn given_negative_remaining_collateral_usd_when_checking_liquidatability_then_invalid_position() {
+ //
+ // Setup
+ //
+ let (caller_address, role_store, data_store) = setup();
+ let (event_emitter_address, event_emitter) = setup_event_emitter();
+
+ let referral_storage_address: ContractAddress = deploy_referral_storage(event_emitter_address);
+
+ let referral_storage = IReferralStorageDispatcher {
+ contract_address: referral_storage_address
+ };
+
+ let market_token: ContractAddress = contract_address_const::<'market_token'>();
+ let long_token: ContractAddress = contract_address_const::<'long_token'>();
+ let short_token: ContractAddress = contract_address_const::<'short_token'>();
+
+ //Create a long position
+ let mut position: Position = Default::default();
+ position.size_in_usd = 10000;
+ position.collateral_amount = 10;
+ position.borrowing_factor = 2;
+ position.size_in_tokens = 50;
+ position.is_long = true;
+ position.collateral_token = long_token;
+ position.market = market_token;
+
+ // Fill required data store keys.
+
+ // setting long interest greater than te position size in USD...
+ let open_interest_key = keys::open_interest_key(market_token, long_token, true);
+ data_store.set_u128(open_interest_key, 15000);
+
+ // setting cumulative borrowing factor greater than the borrowing factor...
+ let cumulative_borrowing_factor_key = keys::cumulative_borrowing_factor_key(market_token, true);
+ data_store.set_u128(cumulative_borrowing_factor_key, 1000);
+
+ let market = Market { market_token, index_token: long_token, long_token, short_token, };
+
+ let long_token_price = Price { min: 100, max: 110 };
+ let index_token_price = Price { min: 100, max: 110 };
+ let short_token_price = Price { min: 100, max: 110 };
+
+ let prices: MarketPrices = MarketPrices {
+ index_token_price: index_token_price,
+ long_token_price: long_token_price,
+ short_token_price: short_token_price
+ };
+
+ // Test
+
+ let (is_liquiditable, reason) = position_utils::is_position_liquiditable(
+ data_store, referral_storage, position, market, prices, false
+ );
+
+ assert(is_liquiditable, 'Invalid position liquidation');
+ assert(reason == '0<', 'Invalid liquidation reason');
+}
+
+
+#[test]
+fn given_below_minimum_collateral_when_checking_liquidatability_then_invalid_position() {
+ //
+ // Setup
+ //
+ let (caller_address, role_store, data_store) = setup();
+ let (event_emitter_address, event_emitter) = setup_event_emitter();
+
+ let referral_storage_address: ContractAddress = deploy_referral_storage(event_emitter_address);
+
+ let referral_storage = IReferralStorageDispatcher {
+ contract_address: referral_storage_address
+ };
+
+ let market_token: ContractAddress = contract_address_const::<'market_token'>();
+ let long_token: ContractAddress = contract_address_const::<'long_token'>();
+ let short_token: ContractAddress = contract_address_const::<'short_token'>();
+
+ //Create a long position
+ let mut position: Position = Default::default();
+ position.size_in_usd = 10000;
+ position.collateral_amount = 10;
+ position.borrowing_factor = 2;
+ position.size_in_tokens = 50;
+ position.is_long = true;
+ position.collateral_token = long_token;
+ position.market = market_token;
+
+ // Fill required data store keys.
+
+ // setting long interest greater than te position size in USD...
+ let open_interest_key = keys::open_interest_key(market_token, long_token, true);
+ data_store.set_u128(open_interest_key, 15000);
+
+ // setting cumulative borrowing factor greater than the borrowing factor...
+ let cumulative_borrowing_factor_key = keys::cumulative_borrowing_factor_key(market_token, true);
+ data_store.set_u128(cumulative_borrowing_factor_key, 1000);
+
+ let market = Market { market_token, index_token: long_token, long_token, short_token, };
+
+ let long_token_price = Price { min: 100, max: 110 };
+ let index_token_price = Price { min: 100, max: 110 };
+ let short_token_price = Price { min: 100, max: 110 };
+
+ let prices: MarketPrices = MarketPrices {
+ index_token_price: index_token_price,
+ long_token_price: long_token_price,
+ short_token_price: short_token_price
+ };
+
+ // Test
+
+ let (is_liquiditable, reason) = position_utils::is_position_liquiditable(
+ data_store, referral_storage, position, market, prices, true
+ );
+
+ assert(is_liquiditable, 'Invalid position liquidation');
+ assert(reason == 'min collateral', 'Invalid liquidation reason');
+}
+
+#[test]
+fn given_valid_position_when_checking_liquidatability_then_valid_position() {
+ //
+ // Setup
+ //
+ let (caller_address, role_store, data_store) = setup();
+ let (event_emitter_address, event_emitter) = setup_event_emitter();
+
+ let referral_storage_address: ContractAddress = deploy_referral_storage(event_emitter_address);
+
+ let referral_storage = IReferralStorageDispatcher {
+ contract_address: referral_storage_address
+ };
+
+ let market_token: ContractAddress = contract_address_const::<'market_token'>();
+ let long_token: ContractAddress = contract_address_const::<'long_token'>();
+ let short_token: ContractAddress = contract_address_const::<'short_token'>();
+
+ //Create a long position
+ let mut position: Position = Default::default();
+ position.size_in_usd = 10000;
+ position.collateral_amount = 1000;
+ position.borrowing_factor = 2;
+ position.size_in_tokens = 50;
+ position.is_long = true;
+ position.collateral_token = long_token;
+ position.market = market_token;
+
+ // Fill required data store keys.
+
+ // setting long interest greater than te position size in USD...
+ let open_interest_key = keys::open_interest_key(market_token, long_token, true);
+ data_store.set_u128(open_interest_key, 15000);
+
+ // setting cumulative borrowing factor greater than the borrowing factor...
+ let cumulative_borrowing_factor_key = keys::cumulative_borrowing_factor_key(market_token, true);
+ data_store.set_u128(cumulative_borrowing_factor_key, 1000);
+
+ let market = Market { market_token, index_token: long_token, long_token, short_token, };
+
+ let long_token_price = Price { min: 100, max: 110 };
+ let index_token_price = Price { min: 100, max: 110 };
+ let short_token_price = Price { min: 100, max: 110 };
+
+ let prices: MarketPrices = MarketPrices {
+ index_token_price: index_token_price,
+ long_token_price: long_token_price,
+ short_token_price: short_token_price
+ };
+
+ // Test
+
+ let (is_liquiditable, reason) = position_utils::is_position_liquiditable(
+ data_store, referral_storage, position, market, prices, true
+ );
+
+ assert(!is_liquiditable, 'Invalid position liquidation');
+ assert(reason == '', 'Invalid liquidation reason');
+}
+
+#[test]
+fn given_below_min_collateral_leverage_when_checking_liquidatability_then_invalid_position() {
+ //
+ // Setup
+ //
+ let (caller_address, role_store, data_store) = setup();
+ let (event_emitter_address, event_emitter) = setup_event_emitter();
+
+ let referral_storage_address: ContractAddress = deploy_referral_storage(event_emitter_address);
+
+ let referral_storage = IReferralStorageDispatcher {
+ contract_address: referral_storage_address
+ };
+
+ let market_token: ContractAddress = contract_address_const::<'market_token'>();
+ let long_token: ContractAddress = contract_address_const::<'long_token'>();
+ let short_token: ContractAddress = contract_address_const::<'short_token'>();
+
+ //Create a long position
+ let mut position: Position = Default::default();
+ position.size_in_usd = 10000;
+ position.collateral_amount = 60;
+ position.borrowing_factor = 2;
+ position.size_in_tokens = 50;
+ position.is_long = true;
+ position.collateral_token = long_token;
+ position.market = market_token;
+
+ // Fill required data store keys.
+
+ // setting long interest greater than te position size in USD...
+ let open_interest_key = keys::open_interest_key(market_token, long_token, true);
+ data_store.set_u128(open_interest_key, 15000);
+
+ // setting cumulative borrowing factor greater than the borrowing factor...
+ let cumulative_borrowing_factor_key = keys::cumulative_borrowing_factor_key(market_token, true);
+ data_store.set_u128(cumulative_borrowing_factor_key, 1000);
+
+ // setting a min collateral factor for the market
+ let min_collateral_factor_key = keys::min_collateral_factor_key(market_token);
+ data_store.set_u128(min_collateral_factor_key, 10_000_000_000_000_000_000);
+
+ let market = Market { market_token, index_token: long_token, long_token, short_token, };
+
+ let long_token_price = Price { min: 100, max: 110 };
+ let index_token_price = Price { min: 100, max: 110 };
+ let short_token_price = Price { min: 100, max: 110 };
+
+ let prices: MarketPrices = MarketPrices {
+ index_token_price: index_token_price,
+ long_token_price: long_token_price,
+ short_token_price: short_token_price
+ };
+
+ // Test
+
+ let (is_liquiditable, reason) = position_utils::is_position_liquiditable(
+ data_store, referral_storage, position, market, prices, false
+ );
+
+ assert(is_liquiditable, 'Invalid position liquidation');
+ assert(reason == 'min collateral for leverage', 'Invalid liquidation reason');
+}
+
+
+#[test]
+fn given_initial_total_borrowing_when_updating_then_correct_total_borrowing() {
+ //
+ // Setup
+ //
+ let (caller_address, role_store, data_store) = setup();
+ let (event_emitter_address, event_emitter) = setup_event_emitter();
+
+ let market_token: ContractAddress = contract_address_const::<'market_token'>();
+ let long_token: ContractAddress = contract_address_const::<'long_token'>();
+ let short_token: ContractAddress = contract_address_const::<'short_token'>();
+
+ // Fill required data store keys.
+ let total_borrowing_key = keys::total_borrowing_key(market_token, false);
+ data_store.set_u128(total_borrowing_key, 1000);
+
+ let mut params: position_utils::UpdatePositionParams = UpdatePositionParams {
+ contracts: ExecuteOrderParamsContracts {
+ data_store,
+ event_emitter,
+ order_vault: IOrderVaultDispatcher { contract_address: Zeroable::zero() },
+ oracle: IOracleDispatcher { contract_address: Zeroable::zero() },
+ swap_handler: ISwapHandlerDispatcher { contract_address: Zeroable::zero() },
+ referral_storage: IReferralStorageDispatcher { contract_address: Zeroable::zero() },
+ },
+ market: Market { market_token, index_token: long_token, long_token, short_token, },
+ order: Default::default(),
+ order_key: 0,
+ position: Default::default(),
+ position_key: 0,
+ secondary_order_type: SecondaryOrderType::None,
+ };
+
+ //Test
+
+ //Update total borrowing
+ let next_position_size_in_usd: u128 = 1000000000000000;
+ let next_position_borrowing_factor: u128 = 20000000;
+
+ position_utils::update_total_borrowing(
+ params, next_position_size_in_usd, next_position_borrowing_factor
+ );
+
+ let total_borrowing_value: u128 = data_store.get_u128(total_borrowing_key);
+ assert(total_borrowing_value == 1200, 'Invalid total borrowing')
+}
+
+#[test]
+fn given_initial_open_interest_when_updating_then_correct_open_interest() {
+ //
+ // Setup
+ //
+ let (caller_address, role_store, data_store) = setup();
+ let (event_emitter_address, event_emitter) = setup_event_emitter();
+
+ let market_token: ContractAddress = contract_address_const::<'market_token'>();
+ let long_token: ContractAddress = contract_address_const::<'long_token'>();
+ let short_token: ContractAddress = contract_address_const::<'short_token'>();
+
+ // Fill required data store keys.
+ let key_open_interest = keys::open_interest_key(
+ market_token, contract_address_const::<0>(), false
+ );
+ data_store.set_u128(key_open_interest, 1000);
+
+ let key_open_interest_in_tokens = keys::open_interest_in_tokens_key(
+ market_token, contract_address_const::<0>(), false
+ );
+ data_store.set_u128(key_open_interest_in_tokens, 2000);
+
+ let mut params: position_utils::UpdatePositionParams = UpdatePositionParams {
+ contracts: ExecuteOrderParamsContracts {
+ data_store,
+ event_emitter,
+ order_vault: IOrderVaultDispatcher { contract_address: Zeroable::zero() },
+ oracle: IOracleDispatcher { contract_address: Zeroable::zero() },
+ swap_handler: ISwapHandlerDispatcher { contract_address: Zeroable::zero() },
+ referral_storage: IReferralStorageDispatcher { contract_address: Zeroable::zero() },
+ },
+ market: Market { market_token, index_token: long_token, long_token, short_token, },
+ order: Default::default(),
+ order_key: 0,
+ position: Default::default(),
+ position_key: 0,
+ secondary_order_type: SecondaryOrderType::None,
+ };
+
+ //Update open interest
+ let size_delta_usd: i128 = 10.try_into().unwrap();
+ let size_delta_in_tokens: i128 = 20.try_into().unwrap();
+
+ //Test
+
+ position_utils::update_open_interest(params, size_delta_usd, size_delta_in_tokens);
+
+ let open_interest = data_store.get_u128(key_open_interest);
+
+ let open_interest_in_tokens = data_store.get_u128(key_open_interest_in_tokens);
+
+ assert(open_interest == 1010, 'Invalid open interest value');
+ assert(open_interest_in_tokens == 2020, 'Invalid open interest value');
+}
+
+#[test]
+fn given_valid_referral_when_handling_then_referral_successfully_processed() {
+ //
+ // Setup
+ //
+ let (caller_address, role_store, data_store) = setup();
+ let (event_emitter_address, event_emitter) = setup_event_emitter();
+
+ let market_token: ContractAddress = contract_address_const::<'market_token'>();
+ let long_token: ContractAddress = contract_address_const::<'long_token'>();
+ let short_token: ContractAddress = contract_address_const::<'short_token'>();
+
+ let mut fees: PositionFees = Default::default();
+ let mut referral: PositionReferralFees = Default::default();
+
+ referral.affiliate = contract_address_const::<'1'>();
+ referral.affiliate_reward_amount = 20;
+ fees.referral = referral;
+
+ // Fill required data store keys.
+ let affiliate_reward_for_account_key = keys::affiliate_reward_for_account_key(
+ market_token, contract_address_const::<0>(), referral.affiliate
+ );
+ data_store.set_u128(affiliate_reward_for_account_key, 10);
+
+ let mut params: position_utils::UpdatePositionParams = UpdatePositionParams {
+ contracts: ExecuteOrderParamsContracts {
+ data_store,
+ event_emitter,
+ order_vault: IOrderVaultDispatcher { contract_address: Zeroable::zero() },
+ oracle: IOracleDispatcher { contract_address: Zeroable::zero() },
+ swap_handler: ISwapHandlerDispatcher { contract_address: Zeroable::zero() },
+ referral_storage: IReferralStorageDispatcher { contract_address: Zeroable::zero() },
+ },
+ market: Market { market_token, index_token: long_token, long_token, short_token, },
+ order: Default::default(),
+ order_key: 0,
+ position: Default::default(),
+ position_key: 0,
+ secondary_order_type: SecondaryOrderType::None,
+ };
+
+ //Attribute position.market to the market instance define above
+
+ params.position.market = params.market.market_token;
+
+ //Test
+
+ position_utils::handle_referral(params, fees);
+ let affiliate_reward_value = data_store.get_u128(affiliate_reward_for_account_key);
+
+ assert(affiliate_reward_value == 30, 'Invalide affiliate reward value')
+}
+//TODO
+// #[test]
+// fn test_will_position_collateral_be_sufficient() {
+//}
+
+//TODO
+// #[test]
+// fn test_update_funding_and_borrowing_state() {
+// }
diff --git a/tests/utils/test_reentrancy_guard.cairo b/tests/utils/test_reentrancy_guard.cairo
index 4b839e51..2a710542 100644
--- a/tests/utils/test_reentrancy_guard.cairo
+++ b/tests/utils/test_reentrancy_guard.cairo
@@ -16,18 +16,18 @@ fn given_normal_conditions_when_non_reentrancy_before_and_after_then_works() {
let initial_value = data_store.get_bool('REENTRANCY_GUARD_STATUS');
// Initial value should be false.
- assert(initial_value.is_none(), 'Initial value wrong');
+ assert(initial_value == false, 'Initial value wrong');
// Sets value to true
non_reentrant_before(data_store);
// Gets value after non_reentrant_before call
- let entrant = data_store.get_bool('REENTRANCY_GUARD_STATUS').unwrap();
+ let entrant = data_store.get_bool('REENTRANCY_GUARD_STATUS');
assert(entrant, 'Entered value wrong');
non_reentrant_after(data_store); // This should set value false.
// Gets final value
- let after: bool = data_store.get_bool('REENTRANCY_GUARD_STATUS').unwrap();
+ let after: bool = data_store.get_bool('REENTRANCY_GUARD_STATUS');
assert(!after, 'Final value wrong');
}
@@ -48,7 +48,7 @@ fn given_reentrant_call_when_reentrancy_before_and_after_then_fails() {
non_reentrant_before(data_store);
// Gets value after non_reentrant_before
- let entraant: bool = data_store.get_bool('REENTRANCY_GUARD_STATUS').unwrap();
+ let entraant: bool = data_store.get_bool('REENTRANCY_GUARD_STATUS');
assert(entraant, 'Entered value wrong');
// This should revert, means reentrant call happened.