Skip to content

Commit 4493e36

Browse files
committed
Merge branch 'main' into 0x/bump
2 parents 97f98e5 + b5d56be commit 4493e36

File tree

3 files changed

+246
-44
lines changed

3 files changed

+246
-44
lines changed

pragma-oracle/src/entry/structs.cairo

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,3 +404,10 @@ impl CheckpointStorePacking of StorePacking<Checkpoint, felt252> {
404404
}
405405
}
406406
}
407+
408+
409+
#[derive(Copy, Drop, Serde, starknet::Store)]
410+
struct TokenizedVaultInfo {
411+
vault_address: ContractAddress,
412+
underlying_asset: felt252,
413+
}

pragma-oracle/src/oracle/oracle.cairo

Lines changed: 83 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use pragma::entry::structs::{
22
BaseEntry, SpotEntry, Currency, Pair, DataType, PragmaPricesResponse, Checkpoint,
33
USD_CURRENCY_ID, SPOT, FUTURE, OPTION, GENERIC, FutureEntry, OptionEntry, GenericEntry,
44
SimpleDataType, AggregationMode, PossibleEntries, ArrayEntry, EntryStorage, HasPrice,
5-
HasBaseEntry, GenericEntryStorage
5+
HasBaseEntry, GenericEntryStorage, TokenizedVaultInfo
66
};
77
use pragma::admin::admin::Ownable;
88
use pragma::upgradeable::upgradeable::Upgradeable;
@@ -90,7 +90,7 @@ trait IOracleABI<TContractState> {
9090
fn add_currency(ref self: TContractState, new_currency: Currency);
9191
fn update_currency(ref self: TContractState, currency_id: felt252, currency: Currency);
9292
fn get_currency(self: @TContractState, currency_id: felt252) -> Currency;
93-
fn get_tokenized_vaults(self: @TContractState, token: felt252) -> ContractAddress;
93+
fn get_tokenized_vaults(self: @TContractState, token: felt252) -> TokenizedVaultInfo;
9494
fn update_pair(ref self: TContractState, pair_id: felt252, pair: Pair);
9595
fn add_pair(ref self: TContractState, new_pair: Pair);
9696
fn get_pair(self: @TContractState, pair_id: felt252) -> Pair;
@@ -103,7 +103,10 @@ trait IOracleABI<TContractState> {
103103
fn remove_source(ref self: TContractState, source: felt252, data_type: DataType) -> bool;
104104
fn set_sources_threshold(ref self: TContractState, threshold: u32);
105105
fn register_tokenized_vault(
106-
ref self: TContractState, token: felt252, token_address: ContractAddress
106+
ref self: TContractState,
107+
token: felt252,
108+
underlying_token: felt252,
109+
token_address: ContractAddress
107110
);
108111
fn get_registered_conversion_rate_pairs(self: @TContractState) -> Span<felt252>;
109112
fn add_registered_conversion_rate_pair(ref self: TContractState, new_pair_id: felt252);
@@ -188,7 +191,7 @@ mod Oracle {
188191
storage_base_address_from_felt252, Store, StorageBaseAddress, SyscallResult,
189192
ContractAddress, get_caller_address, ClassHash, Into, TryInto, ResultTrait, ResultTraitImpl,
190193
BoxTrait, ArrayTrait, SpanTrait, Zeroable, IOracleABI, EntryStorage, List, ListTrait,
191-
HasPrice, HasBaseEntry, GenericEntryStorage
194+
HasPrice, HasBaseEntry, GenericEntryStorage, TokenizedVaultInfo
192195
};
193196
use alexandria_data_structures::array_ext::SpanTraitExt;
194197
use hash::LegacyHash;
@@ -243,10 +246,12 @@ mod Oracle {
243246
//oracle_checkpoint_index, legacyMap between (pair_id, (SPOT/FUTURES/OPTIONS), expiration_timestamp (0 for SPOT)) and the index of the last checkpoint
244247
oracle_checkpoint_index: LegacyMap::<(felt252, felt252, u64, u8), u64>,
245248
oracle_sources_threshold_storage: u32,
246-
// registry containing registered tokenized vaults and the corresponding address
249+
// registry containing registered tokenized vaults and the corresponding address (BACKWARD COMPATIBILITY)
247250
tokenized_vault: LegacyMap<(felt252, felt252), ContractAddress>,
248251
// registry containing the conversion rate compatible currencies
249252
conversion_rate_compatible_pairs: List<felt252>,
253+
// extension of tokenized vault to support multiple underlying assets
254+
extended_tokenized_vault: LegacyMap<felt252, TokenizedVaultInfo>,
250255
}
251256

252257

@@ -949,8 +954,13 @@ mod Oracle {
949954
Ownable::OwnableImpl::owner(@state)
950955
}
951956

952-
fn get_tokenized_vaults(self: @ContractState, token: felt252) -> ContractAddress {
953-
self.tokenized_vault.read((token, 'STRK'))
957+
fn get_tokenized_vaults(self: @ContractState, token: felt252) -> TokenizedVaultInfo {
958+
// We assume that the token available with STRK as underlying are distinct from those with other underlyings
959+
let address = self.tokenized_vault.read((token, 'STRK'));
960+
if address != 0.try_into().unwrap() {
961+
return TokenizedVaultInfo { vault_address: address, underlying_asset: 'STRK', };
962+
}
963+
self.extended_tokenized_vault.read(token)
954964
}
955965

956966

@@ -1817,11 +1827,26 @@ mod Oracle {
18171827
// @param token The token to register
18181828
// @param token_address Token address to register
18191829
fn register_tokenized_vault(
1820-
ref self: ContractState, token: felt252, token_address: ContractAddress
1830+
ref self: ContractState,
1831+
token: felt252,
1832+
underlying_token: felt252,
1833+
token_address: ContractAddress
18211834
) {
18221835
OracleInternal::assert_only_admin();
18231836
assert(token != 0, 'Token cannot be 0');
1824-
self.tokenized_vault.write((token, 'STRK'), token_address)
1837+
assert(underlying_token != 0, 'Underlying token cannot be 0');
1838+
if (underlying_token == 'STRK') {
1839+
self.tokenized_vault.write((token, 'STRK'), token_address);
1840+
} else {
1841+
self
1842+
.extended_tokenized_vault
1843+
.write(
1844+
token,
1845+
TokenizedVaultInfo {
1846+
vault_address: token_address, underlying_asset: underlying_token
1847+
}
1848+
);
1849+
}
18251850
}
18261851

18271852
// @notice set a new checkpoint for a given data type and and aggregation mode
@@ -2443,29 +2468,60 @@ mod Oracle {
24432468
fn get_conversion_rate_price(
24442469
self: @ContractState, data_type: DataType
24452470
) -> PragmaPricesResponse {
2446-
// Query median for STRK/USD
2447-
let sources = IOracleABI::get_all_sources(self, DataType::SpotEntry('STRK/USD'));
2448-
let response: PragmaPricesResponse = IOracleABI::get_data_for_sources(
2449-
self, DataType::SpotEntry('STRK/USD'), AggregationMode::Median(()), sources
2450-
);
2451-
2452-
// Extract quote asset
2453-
// Here we extract a second time to make sure we panic for future and generic entries
2454-
let asset: felt252 = match data_type {
2455-
DataType::SpotEntry(asset) => asset,
2456-
DataType::FutureEntry(_) => panic_with_felt252('Set only for Spot entries'),
2457-
DataType::GenericEntry(_) => panic_with_felt252('Set only for Spot entries'),
2458-
};
2471+
// First step is retrieve the Pair associated to the data type.
2472+
let pair: Pair = self
2473+
.get_pair(
2474+
match data_type {
2475+
DataType::SpotEntry(asset) => asset,
2476+
DataType::FutureEntry((asset, _)) => asset,
2477+
DataType::GenericEntry(key) => key,
2478+
}
2479+
);
24592480

2460-
// Get quote currency and pool
2461-
let quote_asset: felt252 = self.get_pair(asset).quote_currency_id;
2481+
let quote_asset = pair.quote_currency_id;
24622482
assert(quote_asset != 0, 'Asset not registered');
2463-
let pool_address: ContractAddress = self.tokenized_vault.read((quote_asset, 'STRK'));
2483+
2484+
let tokenized_vault = self.get_tokenized_vaults(quote_asset);
24642485
assert(
2465-
pool_address != starknet::contract_address_const::<0>(),
2486+
tokenized_vault.vault_address != starknet::contract_address_const::<0>(),
24662487
'No pool address for given token'
24672488
);
2468-
let pool = IERC4626Dispatcher { contract_address: pool_address };
2489+
assert(
2490+
(tokenized_vault.underlying_asset == 'STRK')
2491+
|| (tokenized_vault.underlying_asset == 'BTC')
2492+
|| (tokenized_vault.underlying_asset == 'tBTC')
2493+
|| (tokenized_vault.underlying_asset == 'LBTC'),
2494+
'Underlying asset not supported'
2495+
);
2496+
2497+
let pool = IERC4626Dispatcher { contract_address: tokenized_vault.vault_address };
2498+
// The goal is to be able to rebuild the price of the base asset in USD
2499+
// Starting from the base asset and 'USD', we can mathematically build the pair id, but for simplicity we
2500+
// will work with hardcoded value
2501+
let response = if (tokenized_vault.underlying_asset == 'STRK') {
2502+
let sources = IOracleABI::get_all_sources(self, DataType::SpotEntry('STRK/USD'));
2503+
IOracleABI::get_data_for_sources(
2504+
self, DataType::SpotEntry('STRK/USD'), AggregationMode::Median(()), sources
2505+
)
2506+
} else if (tokenized_vault.underlying_asset == 'BTC') {
2507+
// We can pu put else, because we filtered out the case where base asset is neither STRK nor USDC
2508+
let sources = IOracleABI::get_all_sources(self, DataType::SpotEntry('BTC/USD'));
2509+
IOracleABI::get_data_for_sources(
2510+
self, DataType::SpotEntry('BTC/USD'), AggregationMode::Median(()), sources
2511+
)
2512+
} else if (tokenized_vault.underlying_asset == 'tBTC') {
2513+
let sources = IOracleABI::get_all_sources(self, DataType::SpotEntry('tBTC/USD'));
2514+
IOracleABI::get_data_for_sources(
2515+
self, DataType::SpotEntry('tBTC/USD'), AggregationMode::Median(()), sources
2516+
)
2517+
} else {
2518+
// LBTC case
2519+
let sources = IOracleABI::get_all_sources(self, DataType::SpotEntry('LBTC/USD'));
2520+
IOracleABI::get_data_for_sources(
2521+
self, DataType::SpotEntry('LBTC/USD'), AggregationMode::Median(()), sources
2522+
)
2523+
};
2524+
assert(!response.last_updated_timestamp.is_zero(), 'No price available');
24692525

24702526
// Compute adjusted price
24712527
// `preview_mint` takes as argument an e18 and returns an e18

0 commit comments

Comments
 (0)