sui_graphql_rpc/
mutation.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::types::transaction_block_effects::TransactionBlockEffectsKind;
5use crate::{
6    error::Error, types::execution_result::ExecutionResult,
7    types::transaction_block_effects::TransactionBlockEffects,
8};
9use async_graphql::*;
10use fastcrypto::encoding::Encoding;
11use fastcrypto::{encoding::Base64, traits::ToFromBytes};
12use sui_json_rpc_types::SuiTransactionBlockResponseOptions;
13use sui_sdk::SuiClient;
14use sui_types::effects::TransactionEffects as NativeTransactionEffects;
15use sui_types::event::Event as NativeEvent;
16use sui_types::quorum_driver_types::ExecuteTransactionRequestType;
17use sui_types::transaction::SenderSignedData;
18use sui_types::{signature::GenericSignature, transaction::Transaction};
19pub struct Mutation;
20
21/// Mutations are used to write to the Sui network.
22#[Object]
23impl Mutation {
24    /// Execute a transaction, committing its effects on chain.
25    ///
26    /// - `txBytes` is a `TransactionData` struct that has been BCS-encoded and then Base64-encoded.
27    /// - `signatures` are a list of `flag || signature || pubkey` bytes, Base64-encoded.
28    ///
29    /// Waits until the transaction has reached finality on chain to return its transaction digest,
30    /// or returns the error that prevented finality if that was not possible. A transaction is
31    /// final when its effects are guaranteed on chain (it cannot be revoked).
32    ///
33    /// There may be a delay between transaction finality and when GraphQL requests (including the
34    /// request that issued the transaction) reflect its effects. As a result, queries that depend
35    /// on indexing the state of the chain (e.g. contents of output objects, address-level balance
36    /// information at the time of the transaction), must wait for indexing to catch up by polling
37    /// for the transaction digest using `Query.transactionBlock`.
38    async fn execute_transaction_block(
39        &self,
40        ctx: &Context<'_>,
41        tx_bytes: String,
42        signatures: Vec<String>,
43    ) -> Result<ExecutionResult> {
44        let sui_sdk_client: &Option<SuiClient> = ctx
45            .data()
46            .map_err(|_| Error::Internal("Unable to fetch Sui SDK client".to_string()))
47            .extend()?;
48        let sui_sdk_client = sui_sdk_client
49            .as_ref()
50            .ok_or_else(|| Error::Internal("Sui SDK client not initialized".to_string()))
51            .extend()?;
52        let tx_data = bcs::from_bytes(
53            &Base64::decode(&tx_bytes)
54                .map_err(|e| {
55                    Error::Client(format!(
56                        "Unable to deserialize transaction bytes from Base64: {e}"
57                    ))
58                })
59                .extend()?,
60        )
61        .map_err(|e| {
62            Error::Client(format!(
63                "Unable to deserialize transaction bytes as BCS: {e}"
64            ))
65        })
66        .extend()?;
67
68        let mut sigs = Vec::new();
69        for sig in signatures {
70            sigs.push(
71                GenericSignature::from_bytes(
72                    &Base64::decode(&sig)
73                        .map_err(|e| {
74                            Error::Client(format!(
75                                "Unable to deserialize signature bytes {sig} from Base64: {e}"
76                            ))
77                        })
78                        .extend()?,
79                )
80                .map_err(|e| Error::Client(format!("Unable to create signature from bytes: {e}")))
81                .extend()?,
82            );
83        }
84        let transaction = Transaction::from_generic_sig_data(tx_data, sigs);
85        let options = SuiTransactionBlockResponseOptions::new()
86            .with_events()
87            .with_raw_input()
88            .with_raw_effects();
89
90        let result = sui_sdk_client
91            .quorum_driver_api()
92            .execute_transaction_block(
93                transaction,
94                options,
95                Some(ExecuteTransactionRequestType::WaitForEffectsCert),
96            )
97            .await
98            // TODO: use proper error type as this could be a client error or internal error
99            // depending on the specific error returned
100            .map_err(|e| Error::Internal(format!("Unable to execute transaction: {e}")))
101            .extend()?;
102
103        let native: NativeTransactionEffects = bcs::from_bytes(&result.raw_effects)
104            .map_err(|e| Error::Internal(format!("Unable to deserialize transaction effects: {e}")))
105            .extend()?;
106        let tx_data: SenderSignedData = bcs::from_bytes(&result.raw_transaction)
107            .map_err(|e| Error::Internal(format!("Unable to deserialize transaction data: {e}")))
108            .extend()?;
109
110        let events = result
111            .events
112            .ok_or_else(|| {
113                Error::Internal("No events are returned from transaction execution".to_string())
114            })?
115            .data
116            .into_iter()
117            .map(|e| NativeEvent {
118                package_id: e.package_id,
119                transaction_module: e.transaction_module,
120                sender: e.sender,
121                type_: e.type_,
122                contents: e.bcs.into_bytes(),
123            })
124            .collect();
125
126        Ok(ExecutionResult {
127            errors: if result.errors.is_empty() {
128                None
129            } else {
130                Some(result.errors)
131            },
132            effects: TransactionBlockEffects {
133                kind: TransactionBlockEffectsKind::Executed {
134                    tx_data,
135                    native,
136                    events,
137                },
138                // set to u64::MAX, as the executed transaction has not been indexed yet
139                checkpoint_viewed_at: u64::MAX,
140            },
141        })
142    }
143}