diff --git a/sdk/rust/src/account/account_update_transaction.rs b/sdk/rust/src/account/account_update_transaction.rs index 0a0f51a7..36e522f2 100644 --- a/sdk/rust/src/account/account_update_transaction.rs +++ b/sdk/rust/src/account/account_update_transaction.rs @@ -38,7 +38,7 @@ pub struct AccountUpdateTransactionData { pub auto_renew_period: Option, /// The new expiration time to extend to (ignored if equal to or before the current one). - pub expire_at: Option, + pub expires_at: Option, /// The memo associated with the account. pub memo: Option, @@ -63,6 +63,12 @@ impl AccountUpdateTransaction { self } + /// Sets the new expiration time to extend to (ignored if equal to or before the current one). + pub fn expires_at(&mut self, at: OffsetDateTime) -> &mut Self { + self.body.data.expires_at = Some(at); + self + } + /// Set the key for this account. pub fn key(&mut self, key: impl Into) -> &mut Self { self.body.data.key = Some(key.into()); @@ -126,7 +132,7 @@ impl ToTransactionDataProtobuf for AccountUpdateTransactionData { let account_id = self.account_id.as_ref().map(AccountIdOrAlias::to_protobuf); let key = self.key.as_ref().map(Key::to_protobuf); let auto_renew_period = self.auto_renew_period.as_ref().map(Duration::to_protobuf); - let expiration_time = self.expire_at.as_ref().map(OffsetDateTime::to_protobuf); + let expiration_time = self.expires_at.as_ref().map(OffsetDateTime::to_protobuf); let receiver_signature_required = self.receiver_signature_required.map(|required| { services::crypto_update_transaction_body::ReceiverSigRequiredField::ReceiverSigRequiredWrapper(required) diff --git a/sdk/rust/src/lib.rs b/sdk/rust/src/lib.rs index 67efe848..e67c04a8 100644 --- a/sdk/rust/src/lib.rs +++ b/sdk/rust/src/lib.rs @@ -54,7 +54,9 @@ pub use schedule::ScheduleId; pub use signature::{Signature, SignaturePair}; pub use signer::Signer; pub use token::TokenId; -pub use topic::TopicId; +pub use topic::{ + TopicCreateTransaction, TopicDeleteTransaction, TopicId, TopicMessageSubmitTransaction, TopicUpdateTransaction +}; pub use transaction::Transaction; pub use transaction_hash::TransactionHash; pub use transaction_id::TransactionId; diff --git a/sdk/rust/src/schedule/schedule_id.rs b/sdk/rust/src/schedule/schedule_id.rs index e74ad79e..449f99b1 100644 --- a/sdk/rust/src/schedule/schedule_id.rs +++ b/sdk/rust/src/schedule/schedule_id.rs @@ -15,6 +15,10 @@ impl FromProtobuf for ScheduleId { type Protobuf = services::ScheduleId; fn from_protobuf(pb: Self::Protobuf) -> crate::Result { - Ok(Self { num: pb.schedule_num as u64, shard: pb.shard_num as u64, realm: pb.realm_num as u64 }) + Ok(Self { + num: pb.schedule_num as u64, + shard: pb.shard_num as u64, + realm: pb.realm_num as u64, + }) } } diff --git a/sdk/rust/src/token/token_id.rs b/sdk/rust/src/token/token_id.rs index 1d4cb31c..1fc07f19 100644 --- a/sdk/rust/src/token/token_id.rs +++ b/sdk/rust/src/token/token_id.rs @@ -15,6 +15,10 @@ impl FromProtobuf for TokenId { type Protobuf = services::TokenId; fn from_protobuf(pb: Self::Protobuf) -> crate::Result { - Ok(Self { num: pb.token_num as u64, shard: pb.shard_num as u64, realm: pb.realm_num as u64 }) + Ok(Self { + num: pb.token_num as u64, + shard: pb.shard_num as u64, + realm: pb.realm_num as u64, + }) } } diff --git a/sdk/rust/src/topic/mod.rs b/sdk/rust/src/topic/mod.rs index 76c3a82b..086070e5 100644 --- a/sdk/rust/src/topic/mod.rs +++ b/sdk/rust/src/topic/mod.rs @@ -1,3 +1,15 @@ +mod topic_create_transaction; +mod topic_delete_transaction; mod topic_id; +mod topic_message_submit_transaction; +mod topic_update_transaction; +pub use topic_create_transaction::TopicCreateTransaction; +pub(crate) use topic_create_transaction::TopicCreateTransactionData; +pub use topic_delete_transaction::TopicDeleteTransaction; +pub(crate) use topic_delete_transaction::TopicDeleteTransactionData; pub use topic_id::TopicId; +pub use topic_message_submit_transaction::TopicMessageSubmitTransaction; +pub(crate) use topic_message_submit_transaction::TopicMessageSubmitTransactionData; +pub use topic_update_transaction::TopicUpdateTransaction; +pub(crate) use topic_update_transaction::TopicUpdateTransactionData; diff --git a/sdk/rust/src/topic/topic_create_transaction.rs b/sdk/rust/src/topic/topic_create_transaction.rs new file mode 100644 index 00000000..83d35803 --- /dev/null +++ b/sdk/rust/src/topic/topic_create_transaction.rs @@ -0,0 +1,119 @@ +use async_trait::async_trait; +use hedera_proto::services; +use hedera_proto::services::consensus_service_client::ConsensusServiceClient; +use serde_with::skip_serializing_none; +use time::Duration; +use tonic::transport::Channel; + +use crate::protobuf::ToProtobuf; +use crate::transaction::{AnyTransactionData, ToTransactionDataProtobuf, TransactionExecute}; +use crate::{AccountId, Key, Transaction, TransactionId}; + +/// Create a topic to be used for consensus. +/// +/// If an `auto_renew_account` is specified, that account must also sign this transaction. +/// +/// If an `admin_key` is specified, the adminKey must sign the transaction. +/// +/// On success, the resulting `TransactionReceipt` contains the newly created `TopicId`. +/// +pub type TopicCreateTransaction = Transaction; + +#[skip_serializing_none] +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TopicCreateTransactionData { + /// Short publicly visible memo about the topic. No guarantee of uniqueness. + #[serde(skip_serializing_if = "String::is_empty")] + topic_memo: String, + + /// Access control for `TopicUpdateTransaction` and `TopicDeleteTransaction`. + admin_key: Option, + + /// Access control for `TopicMessageSubmitTransaction`. + submit_key: Option, + + /// The initial lifetime of the topic and the amount of time to attempt to + /// extend the topic's lifetime by automatically at the topic's expiration time, if + /// the `auto_renew_account_id` is configured. + auto_renew_period: Option, + + /// Optional account to be used at the topic's expiration time to extend the life of the topic. + auto_renew_account_id: Option, +} + +impl TopicCreateTransaction { + /// Sets the short publicly visible memo about the topic. + /// + /// No guarantee of uniqueness. + /// + pub fn topic_memo(&mut self, memo: impl Into) -> &mut Self { + self.body.data.topic_memo = memo.into(); + self + } + + /// Sets the access control for `TopicUpdateTransaction` and `TopicDeleteTransaction`. + pub fn admin_key(&mut self, key: impl Into) -> &mut Self { + self.body.data.admin_key = Some(key.into()); + self + } + + /// Sets the access control for `TopicMessageSubmitTransaction`. + pub fn submit_key(&mut self, key: impl Into) -> &mut Self { + self.body.data.submit_key = Some(key.into()); + self + } + + /// Sets the initial lifetime of the topic and the amount of time to attempt to + /// extend the topic's lifetime by automatically at the topic's expiration time. + pub fn auto_renew_period(&mut self, period: Duration) -> &mut Self { + self.body.data.auto_renew_period = Some(period); + self + } + + /// Sets the account to be used at the topic's expiration time to extend the life of the topic. + pub fn auto_renew_account_id(&mut self, id: impl Into) -> &mut Self { + self.body.data.auto_renew_account_id = Some(id.into()); + self + } +} + +#[async_trait] +impl TransactionExecute for TopicCreateTransactionData { + async fn execute( + &self, + channel: Channel, + request: services::Transaction, + ) -> Result, tonic::Status> { + ConsensusServiceClient::new(channel).create_topic(request).await + } +} + +impl ToTransactionDataProtobuf for TopicCreateTransactionData { + fn to_transaction_data_protobuf( + &self, + _node_account_id: AccountId, + _transaction_id: &TransactionId, + ) -> services::transaction_body::Data { + let admin_key = self.admin_key.as_ref().map(Key::to_protobuf); + let submit_key = self.submit_key.as_ref().map(Key::to_protobuf); + let auto_renew_period = self.auto_renew_period.as_ref().map(Duration::to_protobuf); + let auto_renew_account_id = self.auto_renew_account_id.as_ref().map(AccountId::to_protobuf); + + services::transaction_body::Data::ConsensusCreateTopic( + services::ConsensusCreateTopicTransactionBody { + auto_renew_account: auto_renew_account_id, + memo: self.topic_memo.clone(), + admin_key, + submit_key, + auto_renew_period, + }, + ) + } +} + +impl From for AnyTransactionData { + fn from(transaction: TopicCreateTransactionData) -> Self { + Self::TopicCreate(transaction) + } +} diff --git a/sdk/rust/src/topic/topic_delete_transaction.rs b/sdk/rust/src/topic/topic_delete_transaction.rs new file mode 100644 index 00000000..2aa90464 --- /dev/null +++ b/sdk/rust/src/topic/topic_delete_transaction.rs @@ -0,0 +1,65 @@ +use async_trait::async_trait; +use hedera_proto::services; +use hedera_proto::services::consensus_service_client::ConsensusServiceClient; +use serde_with::skip_serializing_none; +use tonic::transport::Channel; + +use crate::protobuf::ToProtobuf; +use crate::transaction::{AnyTransactionData, ToTransactionDataProtobuf, TransactionExecute}; +use crate::{AccountId, TopicId, Transaction, TransactionId}; + +/// Delete a topic. +/// +/// No more transactions or queries on the topic will succeed. +/// +/// If an `admin_key` is set, this transaction must be signed by that key. +/// If there is no `admin_key`, this transaction will fail `UNAUTHORIZED`. +/// +pub type TopicDeleteTransaction = Transaction; + +#[skip_serializing_none] +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TopicDeleteTransactionData { + /// The topic ID which is being deleted in this transaction. + topic_id: Option, +} + +impl TopicDeleteTransaction { + /// Set the account ID which is being deleted. + pub fn topic_id(&mut self, id: impl Into) -> &mut Self { + self.body.data.topic_id = Some(id.into()); + self + } +} + +#[async_trait] +impl TransactionExecute for TopicDeleteTransactionData { + async fn execute( + &self, + channel: Channel, + request: services::Transaction, + ) -> Result, tonic::Status> { + ConsensusServiceClient::new(channel).delete_topic(request).await + } +} + +impl ToTransactionDataProtobuf for TopicDeleteTransactionData { + fn to_transaction_data_protobuf( + &self, + _node_account_id: AccountId, + _transaction_id: &TransactionId, + ) -> services::transaction_body::Data { + let topic_id = self.topic_id.as_ref().map(TopicId::to_protobuf); + + services::transaction_body::Data::ConsensusDeleteTopic( + services::ConsensusDeleteTopicTransactionBody { topic_id }, + ) + } +} + +impl From for AnyTransactionData { + fn from(transaction: TopicDeleteTransactionData) -> Self { + Self::TopicDelete(transaction) + } +} diff --git a/sdk/rust/src/topic/topic_id.rs b/sdk/rust/src/topic/topic_id.rs index cea75758..3ae7556f 100644 --- a/sdk/rust/src/topic/topic_id.rs +++ b/sdk/rust/src/topic/topic_id.rs @@ -1,6 +1,6 @@ use hedera_proto::services; -use crate::FromProtobuf; +use crate::{FromProtobuf, ToProtobuf}; /// The unique identifier for a topic on Hedera. #[derive(Debug, serde::Serialize, serde::Deserialize, Hash, PartialEq, Eq, Clone, Copy)] @@ -15,6 +15,22 @@ impl FromProtobuf for TopicId { type Protobuf = services::TopicId; fn from_protobuf(pb: Self::Protobuf) -> crate::Result { - Ok(Self { num: pb.topic_num as u64, shard: pb.shard_num as u64, realm: pb.realm_num as u64 }) + Ok(Self { + num: pb.topic_num as u64, + shard: pb.shard_num as u64, + realm: pb.realm_num as u64, + }) + } +} + +impl ToProtobuf for TopicId { + type Protobuf = services::TopicId; + + fn to_protobuf(&self) -> Self::Protobuf { + services::TopicId { + topic_num: self.num as i64, + realm_num: self.realm as i64, + shard_num: self.shard as i64, + } } } diff --git a/sdk/rust/src/topic/topic_message_submit_transaction.rs b/sdk/rust/src/topic/topic_message_submit_transaction.rs new file mode 100644 index 00000000..0e4d7455 --- /dev/null +++ b/sdk/rust/src/topic/topic_message_submit_transaction.rs @@ -0,0 +1,115 @@ +use async_trait::async_trait; +use hedera_proto::services; +use hedera_proto::services::consensus_service_client::ConsensusServiceClient; +use serde_with::skip_serializing_none; +use tonic::transport::Channel; + +use crate::protobuf::ToProtobuf; +use crate::transaction::{AnyTransactionData, ToTransactionDataProtobuf, TransactionExecute}; +use crate::{AccountId, TopicId, Transaction, TransactionId}; + +pub type TopicMessageSubmitTransaction = Transaction; + +#[skip_serializing_none] +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TopicMessageSubmitTransactionData { + /// The topic ID to submit this message to. + topic_id: Option, + + /// Message to be submitted. + /// Max size of the Transaction (including signatures) is 6KiB. + message: Option>, + + /// The `TransactionId` of the first chunk. + /// + /// Should get copied to every subsequent chunk in a fragmented message. + initial_transaction_id: Option, + + /// The total number of chunks in the message. + /// Defaults to 1. + chunk_total: i32, + + /// The sequence number (from 1 to total) of the current chunk in the message. + /// Defaults to 1. + chunk_number: i32, +} + +impl TopicMessageSubmitTransaction { + /// Set the account ID which is being deleted. + pub fn topic_id(&mut self, id: impl Into) -> &mut Self { + self.body.data.topic_id = Some(id.into()); + self + } + + /// Sets the message to be submitted. + pub fn message(&mut self, bytes: Vec) -> &mut Self { + self.body.data.message = Some(bytes); + self + } + + /// Sets the `TransactionId` of the first chunk. + pub fn initial_transaction_id(&mut self, id: impl Into) -> &mut Self { + self.body.data.initial_transaction_id = Some(id.into()); + self + } + + /// Sets the total number of chunks in the message. + pub fn chunk_total(&mut self, total: u32) -> &mut Self { + self.body.data.chunk_total = total as i32; + self + } + + /// Sets the sequence number (from 1 to total) of the current chunk in the message. + pub fn chunk_number(&mut self, number: u32) -> &mut Self { + self.body.data.chunk_number = number as i32; + self + } +} + +#[async_trait] +impl TransactionExecute for TopicMessageSubmitTransactionData { + async fn execute( + &self, + channel: Channel, + request: services::Transaction, + ) -> Result, tonic::Status> { + ConsensusServiceClient::new(channel).submit_message(request).await + } +} + +impl ToTransactionDataProtobuf for TopicMessageSubmitTransactionData { + fn to_transaction_data_protobuf( + &self, + _node_account_id: AccountId, + _transaction_id: &TransactionId, + ) -> services::transaction_body::Data { + let topic_id = self.topic_id.as_ref().map(TopicId::to_protobuf); + + let chunk_info = if let Some(initial_id) = &self.initial_transaction_id { + let initial_id = initial_id.to_protobuf(); + + Some(services::ConsensusMessageChunkInfo { + initial_transaction_id: Some(initial_id), + number: self.chunk_number, + total: self.chunk_total, + }) + } else { + None + }; + + services::transaction_body::Data::ConsensusSubmitMessage( + services::ConsensusSubmitMessageTransactionBody { + topic_id, + message: self.message.clone().unwrap_or_default(), + chunk_info, + }, + ) + } +} + +impl From for AnyTransactionData { + fn from(transaction: TopicMessageSubmitTransactionData) -> Self { + Self::TopicMessageSubmit(transaction) + } +} diff --git a/sdk/rust/src/topic/topic_update_transaction.rs b/sdk/rust/src/topic/topic_update_transaction.rs new file mode 100644 index 00000000..5e78d4a4 --- /dev/null +++ b/sdk/rust/src/topic/topic_update_transaction.rs @@ -0,0 +1,136 @@ +use async_trait::async_trait; +use hedera_proto::services; +use hedera_proto::services::consensus_service_client::ConsensusServiceClient; +use serde_with::skip_serializing_none; +use time::{Duration, OffsetDateTime}; +use tonic::transport::Channel; + +use crate::protobuf::ToProtobuf; +use crate::transaction::{AnyTransactionData, ToTransactionDataProtobuf, TransactionExecute}; +use crate::{AccountId, Key, TopicId, Transaction, TransactionId}; + +/// Change properties for the given topic. +/// +/// Any null field is ignored (left unchanged). +/// +pub type TopicUpdateTransaction = Transaction; + +#[skip_serializing_none] +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TopicUpdateTransactionData { + /// The topic ID which is being updated in this transaction. + topic_id: Option, + + /// The new expiration time to extend to (ignored if equal to or before the current one). + expires_at: Option, + + /// Short publicly visible memo about the topic. No guarantee of uniqueness. + topic_memo: Option, + + /// Access control for `TopicUpdateTransaction` and `TopicDeleteTransaction`. + admin_key: Option, + + /// Access control for `TopicMessageSubmitTransaction`. + submit_key: Option, + + /// The initial lifetime of the topic and the amount of time to attempt to + /// extend the topic's lifetime by automatically at the topic's expiration time, if + /// the `auto_renew_account_id` is configured. + auto_renew_period: Option, + + /// Optional account to be used at the topic's expiration time to extend the life of the topic. + auto_renew_account_id: Option, +} + +impl TopicUpdateTransaction { + /// Set the account ID which is being updated. + pub fn topic_id(&mut self, id: impl Into) -> &mut Self { + self.body.data.topic_id = Some(id.into()); + self + } + + /// Sets the new expiration time to extend to (ignored if equal to or before the current one). + pub fn expires_at(&mut self, at: OffsetDateTime) -> &mut Self { + self.body.data.expires_at = Some(at); + self + } + + /// Sets the short publicly visible memo about the topic. + /// + /// No guarantee of uniqueness. + /// + pub fn topic_memo(&mut self, memo: impl Into) -> &mut Self { + self.body.data.topic_memo = Some(memo.into()); + self + } + + /// Sets the access control for `TopicUpdateTransaction` and `TopicDeleteTransaction`. + pub fn admin_key(&mut self, key: impl Into) -> &mut Self { + self.body.data.admin_key = Some(key.into()); + self + } + + /// Sets the access control for `TopicMessageSubmitTransaction`. + pub fn submit_key(&mut self, key: impl Into) -> &mut Self { + self.body.data.submit_key = Some(key.into()); + self + } + + /// Sets the initial lifetime of the topic and the amount of time to attempt to + /// extend the topic's lifetime by automatically at the topic's expiration time. + pub fn auto_renew_period(&mut self, period: Duration) -> &mut Self { + self.body.data.auto_renew_period = Some(period); + self + } + + /// Sets the account to be used at the topic's expiration time to extend the life of the topic. + pub fn auto_renew_account_id(&mut self, id: impl Into) -> &mut Self { + self.body.data.auto_renew_account_id = Some(id.into()); + self + } +} + +#[async_trait] +impl TransactionExecute for TopicUpdateTransactionData { + async fn execute( + &self, + channel: Channel, + request: services::Transaction, + ) -> Result, tonic::Status> { + ConsensusServiceClient::new(channel).update_topic(request).await + } +} + +impl ToTransactionDataProtobuf for TopicUpdateTransactionData { + fn to_transaction_data_protobuf( + &self, + _node_account_id: AccountId, + _transaction_id: &TransactionId, + ) -> services::transaction_body::Data { + let topic_id = self.topic_id.as_ref().map(TopicId::to_protobuf); + let expiration_time = self.expires_at.as_ref().map(OffsetDateTime::to_protobuf); + let admin_key = self.admin_key.as_ref().map(Key::to_protobuf); + let submit_key = self.submit_key.as_ref().map(Key::to_protobuf); + let auto_renew_period = self.auto_renew_period.as_ref().map(Duration::to_protobuf); + let auto_renew_account_id = self.auto_renew_account_id.as_ref().map(AccountId::to_protobuf); + + services::transaction_body::Data::ConsensusUpdateTopic( + services::ConsensusUpdateTopicTransactionBody { + auto_renew_account: auto_renew_account_id, + memo: self.topic_memo.clone(), + expiration_time, + topic_id, + admin_key, + submit_key, + auto_renew_period, + }, + ) + } +} + +impl From for AnyTransactionData { + fn from(transaction: TopicUpdateTransactionData) -> Self { + Self::TopicUpdate(transaction) + } +} diff --git a/sdk/rust/src/transaction/any.rs b/sdk/rust/src/transaction/any.rs index 0740aa72..5edbbf74 100644 --- a/sdk/rust/src/transaction/any.rs +++ b/sdk/rust/src/transaction/any.rs @@ -9,6 +9,9 @@ use tonic::{Response, Status}; use crate::account::{ AccountCreateTransactionData, AccountDeleteTransactionData, AccountUpdateTransactionData }; +use crate::topic::{ + TopicCreateTransactionData, TopicDeleteTransactionData, TopicMessageSubmitTransactionData, TopicUpdateTransactionData +}; use crate::transaction::{ToTransactionDataProtobuf, TransactionBody, TransactionExecute}; use crate::transfer_transaction::TransferTransactionData; use crate::{AccountId, Transaction, TransactionId}; @@ -23,6 +26,10 @@ pub enum AnyTransactionData { AccountUpdate(AccountUpdateTransactionData), AccountDelete(AccountDeleteTransactionData), Transfer(TransferTransactionData), + TopicCreate(TopicCreateTransactionData), + TopicUpdate(TopicUpdateTransactionData), + TopicDelete(TopicDeleteTransactionData), + TopicMessageSubmit(TopicMessageSubmitTransactionData), } impl ToTransactionDataProtobuf for AnyTransactionData { @@ -47,6 +54,22 @@ impl ToTransactionDataProtobuf for AnyTransactionData { Self::AccountDelete(transaction) => { transaction.to_transaction_data_protobuf(node_account_id, transaction_id) } + + Self::TopicCreate(transaction) => { + transaction.to_transaction_data_protobuf(node_account_id, transaction_id) + } + + Self::TopicUpdate(transaction) => { + transaction.to_transaction_data_protobuf(node_account_id, transaction_id) + } + + Self::TopicDelete(transaction) => { + transaction.to_transaction_data_protobuf(node_account_id, transaction_id) + } + + Self::TopicMessageSubmit(transaction) => { + transaction.to_transaction_data_protobuf(node_account_id, transaction_id) + } } } } @@ -59,6 +82,10 @@ impl TransactionExecute for AnyTransactionData { Self::AccountCreate(transaction) => transaction.default_max_transaction_fee(), Self::AccountUpdate(transaction) => transaction.default_max_transaction_fee(), Self::AccountDelete(transaction) => transaction.default_max_transaction_fee(), + Self::TopicCreate(transaction) => transaction.default_max_transaction_fee(), + Self::TopicUpdate(transaction) => transaction.default_max_transaction_fee(), + Self::TopicDelete(transaction) => transaction.default_max_transaction_fee(), + Self::TopicMessageSubmit(transaction) => transaction.default_max_transaction_fee(), } } @@ -68,10 +95,14 @@ impl TransactionExecute for AnyTransactionData { request: services::Transaction, ) -> Result, Status> { match self { + Self::Transfer(transaction) => transaction.execute(channel, request).await, Self::AccountCreate(transaction) => transaction.execute(channel, request).await, Self::AccountUpdate(transaction) => transaction.execute(channel, request).await, Self::AccountDelete(transaction) => transaction.execute(channel, request).await, - Self::Transfer(transaction) => transaction.execute(channel, request).await, + Self::TopicCreate(transaction) => transaction.execute(channel, request).await, + Self::TopicUpdate(transaction) => transaction.execute(channel, request).await, + Self::TopicDelete(transaction) => transaction.execute(channel, request).await, + Self::TopicMessageSubmit(transaction) => transaction.execute(channel, request).await, } } }