@@ -18,6 +18,8 @@ use query_types::CheckpointQuery;
1818use query_types:: CoinMetadata ;
1919use query_types:: CoinMetadataArgs ;
2020use query_types:: CoinMetadataQuery ;
21+ use query_types:: DryRunArgs ;
22+ use query_types:: DryRunQuery ;
2123use query_types:: EpochSummaryArgs ;
2224use query_types:: EpochSummaryQuery ;
2325use query_types:: EventFilter ;
@@ -40,9 +42,10 @@ use query_types::TransactionBlockArgs;
4042use query_types:: TransactionBlockQuery ;
4143use query_types:: TransactionBlocksQuery ;
4244use query_types:: TransactionBlocksQueryArgs ;
45+ use query_types:: TransactionMetadata ;
4346use query_types:: TransactionsFilter ;
4447use query_types:: Validator ;
45- use reqwest :: Url ;
48+
4649use sui_types:: types:: framework:: Coin ;
4750use sui_types:: types:: Address ;
4851use sui_types:: types:: CheckpointSequenceNumber ;
@@ -52,6 +55,7 @@ use sui_types::types::Object;
5255use sui_types:: types:: SignedTransaction ;
5356use sui_types:: types:: Transaction ;
5457use sui_types:: types:: TransactionEffects ;
58+ use sui_types:: types:: TransactionKind ;
5559use sui_types:: types:: UserSignature ;
5660
5761use anyhow:: anyhow;
@@ -64,6 +68,7 @@ use cynic::MutationBuilder;
6468use cynic:: Operation ;
6569use cynic:: QueryBuilder ;
6670use futures:: Stream ;
71+ use reqwest:: Url ;
6772use std:: pin:: Pin ;
6873
6974const MAINNET_HOST : & str = "https://sui-mainnet.mystenlabs.com/graphql" ;
@@ -72,6 +77,12 @@ const DEVNET_HOST: &str = "https://sui-devnet.mystenlabs.com/graphql";
7277const LOCAL_HOST : & str = "http://localhost:9125/graphql" ;
7378static USER_AGENT : & str = concat ! ( env!( "CARGO_PKG_NAME" ) , "/" , env!( "CARGO_PKG_VERSION" ) , ) ;
7479
80+ #[ derive( Debug ) ]
81+ pub struct DryRunResult {
82+ pub effects : Option < TransactionEffects > ,
83+ pub error : Option < String > ,
84+ }
85+
7586#[ derive( Debug ) ]
7687/// A page of items returned by the GraphQL server.
7788pub struct Page < T > {
@@ -671,6 +682,87 @@ impl Client {
671682 }
672683 }
673684
685+ // ===========================================================================
686+ // Dry Run API
687+ // ===========================================================================
688+
689+ /// Dry run a [`Transaction`] and return the transaction effects and dry run error (if any).
690+ ///
691+ /// `skipChecks` optional flag disables the usual verification checks that prevent access to
692+ /// objects that are owned by addresses other than the sender, and calling non-public,
693+ /// non-entry functions, and some other checks. Defaults to false.
694+ pub async fn dry_run_tx (
695+ & self ,
696+ tx : & Transaction ,
697+ skip_checks : Option < bool > ,
698+ ) -> Result < DryRunResult , Error > {
699+ let tx_bytes = base64ct:: Base64 :: encode_string (
700+ & bcs:: to_bytes ( & tx) . map_err ( |_| Error :: msg ( "Cannot encode Transaction as BCS" ) ) ?,
701+ ) ;
702+ self . dry_run ( tx_bytes, skip_checks, None ) . await
703+ }
704+
705+ /// Dry run a [`TransactionKind`] and return the transaction effects and dry run error (if any).
706+ ///
707+ /// `skipChecks` optional flag disables the usual verification checks that prevent access to
708+ /// objects that are owned by addresses other than the sender, and calling non-public,
709+ /// non-entry functions, and some other checks. Defaults to false.
710+ ///
711+ /// `tx_meta` is the transaction metadata.
712+ pub async fn dry_run_tx_kind (
713+ & self ,
714+ tx_kind : & TransactionKind ,
715+ skip_checks : Option < bool > ,
716+ tx_meta : TransactionMetadata ,
717+ ) -> Result < DryRunResult , Error > {
718+ let tx_bytes = base64ct:: Base64 :: encode_string (
719+ & bcs:: to_bytes ( & tx_kind) . map_err ( |_| Error :: msg ( "Cannot encode Transaction as BCS" ) ) ?,
720+ ) ;
721+ self . dry_run ( tx_bytes, skip_checks, Some ( tx_meta) ) . await
722+ }
723+
724+ /// Internal implementation of the dry run API.
725+ async fn dry_run (
726+ & self ,
727+ tx_bytes : String ,
728+ skip_checks : Option < bool > ,
729+ tx_meta : Option < TransactionMetadata > ,
730+ ) -> Result < DryRunResult , Error > {
731+ let skip_checks = skip_checks. unwrap_or ( false ) ;
732+ let operation = DryRunQuery :: build ( DryRunArgs {
733+ tx_bytes,
734+ skip_checks,
735+ tx_meta,
736+ } ) ;
737+ let response = self . run_query ( & operation) . await ?;
738+
739+ // Query errors
740+ if let Some ( errors) = response. errors {
741+ return Err ( Error :: msg ( format ! ( "{:?}" , errors) ) ) ;
742+ }
743+
744+ // Dry Run errors
745+ let error = response
746+ . data
747+ . as_ref ( )
748+ . and_then ( |tx| tx. dry_run_transaction_block . error . clone ( ) ) ;
749+
750+ let effects = response
751+ . data
752+ . map ( |tx| tx. dry_run_transaction_block )
753+ . and_then ( |tx| tx. transaction )
754+ . and_then ( |tx| tx. effects )
755+ . and_then ( |bcs| bcs. bcs )
756+ . map ( |bcs| base64ct:: Base64 :: decode_vec ( bcs. 0 . as_str ( ) ) )
757+ . transpose ( )
758+ . map_err ( |_| Error :: msg ( "Cannot decode bcs bytes from Base64 for transaction effects" ) ) ?
759+ . map ( |bcs| bcs:: from_bytes :: < TransactionEffects > ( & bcs) )
760+ . transpose ( )
761+ . map_err ( |_| Error :: msg ( "Cannot decode bcs bytes into TransactionEffects" ) ) ?;
762+
763+ Ok ( DryRunResult { effects, error } )
764+ }
765+
674766 // ===========================================================================
675767 // Transaction API
676768 // ===========================================================================
@@ -1061,4 +1153,15 @@ mod tests {
10611153 ) ;
10621154 }
10631155 }
1156+
1157+ #[ tokio:: test]
1158+ async fn test_dry_run ( ) {
1159+ let client = Client :: new_testnet ( ) ;
1160+ // this tx bytes works on testnet
1161+ let tx_bytes = "AAACAAiA8PoCAAAAAAAg7q6yDns6nPznaKLd9pUD2K6NFiiibC10pDVQHJKdP2kCAgABAQAAAQECAAABAQBGLuHCJ/xjZfhC4vTJt/Zrvq1gexKLaKf3aVzyIkxRaAFUHzz8ftiZdY25qP4f9zySuT1K/qyTWjbGiTu0i0Z1ZFA4gwUAAAAAILeG86EeQm3qY3ajat3iUnY2Gbrk/NbdwV/d9MZviAwwRi7hwif8Y2X4QuL0ybf2a76tYHsSi2in92lc8iJMUWjoAwAAAAAAAECrPAAAAAAAAA==" ;
1162+
1163+ let dry_run = client. dry_run ( tx_bytes. to_string ( ) , None , None ) . await ;
1164+
1165+ assert ! ( dry_run. is_ok( ) ) ;
1166+ }
10641167}
0 commit comments