Skip to content

Commit

Permalink
WIP: Tests for adding ekubo
Browse files Browse the repository at this point in the history
  • Loading branch information
Chepelau committed Oct 14, 2024
1 parent 8ad71a2 commit 94018a9
Show file tree
Hide file tree
Showing 3 changed files with 351 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/amm_core/oracles/pragma.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ mod Pragma {
set_pragma_checkpoint(PragmaUtils::PRAGMA_USDC_USD_KEY);
set_pragma_checkpoint(PragmaUtils::PRAGMA_WBTC_USD_KEY);
set_pragma_checkpoint(PragmaUtils::PRAGMA_STRK_USD_KEY);
set_pragma_checkpoint(PragmaUtils::PRAGMA_EKUBO_USD_KEY);
}
}

Expand Down
347 changes: 347 additions & 0 deletions tests/forks/add_ekubo.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
use array::ArrayTrait;
use core::traits::TryInto;
use debug::PrintTrait;
use snforge_std::{
BlockId, declare, ContractClassTrait, ContractClass, start_prank, start_warp, stop_prank,
start_mock_call, start_roll, stop_roll
};
use starknet::{ContractAddress, get_block_timestamp, get_block_info, ClassHash};
use cubit::f128::types::fixed::{Fixed, FixedTrait};

use carmine_protocol::amm_core::oracles::pragma::Pragma::PRAGMA_ORACLE_ADDRESS;
use carmine_protocol::amm_core::oracles::pragma::PragmaUtils::{
PragmaPricesResponse, Checkpoint, AggregationMode
};
use carmine_protocol::amm_core::oracles::agg::OracleAgg;

use carmine_protocol::amm_interface::IAMM;
use carmine_protocol::amm_interface::IAMMDispatcher;
use carmine_protocol::amm_interface::IAMMDispatcherTrait;
use carmine_protocol::amm_core::state::State::{get_option_token_address, get_available_options};

use carmine_protocol::erc20_interface::IERC20;
use carmine_protocol::erc20_interface::IERC20Dispatcher;
use carmine_protocol::erc20_interface::IERC20DispatcherTrait;
use carmine_protocol::types::basic::{OptionType, OptionSide};

use carmine_protocol::amm_core::constants::TOKEN_EKUBO_ADDRESS;
use carmine_protocol::amm_core::constants::TOKEN_USDC_ADDRESS;

use carmine_protocol::amm_core::constants::OPTION_CALL;
use carmine_protocol::amm_core::constants::OPTION_PUT;

use carmine_protocol::amm_core::constants::TRADE_SIDE_LONG;
use carmine_protocol::amm_core::constants::TRADE_SIDE_SHORT;

use carmine_protocol::oz::access::interface::IOwnable;
use carmine_protocol::oz::access::interface::IOwnableDispatcher;
use carmine_protocol::oz::access::interface::IOwnableDispatcherTrait;

/// TODO:
/// [x] Add lpools
/// [x] Add options
/// [ ] Add liquidity to both pools
/// [ ] Trade options
/// [ ] Settle options

fn EKUBO_WHALE() -> ContractAddress {
0x02a3ed03046e1042e193651e3da6d3c973e3d45c624442be936a374380a78bb5.try_into().unwrap()
}

fn USDC_WHALE() -> ContractAddress {
0x00000005dd3d2f4429af886cd1a3b08289dbcea99a294197e9eb43b0e0325b4b.try_into().unwrap()
}


