@@ -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};
77use pragma :: admin :: admin :: Ownable ;
88use 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