Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
scarb 2.3.1
starknet-foundry 0.10.1

6 changes: 6 additions & 0 deletions Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ test = 'snforge test'
name = "MAINNET"
url = "https://starknet-mainnet.public.blastapi.io/rpc/v0_7"
block_id.tag = "Latest"


[[tool.snforge.fork]]
name = "MAINNET_MISSING_TERMINAL_PRICE"
url = "https://starknet-mainnet.public.blastapi.io/rpc/v0_7"
block_id.number = "1366040"
48 changes: 48 additions & 0 deletions src/amm_core/oracles/pragma.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@ mod Pragma {
fn get_pragma_terminal_price(
quote_token_addr: ContractAddress, base_token_addr: ContractAddress, maturity: Timestamp
) -> Fixed {
match _get_missing_terminal_price(quote_token_addr, base_token_addr, maturity) {
Option::Some(missing_price) => {
// No `if let Option...` in this version
return missing_price;
},
Option::None(()) => {}
}

if base_token_addr.into() == TOKEN_ETH_ADDRESS
&& quote_token_addr.into() == TOKEN_STRK_ADDRESS {
let eth_in_usd = _get_pragma_terminal_price(PragmaUtils::PRAGMA_ETH_USD_KEY, maturity);
Expand All @@ -182,6 +190,46 @@ mod Pragma {
}
}

fn _get_missing_terminal_price(
quote_token_addr: ContractAddress, base_token_addr: ContractAddress, maturity: Timestamp
) -> Option<Fixed> {
if (base_token_addr.into() == TOKEN_EKUBO_ADDRESS)
&& (quote_token_addr.into() == TOKEN_USDC_ADDRESS) {
if (maturity == 1744329599) {
// Block 1305721 -> 2025-04-10T23:59:43+00:00
// Pragma price -> 383116769 -> 3.83
let price = 383116769;
return Option::Some(convert_from_int_to_Fixed(price, 8));
}
if (maturity == 1744934399) {
// Block 1325477 -> 2025-04-17T23:59:56+00:00
// Pragma price -> 364000000 -> 3.64
let price = 364000000;
return Option::Some(convert_from_int_to_Fixed(price, 8));
}
if (maturity == 1745539199) {
// Block 1345160 -> 2025-04-24T23:59:49+00:00
// Pragma price -> 457500000 -> 4.57
let price = 457500000;
return Option::Some(convert_from_int_to_Fixed(price, 8));
}
if (maturity == 1743119999) {
// Block 1266210 -> 2025-03-27T23:59:55+00:00
// Pragma price -> 660550232 -> 6.6
let price = 660550232;
return Option::Some(convert_from_int_to_Fixed(price, 8));
}
if (maturity == 1746143999) {
// Block 1364987 -> 2025-05-01T23:59:44+00:00
// Pragma price -> 437617938 -> 4.37
let price = 437617938;
return Option::Some(convert_from_int_to_Fixed(price, 8));
}
}

Option::None(())
}

// @notice Takes in current or terminal price and returns it after accounting for stablecoin divergence
// @param price: Current or terminal price, Fixed
// @param quote_token_addr: Address of quote token in given ticker
Expand Down
4 changes: 2 additions & 2 deletions tests/forks/add_ekubo.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ fn USDC_WHALE() -> ContractAddress {
0x00000005dd3d2f4429af886cd1a3b08289dbcea99a294197e9eb43b0e0325b4b.try_into().unwrap()
}

#[test]
#[fork("MAINNET", block_number: 803195)]
// #[test]
// #[fork("MAINNET", block_number: 803195)]
fn test_add_ekubo_options() {
let amm_contract_addr: ContractAddress =
0x047472e6755afc57ada9550b6a3ac93129cc4b5f98f51c73e0644d129fd208d9
Expand Down
122 changes: 122 additions & 0 deletions tests/forks/missing_ekubo_terminal_price.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use starknet::ContractAddress;

use debug::PrintTrait;
use carmine_protocol::amm_interface::IAMMDispatcher;
use carmine_protocol::amm_interface::IAMMDispatcherTrait;

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

use snforge_std::{start_prank, stop_prank,};


use carmine_protocol::amm_core::oracles::pragma::Pragma::PragmaUtils::PRAGMA_EKUBO_USD_KEY;
use carmine_protocol::amm_core::oracles::pragma::Pragma::{
PRAGMA_ORACLE_ADDRESS, IOracleABIDispatcher, IOracleABIDispatcherTrait, DataType,
AggregationMode,
};

use carmine_protocol::amm_core::constants::{
TOKEN_USDC_ADDRESS, TOKEN_EKUBO_ADDRESS, TOKEN_STRK_ADDRESS, TOKEN_ETH_ADDRESS
};


#[test]
#[fork("MAINNET_MISSING_TERMINAL_PRICE")]
fn test_missing_ekubo_terminal_price() {
// New AMM Hash: 0x07fb1aa680d9c02e1017d5ed048612630c30d11991d43b3e4e7a22531621cd5c

// Missing maturities: 1744329599, 1744934399, 1745539199, 1743119999
let mat1: u64 = 1744329599;
let mat2: u64 = 1744934399;
let mat3: u64 = 1745539199;
let mat4: u64 = 1743119999;
let _mat5: u64 = 1743724799; // This one works fine

let quote_token: ContractAddress = TOKEN_USDC_ADDRESS.try_into().unwrap();
let base_token: ContractAddress = TOKEN_EKUBO_ADDRESS.try_into().unwrap();
let eth_address: ContractAddress = TOKEN_ETH_ADDRESS.try_into().unwrap();
let strk_address: ContractAddress = TOKEN_STRK_ADDRESS.try_into().unwrap();

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();

// Get some prices before upgrade
let _eprice1 = amm.get_terminal_price(quote_token, eth_address, mat1);
let _eprice2 = amm.get_terminal_price(quote_token, eth_address, mat2);
let _eprice3 = amm.get_terminal_price(quote_token, eth_address, mat3);
let _eprice4 = amm.get_terminal_price(quote_token, eth_address, _mat5);

let _sprice1 = amm.get_terminal_price(quote_token, strk_address, mat1);
let _sprice2 = amm.get_terminal_price(quote_token, strk_address, mat2);
let _sprice3 = amm.get_terminal_price(quote_token, strk_address, mat3);
let _sprice4 = amm.get_terminal_price(quote_token, strk_address, _mat5);

let ekubo_correct_price = amm.get_terminal_price(quote_token, base_token, _mat5);

start_prank(amm_contract_addr, owner);
let new_amm_hash = 0x07fb1aa680d9c02e1017d5ed048612630c30d11991d43b3e4e7a22531621cd5c;
amm.upgrade(new_amm_hash.try_into().unwrap());

let pragma = IOracleABIDispatcher {
contract_address: PRAGMA_ORACLE_ADDRESS.try_into().unwrap()
};

let (pragma1, _) = pragma
.get_last_checkpoint_before(
DataType::SpotEntry(PRAGMA_EKUBO_USD_KEY), mat1, AggregationMode::Median(()),
);
let (pragma2, _) = pragma
.get_last_checkpoint_before(
DataType::SpotEntry(PRAGMA_EKUBO_USD_KEY), mat2, AggregationMode::Median(()),
);
let (pragma3, _) = pragma
.get_last_checkpoint_before(
DataType::SpotEntry(PRAGMA_EKUBO_USD_KEY), mat3, AggregationMode::Median(()),
);
let (pragma4, _) = pragma
.get_last_checkpoint_before(
DataType::SpotEntry(PRAGMA_EKUBO_USD_KEY), mat4, AggregationMode::Median(()),
);
// These three maturities should have unsuitable terminal price
// meaning the price is more then 2 hours old at time of maturity
assert(pragma1.timestamp < mat1 - 2 * 3600, 'Price1 isnt old');
assert(pragma2.timestamp < mat2 - 2 * 3600, 'Price2 isnt old');
assert(pragma3.timestamp < mat3 - 2 * 3600, 'Price3 isnt old');
assert(pragma4.timestamp < mat4 - 2 * 3600, 'Price3 isnt old');

// Now call amm get terminal price - this should return a price
// and not fail since values for these maturities are hardcoded

let price1 = amm.get_terminal_price(quote_token, base_token, mat1);
let price2 = amm.get_terminal_price(quote_token, base_token, mat2);
let price3 = amm.get_terminal_price(quote_token, base_token, mat3);
let price4 = amm.get_terminal_price(quote_token, base_token, mat4);

assert(price1.mag == 70672569880895012595, 'wrong price1');
assert(price2.mag == 67146148428302767882, 'wrong price2');
assert(price3.mag == 84393854137221198643, 'wrong price3');
assert(price4.mag == 121850010775334694205, 'wrong price4');

// Fetch prices for other assets again after upgrade and assert they haven't changed
assert(_eprice1 == amm.get_terminal_price(quote_token, eth_address, mat1), 'price changed1');
assert(_eprice2 == amm.get_terminal_price(quote_token, eth_address, mat2), 'price changed2');
assert(_eprice3 == amm.get_terminal_price(quote_token, eth_address, mat3), 'price changed3');
assert(_eprice4 == amm.get_terminal_price(quote_token, eth_address, _mat5), 'price changed4');

assert(_sprice1 == amm.get_terminal_price(quote_token, strk_address, mat1), 'price changed5');
assert(_sprice2 == amm.get_terminal_price(quote_token, strk_address, mat2), 'price changed6');
assert(_sprice3 == amm.get_terminal_price(quote_token, strk_address, mat3), 'price changed7');
assert(_sprice4 == amm.get_terminal_price(quote_token, strk_address, _mat5), 'price changed8');

assert(
ekubo_correct_price == amm.get_terminal_price(quote_token, base_token, _mat5),
'price changed9'
);
}

1 change: 1 addition & 0 deletions tests/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ mod trading {
mod test_set_balances;
mod forks {
mod add_ekubo;
mod missing_ekubo_terminal_price;
}