#[test]
#[fork("MAINNET")]
fn test_add_ekubo_options() {
let amm_contract_addr: ContractAddress =
0x047472e6755afc57ada9550b6a3ac93129cc4b5f98f51c73e0644d129fd208d9
.try_into()
.unwrap();
let amm = IAMMDispatcher { contract_address: amm_contract_addr };
let owner = IOwnableDispatcher { contract_address: amm_contract_addr }.owner();

// Add ekubo lpools
let (ekubo_call_lpt, ekubo_put_lpt) = add_ekubo_lpools(amm_contract_addr, owner);

let now = get_block_timestamp();
let expiry = now + 7 * 86_400; // + week
let strike_price = 46116860184273879040; // 2.5 * 2**64
deploy_and_add_ekubo_options(
amm_contract_addr, ekubo_call_lpt, ekubo_put_lpt, expiry, strike_price
);
let strike_fixed = FixedTrait::new(strike_price.try_into().unwrap(), false);

// Try to fetch price for ekubo
let base_token: ContractAddress = TOKEN_EKUBO_ADDRESS.try_into().unwrap();
let quote_token: ContractAddress = TOKEN_USDC_ADDRESS.try_into().unwrap();
let price = amm.get_current_price(quote_token, base_token,);

let TEN_K_EKUBO: u256 = 100000000000000000000000;
let TEN_K_USDC: u256 = 10000000000;

let ekubo_token = IERC20Dispatcher { contract_address: base_token };
let usdc_token = IERC20Dispatcher { contract_address: quote_token };

assert(price != FixedTrait::ZERO(), 'Ekubo price is zero');

// Add liquidity to ekubo call pool
start_prank(base_token, EKUBO_WHALE());
ekubo_token.approve(amm_contract_addr, TEN_K_EKUBO);
stop_prank(base_token);

start_prank(amm_contract_addr, EKUBO_WHALE());
amm.deposit_liquidity(base_token, quote_token, base_token, OPTION_CALL, TEN_K_EKUBO);
stop_prank(amm_contract_addr);

// Add liquidity to ekubo put pool
start_prank(quote_token, USDC_WHALE());
usdc_token.approve(amm_contract_addr, TEN_K_EKUBO);
stop_prank(quote_token);

start_prank(amm_contract_addr, USDC_WHALE());
amm.deposit_liquidity(quote_token, quote_token, base_token, OPTION_PUT, TEN_K_USDC);
stop_prank(amm_contract_addr);

assert(amm.get_lpool_balance(ekubo_call_lpt) == TEN_K_EKUBO, 'wrong clpool bal');
assert(amm.get_lpool_balance(ekubo_put_lpt) == TEN_K_USDC, 'wrong plpool bal');

assert(amm.get_unlocked_capital(ekubo_call_lpt) == TEN_K_EKUBO, 'wrong clpool bal');
assert(amm.get_unlocked_capital(ekubo_put_lpt) == TEN_K_USDC, 'wrong plpool bal');

assert(amm.get_unlocked_capital(ekubo_call_lpt) == TEN_K_EKUBO, 'wrong c unlocked');
assert(amm.get_unlocked_capital(ekubo_put_lpt) == TEN_K_USDC, 'wrong p unlocked');

assert(amm.get_pool_locked_capital(ekubo_call_lpt) == 0, 'wrong c locked');
assert(amm.get_pool_locked_capital(ekubo_put_lpt) == 0, 'wrong p locked');

// Do some trades
start_prank(base_token, EKUBO_WHALE());
ekubo_token.approve(amm_contract_addr, TEN_K_EKUBO);
stop_prank(base_token);

start_prank(amm_contract_addr, EKUBO_WHALE());
let long_call_premia = amm
.trade_open(
OPTION_CALL,
strike_fixed,
expiry,
TRADE_SIDE_LONG,
(TEN_K_EKUBO / 100).try_into().unwrap(),
quote_token,
base_token,
FixedTrait::new_unscaled(1_000_000, false),
expiry
);
stop_prank(amm_contract_addr);
'LCALLL'.print();
long_call_premia.print();

start_prank(quote_token, USDC_WHALE());
usdc_token.approve(amm_contract_addr, TEN_K_EKUBO);
stop_prank(quote_token);

start_prank(amm_contract_addr, USDC_WHALE());
let long_put_premia = amm
.trade_open(
OPTION_PUT,
strike_fixed,
expiry,
TRADE_SIDE_LONG,
(TEN_K_USDC / 10).try_into().unwrap(),
quote_token,
base_token,
FixedTrait::new_unscaled(1_000_000, false),
expiry
);
stop_prank(amm_contract_addr);

'LPUUTT'.print();
long_put_premia.print();
}


fn deploy_and_add_ekubo_options(
amm_address: ContractAddress,
call_lpt: ContractAddress,
put_lpt: ContractAddress,
expiry: u64,
strike_price: felt252
) {
let amm = IAMMDispatcher { contract_address: amm_address };
let owner = IOwnableDispatcher { contract_address: amm_address }.owner();

let option_token_contract = declare('OptionToken');

let mut long_call_data = ArrayTrait::new();
long_call_data.append('OptLongCall');
long_call_data.append('OLC');
long_call_data.append(amm_address.into());
long_call_data.append(TOKEN_USDC_ADDRESS);
long_call_data.append(TOKEN_EKUBO_ADDRESS);
long_call_data.append(OPTION_CALL.into());
long_call_data.append(strike_price);
long_call_data.append(expiry.into());
long_call_data.append(TRADE_SIDE_LONG.into());
let long_call_address = option_token_contract.deploy(@long_call_data).unwrap();

// // Short Call
let mut short_call_data = ArrayTrait::new();
short_call_data.append('OptShortCall');
short_call_data.append('OSC');
short_call_data.append(amm_address.into());
short_call_data.append(TOKEN_USDC_ADDRESS);
short_call_data.append(TOKEN_EKUBO_ADDRESS);
short_call_data.append(OPTION_CALL.into());
short_call_data.append(strike_price);
short_call_data.append(expiry.into());
short_call_data.append(TRADE_SIDE_SHORT.into());
let short_call_address = option_token_contract.deploy(@short_call_data).unwrap();

// // Long put
let mut long_put_data = ArrayTrait::new();
long_put_data.append('OptLongPut');
long_put_data.append('OLP');
long_put_data.append(amm_address.into());
long_put_data.append(TOKEN_USDC_ADDRESS);
long_put_data.append(TOKEN_EKUBO_ADDRESS);
long_put_data.append(OPTION_PUT.into());
long_put_data.append(strike_price);
long_put_data.append(expiry.into());
long_put_data.append(TRADE_SIDE_LONG.into());
let long_put_address = option_token_contract.deploy(@long_put_data).unwrap();

// // Short put
let mut short_put_data = ArrayTrait::new();
short_put_data.append('OptShortPut');
short_put_data.append('OSP');
short_put_data.append(amm_address.into());
short_put_data.append(TOKEN_USDC_ADDRESS);
short_put_data.append(TOKEN_EKUBO_ADDRESS);
short_put_data.append(OPTION_PUT.into());
short_put_data.append(strike_price);
short_put_data.append(expiry.into());
short_put_data.append(TRADE_SIDE_SHORT.into());
let short_put_address = option_token_contract.deploy(@short_put_data).unwrap();

let init_vol = FixedTrait::from_unscaled_felt(100);

start_prank(amm_address, owner);

let base_token: ContractAddress = TOKEN_EKUBO_ADDRESS.try_into().unwrap();
let quote_token: ContractAddress = TOKEN_USDC_ADDRESS.try_into().unwrap();

// Calls
amm
.add_option_both_sides(
expiry,
FixedTrait::new(strike_price.try_into().unwrap(), false),
quote_token,
base_token,
OPTION_CALL,
call_lpt,
long_call_address,
short_call_address,
init_vol
);

// Puts
amm
.add_option_both_sides(
expiry,
FixedTrait::new(strike_price.try_into().unwrap(), false),
quote_token,
base_token,
OPTION_PUT,
put_lpt,
long_put_address,
short_put_address,
init_vol
);

stop_prank(amm_address);

assert(amm.get_all_options(call_lpt).len() == 2, 'Wrong amount of calls');
assert(amm.get_all_options(put_lpt).len() == 2, 'Wrong amount of puts');
}


