11//! Asset info service.
22use crate :: {
3+ chains:: Chain ,
34 config:: RelayConfig ,
45 error:: { AssetError , RelayError } ,
56 types:: {
@@ -17,8 +18,10 @@ use alloy::{
1718 sol_types:: SolCall ,
1819 transports:: TransportErrorKind ,
1920} ;
21+ use futures_util:: future:: join_all;
2022use schnellru:: { ByLength , LruMap } ;
2123use std:: {
24+ collections:: hash_map:: Entry ,
2225 pin:: Pin ,
2326 task:: { Context , Poll , ready} ,
2427} ;
@@ -29,7 +32,7 @@ use tokio::{
2932 } ,
3033 try_join,
3134} ;
32- use tracing:: { error, trace} ;
35+ use tracing:: { error, info , trace} ;
3336
3437/// Messages accepted by the [`AssetInfoService`].
3538#[ derive( Debug ) ]
@@ -203,7 +206,34 @@ impl AssetInfoServiceHandle {
203206
204207 Ok ( builder. build ( metadata, tokens_uris) )
205208 }
209+
210+ /// Populate fee token metadata cache for all chains.
211+ ///
212+ /// These will never be evicted.
213+ pub async fn populate_fee_tokens ( & self , chains : & HashMap < ChainId , Chain > ) {
214+ info ! ( "Populating fee token metadata for all chains" ) ;
215+
216+ let _ = join_all ( chains. values ( ) . filter ( |chain| !chain. assets ( ) . fee_tokens ( ) . is_empty ( ) ) . map (
217+ async |chain| {
218+ let fee_tokens = chain. assets ( ) . fee_tokens ( ) ;
219+
220+ self . get_asset_info_list (
221+ chain. provider ( ) ,
222+ fee_tokens. iter ( ) . map ( |( _, desc) | Asset :: from ( desc. address ) ) . collect ( ) ,
223+ )
224+ . await
225+ . inspect ( |_| {
226+ info ! ( chain_id = chain. id( ) , count = fee_tokens. len( ) , "Pre-populated fee tokens" )
227+ } )
228+ . inspect_err ( |e| {
229+ error ! ( chain_id = chain. id( ) , error = %e, "Failed to pre-populate fee tokens" )
230+ } )
231+ } ,
232+ ) )
233+ . await ;
234+ }
206235}
236+
207237/// Service that provides [`AssetWithInfo`] about any kind of asset.
208238///
209239/// TODO: apart from onchain, there should be a more trusted source that can be passed when
@@ -212,6 +242,11 @@ impl AssetInfoServiceHandle {
212242pub struct AssetInfoService {
213243 /// Cached asset metadata per chain.
214244 cache : LruMap < ( ChainId , Asset ) , AssetWithInfo > ,
245+ /// Fee token metadata per chain. These are never evicted.
246+ ///
247+ /// Keys are populated during initialization based on config, but values start as `None`
248+ /// and get populated later by chains when the metadata is fetched.
249+ fee_tokens : HashMap < ( ChainId , Asset ) , Option < AssetWithInfo > > ,
215250 /// Sender half for asset info messages.
216251 command_tx : UnboundedSender < AssetInfoServiceMessage > ,
217252 /// Incoming messages for the service.
@@ -225,8 +260,23 @@ impl AssetInfoService {
225260 pub fn new ( capacity : u32 , config : & RelayConfig ) -> Self {
226261 let ( command_tx, command_rx) = unbounded_channel ( ) ;
227262
263+ let mut fee_tokens = HashMap :: default ( ) ;
264+ for ( chain, chain_config) in & config. chains {
265+ for ( _, asset_desc) in chain_config. assets . iter ( ) {
266+ if asset_desc. fee_token {
267+ let asset = if asset_desc. address . is_zero ( ) {
268+ Asset :: Native
269+ } else {
270+ Asset :: Token ( asset_desc. address )
271+ } ;
272+ fee_tokens. insert ( ( chain. id ( ) , asset) , None ) ;
273+ }
274+ }
275+ }
276+
228277 Self {
229278 cache : LruMap :: new ( ByLength :: new ( capacity) ) ,
279+ fee_tokens,
230280 command_tx,
231281 command_rx,
232282 native_symbols : config
@@ -272,7 +322,10 @@ impl AssetInfoService {
272322 } ,
273323 } )
274324 } else {
275- self . cache . get ( & ( chain_id, asset) ) . cloned ( )
325+ self . fee_tokens
326+ . get ( & ( chain_id, asset) )
327+ . and_then ( |opt| opt. clone ( ) )
328+ . or_else ( || self . cache . get ( & ( chain_id, asset) ) . cloned ( ) )
276329 } ;
277330
278331 ( asset, info)
@@ -286,7 +339,13 @@ impl AssetInfoService {
286339 trace ! ( chain_id, ?assets, "Received update request for asset infos." ) ;
287340
288341 for asset_with_info in assets {
289- self . cache . get_or_insert ( ( chain_id, asset_with_info. asset ) , || asset_with_info) ;
342+ let key = ( chain_id, asset_with_info. asset ) ;
343+
344+ if let Entry :: Occupied ( mut e) = self . fee_tokens . entry ( key) {
345+ e. insert ( Some ( asset_with_info) ) ;
346+ } else {
347+ self . cache . get_or_insert ( key, || asset_with_info) ;
348+ }
290349 }
291350 }
292351}
0 commit comments