Skip to content
Open
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
1 change: 1 addition & 0 deletions src/amm_core/amm.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ mod AMM {
>,
pool_definition_from_lptoken_address: LegacyMap<LPTAddress, Pool>,
latest_oracle_price: LegacyMap::<(ContractAddress, ContractAddress), (Fixed, u64)>,
historical_oracle_price_cache: LegacyMap::<(ContractAddress, ContractAddress, u64), Fixed>,
}

fn emit_event<S, impl IntoImpl: traits::Into<S, Event>, impl DropImpl: traits::Drop<S>>(
Expand Down
45 changes: 37 additions & 8 deletions src/amm_core/oracles/agg.cairo
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
mod OracleAgg {
use starknet::ContractAddress;
use starknet::get_block_info;
use starknet::get_block_timestamp;

use cubit::f128::types::fixed::{Fixed};
use cubit::f128::types::fixed::{Fixed, FixedTrait};

use carmine_protocol::types::basic::{Timestamp};
use carmine_protocol::amm_core::oracles::pragma::Pragma::{
get_pragma_median_price, get_pragma_terminal_price
};

use carmine_protocol::amm_core::oracles::chainlink::Chainlink;

use carmine_protocol::amm_core::state::State::{
read_latest_oracle_price, write_latest_oracle_price
read_latest_oracle_price, write_latest_oracle_price, read_historical_oracle_price_cache,
write_historical_oracle_price_cache,
};

// @notice Returns current spot price for given ticker (quote and base token)
Expand All @@ -31,10 +35,20 @@ mod OracleAgg {
if last_price_block_num == curr_block {
last_price
} else {
let price_pragma = get_pragma_median_price(quote_token_addr, base_token_addr);
write_latest_oracle_price(base_token_addr, quote_token_addr, price_pragma, curr_block);

price_pragma
let price =
match Chainlink::get_chainlink_current_price(quote_token_addr, base_token_addr) {
// If there is a chainlink price, use that one
// Chainlink price fetch will fail only if the addresses are expected to have some price,
// but no prices were fetched
Option::Some(chainlink_price) => chainlink_price,
// If there is no chainlink price, try to use pragma (which will fail if the addresses are unknown)
// Pragma is used only for tokens that do not have chainlink source
Option::None(()) => get_pragma_median_price(quote_token_addr, base_token_addr)
};

write_latest_oracle_price(base_token_addr, quote_token_addr, price, curr_block);

price
}
}

Expand All @@ -45,8 +59,23 @@ mod OracleAgg {
fn get_terminal_price(
quote_token_addr: ContractAddress, base_token_addr: ContractAddress, maturity: Timestamp
) -> Fixed {
let price_pragma = get_pragma_terminal_price(quote_token_addr, base_token_addr, maturity);
price_pragma
let historical_price = read_historical_oracle_price_cache(
base_token_addr, quote_token_addr, maturity
);

if historical_price != FixedTrait::ZERO() {
return historical_price;
} else {
let price_pragma = get_pragma_terminal_price(
quote_token_addr, base_token_addr, maturity
);

write_historical_oracle_price_cache(
base_token_addr, quote_token_addr, maturity, price_pragma
);

price_pragma
}
}
}

Expand Down
117 changes: 117 additions & 0 deletions src/amm_core/oracles/chainlink.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
mod Chainlink {
use starknet::ContractAddress;


use cubit::f128::types::fixed::Fixed;
use cubit::f128::types::fixed::FixedTrait;

use carmine_protocol::amm_core::constants::TOKEN_USDC_ADDRESS;
use carmine_protocol::amm_core::constants::TOKEN_ETH_ADDRESS;
use carmine_protocol::amm_core::constants::TOKEN_WBTC_ADDRESS;
use carmine_protocol::amm_core::constants::TOKEN_STRK_ADDRESS;

use carmine_protocol::amm_core::oracles::oracle_helpers::convert_from_int_to_Fixed;

use super::ChainlinkUtils;

fn get_chainlink_current_price(
quote_token_addr: ContractAddress, base_token_addr: ContractAddress,
) -> Option<Fixed> {
let base_chainlink_proxy_address = ChainlinkUtils::get_chainlink_proxy_address(
base_token_addr
)?;
let quote_chainlink_proxy_address = ChainlinkUtils::get_chainlink_proxy_address(
quote_token_addr
)?;

let base_price = ChainlinkUtils::get_current_price_from_proxy(base_chainlink_proxy_address);
let quote_price = ChainlinkUtils::get_current_price_from_proxy(
quote_chainlink_proxy_address
);

assert(base_price != 0, 'Unable to fetch base price');
assert(quote_price != 0, 'Unable to fetch quote price');

let base_price_fixed = convert_from_int_to_Fixed(
base_price, ChainlinkUtils::CHAINLINK_DECIMALS
);
let quote_price_fixed = convert_from_int_to_Fixed(
quote_price, ChainlinkUtils::CHAINLINK_DECIMALS
);

Option::Some(base_price_fixed / quote_price_fixed)
}
}


mod ChainlinkUtils {
use starknet::ContractAddress;
use starknet::get_block_timestamp;
use core::panic_with_felt252;

use carmine_protocol::amm_core::constants::TOKEN_USDC_ADDRESS;
use carmine_protocol::amm_core::constants::TOKEN_ETH_ADDRESS;
use carmine_protocol::amm_core::constants::TOKEN_WBTC_ADDRESS;
use carmine_protocol::amm_core::constants::TOKEN_STRK_ADDRESS;


const CHAINLINK_DECIMALS: u8 = 8;

#[derive(Copy, Drop, Serde, PartialEq, starknet::Store)]
struct Round {
round_id: felt252,
answer: u128,
block_num: u64,
started_at: u64,
updated_at: u64,
}

#[starknet::interface]
trait IAggregatorProxy<TContractState> {
fn latest_round_data(self: @TContractState) -> Round;
fn round_data(self: @TContractState, round_id: felt252) -> Round;
fn description(self: @TContractState) -> felt252;
fn decimals(self: @TContractState) -> u8;
fn latest_answer(self: @TContractState) -> u128;
}

mod Mainnet {
const CHAINLINK_WBTC_USD_ADDRESS: felt252 =
0x6275040a2913e2fe1a20bead3feb40694920a7fea98e956b042e082b9e1adad;
const CHAINLINK_ETH_USD_ADDRESS: felt252 =
0x6b2ef9b416ad0f996b2a8ac0dd771b1788196f51c96f5b000df2e47ac756d26;
const CHAINLINK_STRK_USD_ADDRESS: felt252 =
0x76a0254cdadb59b86da3b5960bf8d73779cac88edc5ae587cab3cedf03226ec;
const CHAINLINK_USDC_USD_ADDRESS: felt252 =
0x72495dbb867dd3c6373820694008f8a8bff7b41f7f7112245d687858b243470;
}

fn get_current_price_from_proxy(proxy: ContractAddress) -> u128 {
let contract = IAggregatorProxyDispatcher { contract_address: proxy };
let res = contract.latest_round_data();

let now = get_block_timestamp();
assert(res.updated_at > now - 2 * 3600, 'Oracle data stale');

res.answer
}

fn get_chainlink_proxy_address(token_address: ContractAddress) -> Option<ContractAddress> {
let address_felt: felt252 = token_address.into();

if address_felt == TOKEN_ETH_ADDRESS {
return Option::Some(Mainnet::CHAINLINK_ETH_USD_ADDRESS.try_into().unwrap());
}
if address_felt == TOKEN_WBTC_ADDRESS {
return Option::Some(Mainnet::CHAINLINK_WBTC_USD_ADDRESS.try_into().unwrap());
}
if address_felt == TOKEN_STRK_ADDRESS {
return Option::Some(Mainnet::CHAINLINK_STRK_USD_ADDRESS.try_into().unwrap());
}
if address_felt == TOKEN_USDC_ADDRESS {
return Option::Some(Mainnet::CHAINLINK_USDC_USD_ADDRESS.try_into().unwrap());
}

Option::None(())
}
}
34 changes: 34 additions & 0 deletions src/amm_core/state.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ mod State {
use carmine_protocol::amm_core::amm::AMM::latest_oracle_price;
use carmine_protocol::amm_core::amm::AMM::latest_oracle_priceContractMemberStateTrait;

use carmine_protocol::amm_core::amm::AMM::historical_oracle_price_cache;
use carmine_protocol::amm_core::amm::AMM::historical_oracle_price_cacheContractMemberStateTrait;

use carmine_protocol::amm_core::amm::AMM;

Expand Down Expand Up @@ -558,6 +560,38 @@ mod State {
.write((base_token_address, quote_token_address), (price, current_block))
}

// @notice Reads the historical oracle price for given token pair and maturity
// @param base_token_address: Address of the base token
// @param quote_token_address: Address of the quote token
// @param maturity: Maturity for which to fetch historical price
// @returns price: Historical price at maturity
fn read_historical_oracle_price_cache(
base_token_address: ContractAddress,
quote_token_address: ContractAddress,
maturity: Timestamp
) -> Fixed {
let state = AMM::unsafe_new_contract_state();
state
.historical_oracle_price_cache
.read((base_token_address, quote_token_address, maturity))
}

// @notice Writes the historical oracle price for given token pair and maturity
// @param base_token_address: Address of the base token
// @param quote_token_address: Address of the quote token
// @param maturity: Maturity for which to fetch historical price
// @param price: Price at maturity which'll be stored
fn write_historical_oracle_price_cache(
base_token_address: ContractAddress,
quote_token_address: ContractAddress,
maturity: Timestamp,
price: Fixed
) {
let mut state = AMM::unsafe_new_contract_state();
state
.historical_oracle_price_cache
.write((base_token_address, quote_token_address, maturity), price)
}

// @notice Returns Option struct with addition info based on several option parameters
// @param lptoken_address: Address of liquidity pool that corresponds to the option
Expand Down
1 change: 1 addition & 0 deletions src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod amm_core {
mod oracles {
mod agg;
mod pragma;
mod chainlink;
mod oracle_helpers;
}
mod pricing {
Expand Down