Skip to content

Commit 3cfa333

Browse files
stefan-mystenDaughterOfMars
authored andcommitted
sui-graphql-client: add dry run query (#23)
1 parent c2fc061 commit 3cfa333

File tree

4 files changed

+178
-3
lines changed

4 files changed

+178
-3
lines changed

crates/sui-graphql-client/src/lib.rs

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use query_types::CheckpointQuery;
1818
use query_types::CoinMetadata;
1919
use query_types::CoinMetadataArgs;
2020
use query_types::CoinMetadataQuery;
21+
use query_types::DryRunArgs;
22+
use query_types::DryRunQuery;
2123
use query_types::EpochSummaryArgs;
2224
use query_types::EpochSummaryQuery;
2325
use query_types::EventFilter;
@@ -40,9 +42,10 @@ use query_types::TransactionBlockArgs;
4042
use query_types::TransactionBlockQuery;
4143
use query_types::TransactionBlocksQuery;
4244
use query_types::TransactionBlocksQueryArgs;
45+
use query_types::TransactionMetadata;
4346
use query_types::TransactionsFilter;
4447
use query_types::Validator;
45-
use reqwest::Url;
48+
4649
use sui_types::types::framework::Coin;
4750
use sui_types::types::Address;
4851
use sui_types::types::CheckpointSequenceNumber;
@@ -52,6 +55,7 @@ use sui_types::types::Object;
5255
use sui_types::types::SignedTransaction;
5356
use sui_types::types::Transaction;
5457
use sui_types::types::TransactionEffects;
58+
use sui_types::types::TransactionKind;
5559
use sui_types::types::UserSignature;
5660

5761
use anyhow::anyhow;
@@ -64,6 +68,7 @@ use cynic::MutationBuilder;
6468
use cynic::Operation;
6569
use cynic::QueryBuilder;
6670
use futures::Stream;
71+
use reqwest::Url;
6772
use std::pin::Pin;
6873

6974
const MAINNET_HOST: &str = "https://sui-mainnet.mystenlabs.com/graphql";
@@ -72,6 +77,12 @@ const DEVNET_HOST: &str = "https://sui-devnet.mystenlabs.com/graphql";
7277
const LOCAL_HOST: &str = "http://localhost:9125/graphql";
7378
static 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.
7788
pub 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
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use sui_types::types::ObjectReference;
5+
6+
use crate::query_types::schema;
7+
use crate::query_types::Address;
8+
use crate::query_types::TransactionBlock;
9+
10+
#[derive(cynic::QueryFragment, Debug)]
11+
#[cynic(schema = "rpc", graphql_type = "Query", variables = "DryRunArgs")]
12+
pub struct DryRunQuery {
13+
#[arguments(txBytes: $tx_bytes, skipChecks: $skip_checks, txMeta: $tx_meta)]
14+
pub dry_run_transaction_block: DryRunResult,
15+
}
16+
17+
#[derive(cynic::QueryFragment, Debug)]
18+
#[cynic(schema = "rpc", graphql_type = "DryRunResult")]
19+
pub struct DryRunResult {
20+
pub error: Option<String>,
21+
pub transaction: Option<TransactionBlock>,
22+
}
23+
24+
#[derive(cynic::QueryVariables, Debug)]
25+
pub struct DryRunArgs {
26+
pub tx_bytes: String,
27+
pub skip_checks: bool,
28+
pub tx_meta: Option<TransactionMetadata>,
29+
}
30+
31+
#[derive(cynic::InputObject, Debug)]
32+
#[cynic(schema = "rpc", graphql_type = "TransactionMetadata")]
33+
pub struct TransactionMetadata {
34+
pub gas_budget: Option<u64>,
35+
pub gas_objects: Option<Vec<ObjectRef>>,
36+
pub gas_price: Option<u64>,
37+
pub gas_sponsor: Option<Address>,
38+
pub sender: Option<Address>,
39+
}
40+
41+
#[derive(cynic::InputObject, Debug)]
42+
#[cynic(schema = "rpc", graphql_type = "ObjectRef")]
43+
pub struct ObjectRef {
44+
pub address: Address,
45+
pub digest: String,
46+
pub version: u64,
47+
}
48+
49+
impl From<ObjectReference> for ObjectRef {
50+
fn from(value: ObjectReference) -> Self {
51+
let address: Address = (*value.object_id()).into();
52+
ObjectRef {
53+
address,
54+
version: value.version(),
55+
digest: value.digest().to_string(),
56+
}
57+
}
58+
}

crates/sui-graphql-client/src/query_types/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod balance;
66
mod chain;
77
mod checkpoint;
88
mod coin;
9+
mod dry_run;
910
mod epoch;
1011
mod events;
1112
mod execute_tx;
@@ -20,7 +21,6 @@ pub use active_validators::EpochValidator;
2021
pub use active_validators::Validator;
2122
pub use active_validators::ValidatorConnection;
2223
pub use active_validators::ValidatorSet;
23-
use anyhow::anyhow;
2424
pub use balance::Balance;
2525
pub use balance::BalanceArgs;
2626
pub use balance::BalanceQuery;
@@ -32,6 +32,10 @@ pub use checkpoint::CheckpointQuery;
3232
pub use coin::CoinMetadata;
3333
pub use coin::CoinMetadataArgs;
3434
pub use coin::CoinMetadataQuery;
35+
pub use dry_run::DryRunArgs;
36+
pub use dry_run::DryRunQuery;
37+
pub use dry_run::DryRunResult;
38+
pub use dry_run::TransactionMetadata;
3539
pub use epoch::Epoch;
3640
pub use epoch::EpochSummaryArgs;
3741
pub use epoch::EpochSummaryQuery;
@@ -55,13 +59,16 @@ pub use protocol_config::ProtocolVersionArgs;
5559
pub use service_config::Feature;
5660
pub use service_config::ServiceConfig;
5761
pub use service_config::ServiceConfigQuery;
58-
use sui_types::types::Address;
62+
pub use transaction::TransactionBlock;
5963
pub use transaction::TransactionBlockArgs;
6064
pub use transaction::TransactionBlockQuery;
6165
pub use transaction::TransactionBlocksQuery;
6266
pub use transaction::TransactionBlocksQueryArgs;
6367
pub use transaction::TransactionsFilter;
6468

69+
use sui_types::types::Address;
70+
71+
use anyhow::anyhow;
6572
use cynic::impl_scalar;
6673

6774
#[cynic::schema("rpc")]

crates/sui-graphql-client/src/query_types/transaction.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ pub struct TransactionBlocksQueryArgs {
5858
#[cynic(schema = "rpc", graphql_type = "TransactionBlock")]
5959
pub struct TransactionBlock {
6060
pub bcs: Option<Base64>,
61+
pub effects: Option<TransactionBlockEffects>,
62+
}
63+
64+
#[derive(cynic::QueryFragment, Debug)]
65+
#[cynic(schema = "rpc", graphql_type = "TransactionBlockEffects")]
66+
pub struct TransactionBlockEffects {
67+
pub bcs: Option<Base64>,
6168
}
6269

6370
#[derive(cynic::Enum, Clone, Copy, Debug)]

0 commit comments

Comments
 (0)