@@ -20,7 +20,10 @@ use aptos_rest_client::{
2020 error:: { AptosErrorResponse , RestError } ,
2121 Client ,
2222} ;
23- use aptos_types:: { account_address:: AccountAddress , account_config:: AccountResource } ;
23+ use aptos_types:: {
24+ account_address:: AccountAddress ,
25+ account_config:: { fungible_store:: FungibleStoreResource , AccountResource } ,
26+ } ;
2427use move_core_types:: {
2528 ident_str,
2629 identifier:: IdentStr ,
@@ -126,18 +129,18 @@ async fn get_balances(
126129 if account. is_base_account ( ) {
127130 balances =
128131 get_base_balances ( & rest_client, owner_address, version, currencies_to_lookup) . await ?;
129- } else if pool_address. is_some ( ) {
132+ } else if let Some ( currency) = account. secondary_store_currency ( ) {
133+ if currencies_to_lookup. contains ( currency) {
134+ balances =
135+ get_secondary_store_balance ( & rest_client, owner_address, version, currency) . await ?;
136+ }
137+ } else if let Some ( pool_address) = pool_address {
130138 // Lookup the delegation pool, if it's provided in the account information
131139 // Filter appropriately, must have native coin
132140 if currencies_to_lookup. contains ( & native_coin ( ) ) {
133- ( balances, lockup_expiration) = get_delegation_info (
134- & rest_client,
135- & account,
136- owner_address,
137- pool_address. unwrap ( ) ,
138- version,
139- )
140- . await ?;
141+ ( balances, lockup_expiration) =
142+ get_delegation_info ( & rest_client, & account, owner_address, pool_address, version)
143+ . await ?;
141144 }
142145 } else {
143146 // Retrieve staking information (if it applies)
@@ -159,6 +162,88 @@ async fn get_balances(
159162 ) )
160163}
161164
165+ async fn get_secondary_store_balance (
166+ rest_client : & Client ,
167+ store_address : AccountAddress ,
168+ version : u64 ,
169+ currency : & Currency ,
170+ ) -> ApiResult < Vec < Amount > > {
171+ match rest_client
172+ . get_account_resource_at_version_bcs (
173+ store_address,
174+ "0x1::fungible_asset::FungibleStore" ,
175+ version,
176+ )
177+ . await
178+ {
179+ Ok ( response) => {
180+ let store: FungibleStoreResource = response. into_inner ( ) ;
181+ Ok ( vec ! [ secondary_store_amount( & store, currency) ?] )
182+ } ,
183+ Err ( RestError :: Api ( AptosErrorResponse {
184+ error :
185+ AptosError {
186+ error_code : AptosErrorCode :: AccountNotFound ,
187+ ..
188+ } ,
189+ ..
190+ } ) )
191+ | Err ( RestError :: Api ( AptosErrorResponse {
192+ error :
193+ AptosError {
194+ error_code : AptosErrorCode :: ResourceNotFound ,
195+ ..
196+ } ,
197+ ..
198+ } ) ) => Ok ( vec ! [ Amount {
199+ value: 0 . to_string( ) ,
200+ currency: currency. clone( ) ,
201+ } ] ) ,
202+ Err ( err) => Err ( ApiError :: InternalError ( Some ( format ! (
203+ "Failed to retrieve secondary store balance: {}" ,
204+ err
205+ ) ) ) ) ,
206+ }
207+ }
208+
209+ fn secondary_store_amount ( store : & FungibleStoreResource , currency : & Currency ) -> ApiResult < Amount > {
210+ let expected_metadata = currency_fa_metadata_address ( currency) ?. ok_or_else ( || {
211+ ApiError :: InvalidInput ( Some ( format ! (
212+ "Currency {} does not identify a fungible asset metadata address" ,
213+ currency. symbol
214+ ) ) )
215+ } ) ?;
216+
217+ if store. metadata ( ) != expected_metadata {
218+ return Err ( ApiError :: InvalidInput ( Some ( format ! (
219+ "Secondary store metadata {} does not match requested currency metadata {}" ,
220+ store. metadata( ) ,
221+ expected_metadata
222+ ) ) ) ) ;
223+ }
224+
225+ Ok ( Amount {
226+ value : store. balance ( ) . to_string ( ) ,
227+ currency : currency. clone ( ) ,
228+ } )
229+ }
230+
231+ fn currency_fa_metadata_address ( currency : & Currency ) -> ApiResult < Option < AccountAddress > > {
232+ if let Some ( metadata) = currency. metadata . as_ref ( ) {
233+ if let Some ( fa_address) = metadata. fa_address . as_ref ( ) {
234+ return AccountAddress :: from_str ( fa_address)
235+ . map ( Some )
236+ . map_err ( |err| ApiError :: InvalidInput ( Some ( err. to_string ( ) ) ) ) ;
237+ }
238+ }
239+
240+ if currency == & native_coin ( ) {
241+ Ok ( Some ( AccountAddress :: TEN ) )
242+ } else {
243+ Ok ( None )
244+ }
245+ }
246+
162247async fn get_sequence_number (
163248 rest_client : & Client ,
164249 owner_address : AccountAddress ,
@@ -403,3 +488,56 @@ pub async fn view<T: DeserializeOwned>(
403488 . await ?
404489 . into_inner ( ) )
405490}
491+
492+ #[ cfg( test) ]
493+ mod tests {
494+ use super :: * ;
495+ use crate :: types:: CurrencyMetadata ;
496+
497+ #[ test]
498+ fn test_secondary_store_amount_for_native_coin ( ) {
499+ let store = FungibleStoreResource :: new ( AccountAddress :: TEN , 50_000_000 , false ) ;
500+ let amount = secondary_store_amount ( & store, & native_coin ( ) ) . unwrap ( ) ;
501+
502+ assert_eq ! ( amount. value, "50000000" ) ;
503+ assert_eq ! ( amount. currency, native_coin( ) ) ;
504+ }
505+
506+ #[ test]
507+ fn test_secondary_store_amount_preserves_fa_currency ( ) {
508+ let metadata_address =
509+ AccountAddress :: from_str ( "0x12341234123412341234123412341234" ) . unwrap ( ) ;
510+ let currency = Currency {
511+ symbol : "FUN" . to_string ( ) ,
512+ decimals : 2 ,
513+ metadata : Some ( CurrencyMetadata {
514+ move_type : None ,
515+ fa_address : Some ( metadata_address. to_string ( ) ) ,
516+ } ) ,
517+ } ;
518+ let store = FungibleStoreResource :: new ( metadata_address, 123 , false ) ;
519+ let amount = secondary_store_amount ( & store, & currency) . unwrap ( ) ;
520+
521+ assert_eq ! ( amount. value, "123" ) ;
522+ assert_eq ! ( amount. currency, currency) ;
523+ }
524+
525+ #[ test]
526+ fn test_secondary_store_amount_rejects_mismatched_currency ( ) {
527+ let store_metadata =
528+ AccountAddress :: from_str ( "0x12341234123412341234123412341234" ) . unwrap ( ) ;
529+ let requested_metadata =
530+ AccountAddress :: from_str ( "0x56785678567856785678567856785678" ) . unwrap ( ) ;
531+ let currency = Currency {
532+ symbol : "FUN" . to_string ( ) ,
533+ decimals : 2 ,
534+ metadata : Some ( CurrencyMetadata {
535+ move_type : None ,
536+ fa_address : Some ( requested_metadata. to_string ( ) ) ,
537+ } ) ,
538+ } ;
539+ let store = FungibleStoreResource :: new ( store_metadata, 123 , false ) ;
540+
541+ assert ! ( secondary_store_amount( & store, & currency) . is_err( ) ) ;
542+ }
543+ }
0 commit comments