fn add_ekubo_lpools(
amm_contract_addr: ContractAddress, owner: ContractAddress
) -> (ContractAddress, ContractAddress) {
let amm = IAMMDispatcher { contract_address: amm_contract_addr };
// Upgrade amm
start_prank(amm_contract_addr, owner);
let new_amm_hash = declare('AMM');
amm.upgrade(new_amm_hash.class_hash);
stop_prank(amm_contract_addr);

let (ekubo_call_lpt, ekubo_put_lpt) = deploy_ekubo_lptokens(amm_contract_addr);

/////////////////////////////////////////////
/// Adding New lptokens to amm
/////////////////////////////////////////////

let base_token: ContractAddress = TOKEN_EKUBO_ADDRESS.try_into().unwrap();
let quote_token: ContractAddress = TOKEN_USDC_ADDRESS.try_into().unwrap();
let ekubo_max_bal =
115792089237316195423570985008687907853269984665640564039457584007913129639935;

let lptokens_before = amm.get_all_lptoken_addresses();

start_prank(amm_contract_addr, owner);
// Adding Call
amm
.add_lptoken(
quote_token,
base_token,
OPTION_CALL,
ekubo_call_lpt,
FixedTrait::new(184467440737095516160000, false), // 10k
ekubo_max_bal
);

// Adding Put
amm
.add_lptoken(
quote_token,
base_token,
OPTION_PUT,
ekubo_put_lpt,
FixedTrait::new(922337203685477580800000, false), // 50k
ekubo_max_bal
);

stop_prank(amm_contract_addr);

let lptokens_after = amm.get_all_lptoken_addresses();

assert(lptokens_before.len() + 2 == lptokens_after.len(), 'Lptokens lost');
assert(*lptokens_after.at(lptokens_before.len()) == ekubo_call_lpt, 'Call missing');
assert(*lptokens_after.at(lptokens_before.len() + 1) == ekubo_put_lpt, 'Put missing');

(ekubo_call_lpt, ekubo_put_lpt)
}


fn deploy_ekubo_lptokens(amm: ContractAddress) -> (ContractAddress, ContractAddress) {
let lptoken_hash = declare('LPToken');

let sth: felt252 = lptoken_hash.class_hash.into();

let mut call_lpt_data = ArrayTrait::new();
call_lpt_data.append('Ekubo-Call-LPT'); // Name
call_lpt_data.append('EU-C-LPT'); // Symbol
call_lpt_data.append(amm.into()); // Owner
let call_lpt_address = lptoken_hash.deploy(@call_lpt_data).unwrap();

let mut put_lpt_data = ArrayTrait::new();
put_lpt_data.append('Ekubo-Put-LPT'); // Name
put_lpt_data.append('EU-P-LPT'); // Symbol
put_lpt_data.append(amm.into()); // Owner
let put_lpt_address = lptoken_hash.deploy(@put_lpt_data).unwrap();

(call_lpt_address, put_lpt_address)
}
3 changes: 3 additions & 0 deletions tests/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ mod trading {
}

mod test_set_balances;
mod forks {
mod add_ekubo;
}

0 comments on commit 94018a9

Please sign in to comment.