@@ -17,11 +17,14 @@ use query_types::{
1717} ;
1818use reqwest:: Url ;
1919use sui_types:: types:: {
20- Address , CheckpointSequenceNumber , CheckpointSummary , Event , Object , SignedTransaction ,
20+ framework:: Coin , Address , CheckpointSequenceNumber , CheckpointSummary , Event , Object ,
21+ SignedTransaction ,
2122} ;
2223
2324use anyhow:: { anyhow, ensure, Error , Result } ;
2425use cynic:: { serde, GraphQlResponse , Operation , QueryBuilder } ;
26+ use futures:: Stream ;
27+ use std:: pin:: Pin ;
2528
2629const MAINNET_HOST : & str = "https://sui-mainnet.mystenlabs.com/graphql" ;
2730const TESTNET_HOST : & str = "https://sui-testnet.mystenlabs.com/graphql" ;
@@ -231,6 +234,87 @@ impl Client {
231234 // Coin API
232235 // ===========================================================================
233236
237+ /// Get the list of coins for the specified address.
238+ ///
239+ /// If `coin_type` is not provided, it will default to `0x2::coin::Coin`, which will return all
240+ /// coins. For SUI coin, pass in the coin type: `0x2::coin::Coin<0x2::sui::SUI>`.
241+ pub async fn coins (
242+ & self ,
243+ owner : Address ,
244+ after : Option < & str > ,
245+ before : Option < & str > ,
246+ first : Option < i32 > ,
247+ last : Option < i32 > ,
248+ coin_type : Option < & str > ,
249+ ) -> Result < Option < Page < Coin > > , Error > {
250+ let response = self
251+ . objects (
252+ after,
253+ before,
254+ Some ( ObjectFilter {
255+ type_ : Some ( coin_type. unwrap_or ( "0x2::coin::Coin" ) ) ,
256+ owner : Some ( owner. into ( ) ) ,
257+ object_ids : None ,
258+ object_keys : None ,
259+ } ) ,
260+ first,
261+ last,
262+ )
263+ . await ?;
264+
265+ Ok ( response. map ( |x| {
266+ Page :: new (
267+ x. page_info ,
268+ x. data
269+ . iter ( )
270+ . flat_map ( Coin :: try_from_object)
271+ . map ( |c| c. into_owned ( ) )
272+ . collect :: < Vec < _ > > ( ) ,
273+ )
274+ } ) )
275+ }
276+
277+ /// Stream of coins for the specified address and coin type.
278+ pub fn coins_stream < ' a > (
279+ & ' a self ,
280+ owner : Address ,
281+ coin_type : Option < & ' a str > ,
282+ ) -> Pin < Box < dyn Stream < Item = Result < Coin , Error > > + ' a > > {
283+ Box :: pin ( async_stream:: try_stream! {
284+ let mut after = None ;
285+ loop {
286+ let response = self . objects(
287+ after. as_deref( ) ,
288+ None ,
289+ Some ( ObjectFilter {
290+ type_: Some ( coin_type. unwrap_or( "0x2::coin::Coin" ) ) ,
291+ owner: Some ( owner. into( ) ) ,
292+ object_ids: None ,
293+ object_keys: None ,
294+ } ) ,
295+ None ,
296+ None ,
297+ ) . await ?;
298+
299+ if let Some ( page) = response {
300+ for object in page. data {
301+ if let Some ( coin) = Coin :: try_from_object( & object) {
302+ yield coin. into_owned( ) ;
303+ }
304+ }
305+
306+ if let Some ( end_cursor) = page. page_info. end_cursor {
307+ after = Some ( end_cursor) ;
308+ } else {
309+ break ;
310+ }
311+ } else {
312+ break ;
313+ }
314+ }
315+ } )
316+ }
317+
234318 pub async fn coin_metadata ( & self , coin_type : & str ) -> Result < Option < CoinMetadata > , Error > {
235319 let operation = CoinMetadataQuery :: build ( CoinMetadataArgs { coin_type } ) ;
236320 let response = self . run_query ( & operation) . await ?;
@@ -591,6 +675,8 @@ impl Client {
591675
592676#[ cfg( test) ]
593677mod tests {
678+ use futures:: StreamExt ;
679+
594680 use crate :: { Client , DEFAULT_LOCAL_HOST , DEVNET_HOST , MAINNET_HOST , TESTNET_HOST } ;
595681 const NETWORKS : [ ( & str , & str ) ; 2 ] = [ ( MAINNET_HOST , "35834a8a" ) , ( TESTNET_HOST , "4c78adac" ) ] ;
596682
@@ -783,4 +869,31 @@ mod tests {
783869 ) ;
784870 }
785871 }
872+
873+ #[ tokio:: test]
874+ async fn test_coins_query ( ) {
875+ for ( n, _) in NETWORKS {
876+ let client = Client :: new ( n) . unwrap ( ) ;
877+ let coins = client
878+ . coins ( "0x1" . parse ( ) . unwrap ( ) , None , None , None , None , None )
879+ . await ;
880+ assert ! (
881+ coins. is_ok( ) ,
882+ "Coins query failed for network: {n}. Error: {}" ,
883+ coins. unwrap_err( )
884+ ) ;
885+ }
886+ }
887+
888+ #[ tokio:: test]
889+ async fn test_coins_stream ( ) {
890+ let client = Client :: new_testnet ( ) ;
891+ let mut stream = client. coins_stream ( "0x1" . parse ( ) . unwrap ( ) , None ) ;
892+ let mut num_coins = 0 ;
893+ while let Some ( result) = stream. next ( ) . await {
894+ assert ! ( result. is_ok( ) ) ;
895+ num_coins += 1 ;
896+ }
897+ assert ! ( num_coins > 0 ) ;
898+ }
786899}
0 commit comments