1use serde::{Deserialize, Serialize};
2
3use celestia_types::hash::Hash;
4
5use crate::Error;
6use crate::grpc::{AsyncGrpcCall, TxPriority};
7
8pub use celestia_proto::cosmos::tx::v1beta1::SignDoc;
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
13pub struct TxInfo {
14 pub hash: Hash,
16 pub height: u64,
18}
19
20#[derive(Debug)]
22pub struct SubmittedTx {
23 broadcasted_tx: BroadcastedTx,
24 confirm_tx: AsyncGrpcCall<TxInfo>,
25}
26
27impl SubmittedTx {
28 pub(crate) fn new(tx: BroadcastedTx, confirm_tx: AsyncGrpcCall<TxInfo>) -> SubmittedTx {
29 SubmittedTx {
30 broadcasted_tx: tx,
31 confirm_tx,
32 }
33 }
34
35 pub fn tx_ref(&self) -> &BroadcastedTx {
37 &self.broadcasted_tx
38 }
39
40 pub async fn confirm(self) -> Result<TxInfo, Error> {
42 self.confirm_tx.await
43 }
44}
45
46#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
48#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
49pub struct BroadcastedTx {
50 pub tx: Vec<u8>,
52 pub hash: Hash,
54 pub sequence: u64,
56}
57
58const DEFAULT_CONFIRMATION_INTERVAL_MS: u64 = 500;
59
60fn default_confirmation_interval_ms() -> u64 {
61 DEFAULT_CONFIRMATION_INTERVAL_MS
62}
63
64#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
66#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
67pub struct TxConfig {
68 pub gas_limit: Option<u64>,
71 pub gas_price: Option<f64>,
74 pub memo: Option<String>,
76 pub priority: TxPriority,
78 #[serde(default = "default_confirmation_interval_ms")]
81 pub confirmation_interval_ms: u64,
82}
83
84impl TxConfig {
85 pub fn with_gas_limit(mut self, gas_limit: u64) -> Self {
87 self.gas_limit = Some(gas_limit);
88 self
89 }
90
91 pub fn with_gas_price(mut self, gas_price: f64) -> Self {
93 self.gas_price = Some(gas_price);
94 self
95 }
96
97 pub fn with_memo(mut self, memo: impl Into<String>) -> Self {
99 self.memo = Some(memo.into());
100 self
101 }
102
103 pub fn with_priority(mut self, priority: TxPriority) -> Self {
105 self.priority = priority;
106 self
107 }
108
109 pub fn with_confirmation_interval_ms(mut self, confirmation_interval_ms: u64) -> Self {
111 self.confirmation_interval_ms = confirmation_interval_ms;
112 self
113 }
114}
115
116impl Default for TxConfig {
117 fn default() -> Self {
118 TxConfig {
119 gas_limit: None,
120 gas_price: None,
121 memo: None,
122 priority: TxPriority::default(),
123 confirmation_interval_ms: DEFAULT_CONFIRMATION_INTERVAL_MS,
124 }
125 }
126}
127
128#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
129pub use wbg::*;
130
131#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
132mod wbg {
133 use super::{BroadcastedTx, TxConfig, TxInfo, TxPriority};
134 use js_sys::{BigInt, Uint8Array};
135 use lumina_utils::make_object;
136 use wasm_bindgen::{JsCast, prelude::*};
137
138 #[wasm_bindgen(typescript_custom_section)]
139 const _: &str = "
140 /**
141 * Transaction info
142 */
143 export interface TxInfo {
144 /**
145 * Hash of the transaction.
146 */
147 hash: string;
148 /**
149 * Height at which transaction was submitted.
150 */
151 height: bigint;
152 }
153
154 /**
155 * A transaction that was broadcasted
156 */
157 export interface BroadcastedTx {
158 /**
159 * Broadcasted bytes
160 */
161 tx: Uint8Array;
162 /**
163 * Transaction hash
164 */
165 hash: string;
166 /**
167 * Transaction sequence
168 */
169 sequence: bigint;
170 }
171
172 /**
173 * Transaction config.
174 */
175 export interface TxConfig {
176 /**
177 * Custom gas limit for the transaction (in `utia`). By default, client will
178 * query gas estimation service to get estimate gas limit.
179 */
180 gasLimit?: bigint; // utia
181 /**
182 * Custom gas price for fee calculation. By default, client will query gas
183 * estimation service to get gas price estimate.
184 */
185 gasPrice?: number;
186 /**
187 * Memo for the transaction
188 */
189 memo?: string;
190 /**
191 * Priority of the transaction, used with gas estimation service
192 */
193 priority?: TxPriority;
194 /**
195 * Interval between confirmation polling attempts, in milliseconds.
196 */
197 confirmationIntervalMs?: bigint;
198 }
199 ";
200
201 #[wasm_bindgen]
202 extern "C" {
203 #[wasm_bindgen(typescript_type = "TxInfo")]
205 pub type JsTxInfo;
206
207 #[wasm_bindgen(typescript_type = "BroadcastedTx")]
209 pub type JsBroadcastedTx;
210
211 #[wasm_bindgen(method, getter, js_name = tx)]
212 pub fn tx(this: &JsBroadcastedTx) -> Vec<u8>;
213
214 #[wasm_bindgen(method, getter, js_name = hash)]
215 pub fn hash(this: &JsBroadcastedTx) -> String;
216
217 #[wasm_bindgen(method, getter, js_name = sequence)]
218 pub fn sequence(this: &JsBroadcastedTx) -> BigInt;
219
220 #[wasm_bindgen(typescript_type = "TxConfig")]
222 pub type JsTxConfig;
223
224 #[wasm_bindgen(method, getter, js_name = gasLimit)]
225 pub fn gas_limit(this: &JsTxConfig) -> Option<u64>;
226
227 #[wasm_bindgen(method, getter, js_name = gasPrice)]
228 pub fn gas_price(this: &JsTxConfig) -> Option<f64>;
229
230 #[wasm_bindgen(method, getter, js_name = memo)]
231 pub fn memo(this: &JsTxConfig) -> Option<String>;
232
233 #[wasm_bindgen(method, getter, js_name = priority)]
234 pub fn priority(this: &JsTxConfig) -> Option<TxPriority>;
235
236 #[wasm_bindgen(method, getter, js_name = confirmationIntervalMs)]
237 pub fn confirmation_interval_ms(this: &JsTxConfig) -> Option<u64>;
238 }
239
240 impl From<TxInfo> for JsTxInfo {
241 fn from(value: TxInfo) -> JsTxInfo {
242 let obj = make_object!(
243 "hash" => value.hash.to_string().into(),
244 "height" => BigInt::from(value.height)
245 );
246
247 obj.unchecked_into()
248 }
249 }
250
251 impl From<BroadcastedTx> for JsBroadcastedTx {
252 fn from(value: BroadcastedTx) -> JsBroadcastedTx {
253 let tx_bytes = Uint8Array::from(value.tx.as_slice());
254 let obj = make_object!(
255 "tx" => tx_bytes.into(),
256 "hash" => value.hash.to_string().into(),
257 "sequence" => BigInt::from(value.sequence)
258 );
259
260 obj.unchecked_into()
261 }
262 }
263
264 impl TryFrom<JsBroadcastedTx> for BroadcastedTx {
265 type Error = crate::Error;
266
267 fn try_from(value: JsBroadcastedTx) -> Result<BroadcastedTx, Self::Error> {
268 Ok(BroadcastedTx {
269 tx: value.tx(),
270 hash: value.hash().parse()?,
271 sequence: value.sequence().try_into().map_err(|i| {
272 crate::Error::InvalidBroadcastedTx(format!("invalid sequence: {i}"))
273 })?,
274 })
275 }
276 }
277
278 impl From<JsTxConfig> for TxConfig {
279 fn from(value: JsTxConfig) -> TxConfig {
280 TxConfig {
281 gas_limit: value.gas_limit(),
282 gas_price: value.gas_price(),
283 memo: value.memo(),
284 priority: value.priority().unwrap_or_default(),
285 confirmation_interval_ms: value
286 .confirmation_interval_ms()
287 .unwrap_or(TxConfig::default().confirmation_interval_ms),
288 }
289 }
290 }
291}
292
293impl From<SubmittedTx> for BroadcastedTx {
294 fn from(value: SubmittedTx) -> Self {
295 value.broadcasted_tx
296 }
297}
298
299impl From<SubmittedTx> for AsyncGrpcCall<TxInfo> {
300 fn from(value: SubmittedTx) -> Self {
301 value.confirm_tx
302 }
303}
304
305impl From<SubmittedTx> for (BroadcastedTx, AsyncGrpcCall<TxInfo>) {
306 fn from(
307 SubmittedTx {
308 broadcasted_tx,
309 confirm_tx,
310 }: SubmittedTx,
311 ) -> Self {
312 (broadcasted_tx, confirm_tx)
313 }
314}