iota_graphql_rpc/
mutation.rs1use async_graphql::*;
6use fastcrypto::{
7 encoding::{Base64, Encoding},
8 traits::ToFromBytes,
9};
10use iota_json_rpc_types::IotaTransactionBlockResponseOptions;
11use iota_sdk::IotaClient;
12use iota_types::{
13 effects::TransactionEffects as NativeTransactionEffects,
14 event::Event as NativeEvent,
15 quorum_driver_types::ExecuteTransactionRequestType,
16 signature::GenericSignature,
17 transaction::{SenderSignedData, Transaction},
18};
19
20use crate::{
21 error::Error,
22 types::{
23 execution_result::ExecutionResult,
24 transaction_block_effects::{TransactionBlockEffects, TransactionBlockEffectsKind},
25 },
26};
27pub struct Mutation;
28
29#[Object]
31impl Mutation {
32 async fn execute_transaction_block(
52 &self,
53 ctx: &Context<'_>,
54 tx_bytes: String,
55 signatures: Vec<String>,
56 ) -> Result<ExecutionResult> {
57 let iota_sdk_client: &Option<IotaClient> = ctx
58 .data()
59 .map_err(|_| Error::Internal("Unable to fetch IOTA SDK client".to_string()))
60 .extend()?;
61 let iota_sdk_client = iota_sdk_client
62 .as_ref()
63 .ok_or_else(|| Error::Internal("IOTA SDK client not initialized".to_string()))
64 .extend()?;
65 let tx_data = bcs::from_bytes(
66 &Base64::decode(&tx_bytes)
67 .map_err(|e| {
68 Error::Client(format!(
69 "Unable to deserialize transaction bytes from Base64: {e}"
70 ))
71 })
72 .extend()?,
73 )
74 .map_err(|e| {
75 Error::Client(format!(
76 "Unable to deserialize transaction bytes as BCS: {e}"
77 ))
78 })
79 .extend()?;
80
81 let mut sigs = Vec::new();
82 for sig in signatures {
83 sigs.push(
84 GenericSignature::from_bytes(
85 &Base64::decode(&sig)
86 .map_err(|e| {
87 Error::Client(format!(
88 "Unable to deserialize signature bytes {sig} from Base64: {e}"
89 ))
90 })
91 .extend()?,
92 )
93 .map_err(|e| Error::Client(format!("Unable to create signature from bytes: {e}")))
94 .extend()?,
95 );
96 }
97 let transaction = Transaction::from_generic_sig_data(tx_data, sigs);
98 let options = IotaTransactionBlockResponseOptions::new()
99 .with_events()
100 .with_raw_input()
101 .with_raw_effects();
102
103 let result = iota_sdk_client
104 .quorum_driver_api()
105 .execute_transaction_block(
106 transaction,
107 options,
108 Some(ExecuteTransactionRequestType::WaitForEffectsCert),
109 )
110 .await
111 .map_err(|e| Error::Internal(format!("Unable to execute transaction: {e}")))
114 .extend()?;
115
116 let native: NativeTransactionEffects = bcs::from_bytes(&result.raw_effects)
117 .map_err(|e| Error::Internal(format!("Unable to deserialize transaction effects: {e}")))
118 .extend()?;
119 let tx_data: SenderSignedData = bcs::from_bytes(&result.raw_transaction)
120 .map_err(|e| Error::Internal(format!("Unable to deserialize transaction data: {e}")))
121 .extend()?;
122
123 let events = result
124 .events
125 .ok_or_else(|| {
126 Error::Internal("No events are returned from transaction execution".to_string())
127 })?
128 .data
129 .into_iter()
130 .map(|e| NativeEvent {
131 package_id: e.package_id,
132 transaction_module: e.transaction_module,
133 sender: e.sender,
134 type_: e.type_,
135 contents: e.bcs.into_bytes(),
136 })
137 .collect();
138
139 Ok(ExecutionResult {
140 errors: if result.errors.is_empty() {
141 None
142 } else {
143 Some(result.errors)
144 },
145 effects: TransactionBlockEffects {
146 kind: TransactionBlockEffectsKind::Executed {
147 tx_data,
148 native,
149 events,
150 },
151 checkpoint_viewed_at: u64::MAX,
153 },
154 })
155 }
156}