1use std::{fmt::Debug, str::FromStr};
6
7use axum::{
8 Json,
9 response::{IntoResponse, Response},
10};
11use fastcrypto::encoding::Hex;
12use iota_sdk::rpc_types::{IotaExecutionStatus, IotaTransactionBlockKind};
13use iota_types::{
14 IOTA_SYSTEM_PACKAGE_ID,
15 base_types::{IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest},
16 crypto::{PublicKey as IotaPublicKey, SignatureScheme},
17 governance::{ADD_STAKE_FUN_NAME, WITHDRAW_STAKE_FUN_NAME},
18 iota_system_state::IOTA_SYSTEM_MODULE_NAME,
19 messages_checkpoint::CheckpointDigest,
20 programmable_transaction_builder::ProgrammableTransactionBuilder,
21 transaction::{Argument, CallArg, Command, ObjectArg, TransactionData},
22};
23use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeError};
24use serde_json::Value;
25use strum_macros::{EnumIter, EnumString};
26
27use crate::{
28 IOTA,
29 errors::{Error, ErrorType},
30 operations::Operations,
31};
32
33pub type BlockHeight = u64;
34
35#[derive(Serialize, Deserialize, Clone, Debug)]
36pub struct NetworkIdentifier {
37 pub blockchain: String,
38 pub network: IotaEnv,
39}
40
41#[derive(
42 Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug, Clone, Copy, EnumString,
43)]
44#[strum(serialize_all = "lowercase")]
45#[serde(rename_all = "lowercase")]
46pub enum IotaEnv {
47 MainNet,
48 DevNet,
49 TestNet,
50 LocalNet,
51}
52
53impl IotaEnv {
54 pub fn check_network_identifier(
55 &self,
56 network_identifier: &NetworkIdentifier,
57 ) -> Result<(), Error> {
58 if &network_identifier.blockchain != "iota" {
59 return Err(Error::UnsupportedBlockchain(
60 network_identifier.blockchain.clone(),
61 ));
62 }
63 if &network_identifier.network != self {
64 return Err(Error::UnsupportedNetwork(network_identifier.network));
65 }
66 Ok(())
67 }
68}
69
70#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
71pub struct AccountIdentifier {
72 pub address: IotaAddress,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
74 pub sub_account: Option<SubAccount>,
75}
76
77#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
78pub struct SubAccount {
79 #[serde(rename = "address")]
80 pub account_type: SubAccountType,
81}
82
83#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
84pub enum SubAccountType {
85 Stake,
86 PendingStake,
87 EstimatedReward,
88}
89
90impl From<IotaAddress> for AccountIdentifier {
91 fn from(address: IotaAddress) -> Self {
92 AccountIdentifier {
93 address,
94 sub_account: None,
95 }
96 }
97}
98
99#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
100pub struct Currency {
101 pub symbol: String,
102 pub decimals: u64,
103}
104#[derive(Serialize, Deserialize)]
105pub struct AccountBalanceRequest {
106 pub network_identifier: NetworkIdentifier,
107 pub account_identifier: AccountIdentifier,
108 #[serde(default)]
109 pub block_identifier: PartialBlockIdentifier,
110 #[serde(default, skip_serializing_if = "Vec::is_empty")]
111 pub currencies: Vec<Currency>,
112}
113#[derive(Serialize, Deserialize, Debug)]
114pub struct AccountBalanceResponse {
115 pub block_identifier: BlockIdentifier,
116 pub balances: Vec<Amount>,
117}
118
119impl IntoResponse for AccountBalanceResponse {
120 fn into_response(self) -> Response {
121 Json(self).into_response()
122 }
123}
124
125#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
126pub struct BlockIdentifier {
127 pub index: BlockHeight,
128 pub hash: BlockHash,
129}
130
131pub type BlockHash = CheckpointDigest;
132
133#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
134pub struct Amount {
135 #[serde(with = "str_format")]
136 pub value: i128,
137 pub currency: Currency,
138 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub metadata: Option<AmountMetadata>,
140}
141
142#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
143pub struct AmountMetadata {
144 pub sub_balances: Vec<SubBalance>,
145}
146
147#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
148pub struct SubBalance {
149 pub stake_id: ObjectID,
150 pub validator: IotaAddress,
151 #[serde(with = "str_format")]
152 pub value: i128,
153}
154
155impl Amount {
156 pub fn new(value: i128) -> Self {
157 Self {
158 value,
159 currency: IOTA.clone(),
160 metadata: None,
161 }
162 }
163 pub fn new_from_sub_balances(sub_balances: Vec<SubBalance>) -> Self {
164 let value = sub_balances.iter().map(|b| b.value).sum();
165
166 Self {
167 value,
168 currency: IOTA.clone(),
169 metadata: Some(AmountMetadata { sub_balances }),
170 }
171 }
172}
173
174mod str_format {
175 use std::str::FromStr;
176
177 use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
178
179 pub fn serialize<S>(value: &i128, serializer: S) -> Result<S::Ok, S::Error>
180 where
181 S: Serializer,
182 {
183 value.to_string().serialize(serializer)
184 }
185
186 pub fn deserialize<'de, D>(deserializer: D) -> Result<i128, D::Error>
187 where
188 D: Deserializer<'de>,
189 {
190 let s = String::deserialize(deserializer)?;
191 i128::from_str(&s).map_err(Error::custom)
192 }
193}
194
195#[derive(Deserialize)]
196pub struct AccountCoinsRequest {
197 pub network_identifier: NetworkIdentifier,
198 pub account_identifier: AccountIdentifier,
199 pub include_mempool: bool,
200}
201#[derive(Serialize)]
202pub struct AccountCoinsResponse {
203 pub block_identifier: BlockIdentifier,
204 pub coins: Vec<Coin>,
205}
206impl IntoResponse for AccountCoinsResponse {
207 fn into_response(self) -> Response {
208 Json(self).into_response()
209 }
210}
211#[derive(Serialize)]
212pub struct Coin {
213 pub coin_identifier: CoinIdentifier,
214 pub amount: Amount,
215}
216
217impl From<iota_sdk::rpc_types::Coin> for Coin {
218 fn from(coin: iota_sdk::rpc_types::Coin) -> Self {
219 Self {
220 coin_identifier: CoinIdentifier {
221 identifier: CoinID {
222 id: coin.coin_object_id,
223 version: coin.version,
224 },
225 },
226 amount: Amount {
227 value: coin.balance as i128,
228 currency: IOTA.clone(),
229 metadata: None,
230 },
231 }
232 }
233}
234
235#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
236pub struct CoinIdentifier {
237 pub identifier: CoinID,
238}
239
240#[derive(Clone, Debug, Eq, PartialEq)]
241pub struct CoinID {
242 pub id: ObjectID,
243 pub version: SequenceNumber,
244}
245
246impl Serialize for CoinID {
247 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
248 where
249 S: Serializer,
250 {
251 format!("{}:{}", self.id, self.version.value()).serialize(serializer)
252 }
253}
254
255impl<'de> Deserialize<'de> for CoinID {
256 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
257 where
258 D: Deserializer<'de>,
259 {
260 let s = String::deserialize(deserializer)?;
261
262 let (id, version) = s.split_at(
263 s.find(':')
264 .ok_or_else(|| D::Error::custom(format!("Malformed Coin id [{s}].")))?,
265 );
266 let version = version.trim_start_matches(':');
267 let id = ObjectID::from_hex_literal(id).map_err(D::Error::custom)?;
268 let version = SequenceNumber::from_u64(u64::from_str(version).map_err(D::Error::custom)?);
269
270 Ok(Self { id, version })
271 }
272}
273
274#[test]
275fn test_coin_id_serde() {
276 let id = ObjectID::random();
277 let coin_id = CoinID {
278 id,
279 version: SequenceNumber::from_u64(10),
280 };
281 let s = serde_json::to_string(&coin_id).unwrap();
282 assert_eq!(format!("\"{}:{}\"", id, 10), s);
283
284 let deserialized: CoinID = serde_json::from_str(&s).unwrap();
285
286 assert_eq!(id, deserialized.id);
287 assert_eq!(SequenceNumber::from_u64(10), deserialized.version)
288}
289
290impl From<ObjectRef> for CoinID {
291 fn from((id, version, _): ObjectRef) -> Self {
292 Self { id, version }
293 }
294}
295
296#[derive(Deserialize)]
297pub struct MetadataRequest {
298 #[serde(default)]
299 pub metadata: Option<Value>,
300}
301
302#[derive(Serialize)]
303pub struct NetworkListResponse {
304 pub network_identifiers: Vec<NetworkIdentifier>,
305}
306
307impl IntoResponse for NetworkListResponse {
308 fn into_response(self) -> Response {
309 Json(self).into_response()
310 }
311}
312
313#[derive(Deserialize)]
314pub struct ConstructionDeriveRequest {
315 pub network_identifier: NetworkIdentifier,
316 pub public_key: PublicKey,
317}
318
319#[derive(Serialize, Deserialize, Debug)]
320pub struct PublicKey {
321 pub hex_bytes: Hex,
322 pub curve_type: CurveType,
323}
324
325impl From<IotaPublicKey> for PublicKey {
326 fn from(pk: IotaPublicKey) -> Self {
327 match pk {
328 IotaPublicKey::Ed25519(k) => PublicKey {
329 hex_bytes: Hex::from_bytes(&k.0),
330 curve_type: CurveType::Edwards25519,
331 },
332 IotaPublicKey::Secp256k1(k) => PublicKey {
333 hex_bytes: Hex::from_bytes(&k.0),
334 curve_type: CurveType::Secp256k1,
335 },
336 IotaPublicKey::Secp256r1(k) => PublicKey {
337 hex_bytes: Hex::from_bytes(&k.0),
338 curve_type: CurveType::Secp256r1,
339 },
340 IotaPublicKey::ZkLogin(k) => PublicKey {
341 hex_bytes: Hex::from_bytes(&k.0),
342 curve_type: CurveType::ZkLogin, },
344 IotaPublicKey::Passkey(k) => PublicKey {
345 hex_bytes: Hex::from_bytes(&k.0),
346 curve_type: CurveType::Secp256r1,
347 },
348 }
349 }
350}
351
352impl TryInto<IotaAddress> for PublicKey {
353 type Error = Error;
354
355 fn try_into(self) -> Result<IotaAddress, Self::Error> {
356 let key_bytes = self.hex_bytes.to_vec()?;
357 let pub_key = IotaPublicKey::try_from_bytes(self.curve_type.into(), &key_bytes)?;
358 Ok((&pub_key).into())
359 }
360}
361
362#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
363#[serde(rename_all = "lowercase")]
364pub enum CurveType {
365 Secp256k1,
366 Edwards25519,
367 Secp256r1,
368 ZkLogin,
369}
370
371impl From<CurveType> for SignatureScheme {
372 fn from(type_: CurveType) -> Self {
373 match type_ {
374 CurveType::Secp256k1 => SignatureScheme::Secp256k1,
375 CurveType::Edwards25519 => SignatureScheme::ED25519,
376 CurveType::Secp256r1 => SignatureScheme::Secp256r1,
377 CurveType::ZkLogin => SignatureScheme::ZkLoginAuthenticator,
378 }
379 }
380}
381
382#[derive(Serialize)]
383pub struct ConstructionDeriveResponse {
384 pub account_identifier: AccountIdentifier,
385}
386
387impl IntoResponse for ConstructionDeriveResponse {
388 fn into_response(self) -> Response {
389 Json(self).into_response()
390 }
391}
392
393#[derive(Serialize, Deserialize)]
394pub struct ConstructionPayloadsRequest {
395 pub network_identifier: NetworkIdentifier,
396 pub operations: Operations,
397 #[serde(default, skip_serializing_if = "Option::is_none")]
398 pub metadata: Option<ConstructionMetadata>,
399 #[serde(default, skip_serializing_if = "Vec::is_empty")]
400 pub public_keys: Vec<PublicKey>,
401}
402
403#[derive(Deserialize, Serialize, Copy, Clone, Debug, EnumIter, Eq, PartialEq)]
404pub enum OperationType {
405 Gas,
407 IotaBalanceChange,
408 StakeReward,
409 StakePrinciple,
410 PayIota,
412 Stake,
413 WithdrawStake,
414 EpochChange,
416 Genesis,
417 ConsensusCommitPrologue,
418 ProgrammableTransaction,
419 AuthenticatorStateUpdateV1,
420 RandomnessStateUpdate,
421 EndOfEpochTransaction,
422}
423
424impl From<&IotaTransactionBlockKind> for OperationType {
425 fn from(tx: &IotaTransactionBlockKind) -> Self {
426 match tx {
427 IotaTransactionBlockKind::Genesis(_) => OperationType::Genesis,
428 IotaTransactionBlockKind::ConsensusCommitPrologueV1(_) => {
429 OperationType::ConsensusCommitPrologue
430 }
431 IotaTransactionBlockKind::ProgrammableTransaction(_) => {
432 OperationType::ProgrammableTransaction
433 }
434 IotaTransactionBlockKind::AuthenticatorStateUpdateV1(_) => {
435 OperationType::AuthenticatorStateUpdateV1
436 }
437 IotaTransactionBlockKind::RandomnessStateUpdate(_) => {
438 OperationType::RandomnessStateUpdate
439 }
440 IotaTransactionBlockKind::EndOfEpochTransaction(_) => {
441 OperationType::EndOfEpochTransaction
442 }
443 }
444 }
445}
446
447#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
448pub struct OperationIdentifier {
449 index: u64,
450 #[serde(default, skip_serializing_if = "Option::is_none")]
451 network_index: Option<u64>,
452}
453
454impl From<u64> for OperationIdentifier {
455 fn from(index: u64) -> Self {
456 OperationIdentifier {
457 index,
458 network_index: None,
459 }
460 }
461}
462
463#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)]
464pub struct CoinChange {
465 pub coin_identifier: CoinIdentifier,
466 pub coin_action: CoinAction,
467}
468
469#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)]
470#[serde(rename_all = "snake_case")]
471pub enum CoinAction {
472 CoinCreated,
473 CoinSpent,
474}
475
476#[derive(Serialize, Deserialize, Debug)]
477pub struct ConstructionPayloadsResponse {
478 pub unsigned_transaction: Hex,
479 pub payloads: Vec<SigningPayload>,
480}
481
482impl IntoResponse for ConstructionPayloadsResponse {
483 fn into_response(self) -> Response {
484 Json(self).into_response()
485 }
486}
487
488#[derive(Serialize, Deserialize, Debug, Clone)]
489pub struct SigningPayload {
490 pub account_identifier: AccountIdentifier,
491 pub hex_bytes: String,
493 #[serde(default, skip_serializing_if = "Option::is_none")]
494 pub signature_type: Option<SignatureType>,
495}
496
497#[derive(Deserialize, Serialize, Debug, Clone)]
498#[serde(rename_all = "lowercase")]
499pub enum SignatureType {
500 Ed25519,
501 Ecdsa,
502}
503
504#[derive(Deserialize, Serialize)]
505pub struct ConstructionCombineRequest {
506 pub network_identifier: NetworkIdentifier,
507 pub unsigned_transaction: Hex,
508 pub signatures: Vec<Signature>,
509}
510
511#[derive(Deserialize, Serialize)]
512pub struct Signature {
513 pub signing_payload: SigningPayload,
514 pub public_key: PublicKey,
515 pub signature_type: SignatureType,
516 pub hex_bytes: Hex,
517}
518
519#[derive(Serialize, Deserialize, Debug)]
520pub struct ConstructionCombineResponse {
521 pub signed_transaction: Hex,
522}
523
524impl IntoResponse for ConstructionCombineResponse {
525 fn into_response(self) -> Response {
526 Json(self).into_response()
527 }
528}
529
530#[derive(Serialize, Deserialize)]
531pub struct ConstructionSubmitRequest {
532 pub network_identifier: NetworkIdentifier,
533 pub signed_transaction: Hex,
534}
535#[derive(Serialize, Deserialize, Debug)]
536pub struct TransactionIdentifierResponse {
537 pub transaction_identifier: TransactionIdentifier,
538 #[serde(default, skip_serializing_if = "Option::is_none")]
539 pub metadata: Option<Value>,
540}
541
542impl IntoResponse for TransactionIdentifierResponse {
543 fn into_response(self) -> Response {
544 Json(self).into_response()
545 }
546}
547
548#[derive(Serialize, Deserialize, Clone, Debug)]
549pub struct TransactionIdentifier {
550 pub hash: TransactionDigest,
551}
552
553#[derive(Serialize, Deserialize)]
554pub struct ConstructionPreprocessRequest {
555 pub network_identifier: NetworkIdentifier,
556 pub operations: Operations,
557 #[serde(default, skip_serializing_if = "Option::is_none")]
558 pub metadata: Option<PreprocessMetadata>,
559}
560
561#[derive(Serialize, Deserialize)]
562pub struct PreprocessMetadata {
563 #[serde(default, skip_serializing_if = "Option::is_none")]
564 pub budget: Option<u64>,
565}
566
567#[derive(Serialize, Deserialize, Debug)]
568pub struct ConstructionPreprocessResponse {
569 #[serde(default, skip_serializing_if = "Option::is_none")]
570 pub options: Option<MetadataOptions>,
571 #[serde(default, skip_serializing_if = "Vec::is_empty")]
572 pub required_public_keys: Vec<AccountIdentifier>,
573}
574
575#[derive(Serialize, Deserialize, Debug)]
576pub struct MetadataOptions {
577 pub internal_operation: InternalOperation,
578 #[serde(default, skip_serializing_if = "Option::is_none")]
579 pub budget: Option<u64>,
580}
581
582impl IntoResponse for ConstructionPreprocessResponse {
583 fn into_response(self) -> Response {
584 Json(self).into_response()
585 }
586}
587#[derive(Deserialize)]
588pub struct ConstructionHashRequest {
589 pub network_identifier: NetworkIdentifier,
590 pub signed_transaction: Hex,
591}
592
593#[derive(Serialize, Deserialize)]
594pub struct ConstructionMetadataRequest {
595 pub network_identifier: NetworkIdentifier,
596 #[serde(default, skip_serializing_if = "Option::is_none")]
597 pub options: Option<MetadataOptions>,
598 #[serde(default, skip_serializing_if = "Vec::is_empty")]
599 pub public_keys: Vec<PublicKey>,
600}
601
602#[derive(Serialize, Deserialize, Debug)]
603pub struct ConstructionMetadataResponse {
604 pub metadata: ConstructionMetadata,
605 #[serde(default)]
606 pub suggested_fee: Vec<Amount>,
607}
608
609#[derive(Serialize, Deserialize, Debug)]
610pub struct ConstructionMetadata {
611 pub sender: IotaAddress,
612 pub coins: Vec<ObjectRef>,
613 pub objects: Vec<ObjectRef>,
614 pub total_coin_value: u64,
615 pub gas_price: u64,
616 pub budget: u64,
617}
618
619impl IntoResponse for ConstructionMetadataResponse {
620 fn into_response(self) -> Response {
621 Json(self).into_response()
622 }
623}
624
625#[derive(Deserialize)]
626pub struct ConstructionParseRequest {
627 pub network_identifier: NetworkIdentifier,
628 pub signed: bool,
629 pub transaction: Hex,
630}
631
632#[derive(Serialize)]
633pub struct ConstructionParseResponse {
634 pub operations: Operations,
635 pub account_identifier_signers: Vec<AccountIdentifier>,
636 #[serde(default, skip_serializing_if = "Option::is_none")]
637 pub metadata: Option<Value>,
638}
639
640impl IntoResponse for ConstructionParseResponse {
641 fn into_response(self) -> Response {
642 Json(self).into_response()
643 }
644}
645
646#[derive(Deserialize)]
647pub struct NetworkRequest {
648 pub network_identifier: NetworkIdentifier,
649 #[serde(default, skip_serializing_if = "Option::is_none")]
650 pub metadata: Option<Value>,
651}
652
653#[derive(Serialize)]
654pub struct NetworkStatusResponse {
655 pub current_block_identifier: BlockIdentifier,
656 pub current_block_timestamp: u64,
657 pub genesis_block_identifier: BlockIdentifier,
658 #[serde(default, skip_serializing_if = "Option::is_none")]
659 pub oldest_block_identifier: Option<BlockIdentifier>,
660 #[serde(default, skip_serializing_if = "Option::is_none")]
661 pub sync_status: Option<SyncStatus>,
662 pub peers: Vec<Peer>,
663}
664
665impl IntoResponse for NetworkStatusResponse {
666 fn into_response(self) -> Response {
667 Json(self).into_response()
668 }
669}
670
671#[derive(Serialize)]
672pub struct SyncStatus {
673 #[serde(default, skip_serializing_if = "Option::is_none")]
674 pub current_index: Option<u64>,
675 #[serde(default, skip_serializing_if = "Option::is_none")]
676 pub target_index: Option<u64>,
677 #[serde(default, skip_serializing_if = "Option::is_none")]
678 pub stage: Option<String>,
679 #[serde(default, skip_serializing_if = "Option::is_none")]
680 pub synced: Option<bool>,
681}
682#[derive(Serialize)]
683pub struct Peer {
684 pub peer_id: IotaAddress,
685 #[serde(default, skip_serializing_if = "Option::is_none")]
686 pub metadata: Option<Value>,
687}
688
689#[derive(Serialize)]
690pub struct NetworkOptionsResponse {
691 pub version: Version,
692 pub allow: Allow,
693}
694
695impl IntoResponse for NetworkOptionsResponse {
696 fn into_response(self) -> Response {
697 Json(self).into_response()
698 }
699}
700
701#[derive(Serialize)]
702pub struct Version {
703 pub rosetta_version: String,
704 pub node_version: String,
705 #[serde(default, skip_serializing_if = "Option::is_none")]
706 pub middleware_version: Option<String>,
707 #[serde(default, skip_serializing_if = "Option::is_none")]
708 pub metadata: Option<Value>,
709}
710
711#[derive(Serialize)]
712pub struct Allow {
713 pub operation_statuses: Vec<Value>,
714 pub operation_types: Vec<OperationType>,
715 pub errors: Vec<ErrorType>,
716 pub historical_balance_lookup: bool,
717 #[serde(default, skip_serializing_if = "Option::is_none")]
718 pub timestamp_start_index: Option<u64>,
719 pub call_methods: Vec<String>,
720 pub balance_exemptions: Vec<BalanceExemption>,
721 pub mempool_coins: bool,
722 #[serde(default, skip_serializing_if = "Option::is_none")]
723 pub block_hash_case: Option<Case>,
724 #[serde(default, skip_serializing_if = "Option::is_none")]
725 pub transaction_hash_case: Option<Case>,
726}
727
728#[derive(Copy, Clone, Deserialize, Serialize, Debug, Eq, PartialEq)]
729#[serde(rename_all = "UPPERCASE")]
730pub enum OperationStatus {
731 Success,
732 Failure,
733}
734
735impl From<IotaExecutionStatus> for OperationStatus {
736 fn from(es: IotaExecutionStatus) -> Self {
737 match es {
738 IotaExecutionStatus::Success => OperationStatus::Success,
739 IotaExecutionStatus::Failure { .. } => OperationStatus::Failure,
740 }
741 }
742}
743
744#[derive(Serialize)]
745pub struct BalanceExemption {
746 #[serde(default, skip_serializing_if = "Option::is_none")]
747 pub sub_account_address: Option<String>,
748 #[serde(default, skip_serializing_if = "Option::is_none")]
749 pub currency: Option<Currency>,
750 #[serde(default, skip_serializing_if = "Option::is_none")]
751 pub exemption_type: Option<ExemptionType>,
752}
753
754#[derive(Serialize)]
755#[serde(rename_all = "snake_case")]
756pub enum ExemptionType {
757 GreaterOrEqual,
758 LessOrEqual,
759 Dynamic,
760}
761
762#[derive(Serialize)]
763#[serde(rename_all = "snake_case")]
764pub enum Case {
765 UpperCase,
766 LowerCase,
767 CaseSensitive,
768 Null,
769}
770
771#[derive(Serialize, Deserialize, Clone, Debug)]
772pub struct Block {
773 pub block_identifier: BlockIdentifier,
774 pub parent_block_identifier: BlockIdentifier,
775 pub timestamp: u64,
776 pub transactions: Vec<Transaction>,
777 #[serde(default, skip_serializing_if = "Option::is_none")]
778 pub metadata: Option<Value>,
779}
780
781#[derive(Serialize, Deserialize, Clone, Debug)]
782pub struct Transaction {
783 pub transaction_identifier: TransactionIdentifier,
784 pub operations: Operations,
785 #[serde(default, skip_serializing_if = "Vec::is_empty")]
786 pub related_transactions: Vec<RelatedTransaction>,
787 #[serde(default, skip_serializing_if = "Option::is_none")]
788 pub metadata: Option<Value>,
789}
790
791#[derive(Serialize, Deserialize, Clone, Debug)]
792pub struct RelatedTransaction {
793 network_identifier: NetworkIdentifier,
794 transaction_identifier: TransactionIdentifier,
795 direction: Direction,
796}
797
798#[derive(Serialize, Deserialize, Clone, Debug)]
799#[serde(rename_all = "lowercase")]
800pub enum Direction {
801 Forward,
802 Backward,
803}
804
805#[derive(Serialize, Deserialize, Clone, Debug)]
806pub struct BlockResponse {
807 pub block: Block,
808 #[serde(default, skip_serializing_if = "Vec::is_empty")]
809 pub other_transactions: Vec<TransactionIdentifier>,
810}
811
812impl IntoResponse for BlockResponse {
813 fn into_response(self) -> Response {
814 Json(self).into_response()
815 }
816}
817#[derive(Serialize, Deserialize, Default, Debug)]
818pub struct PartialBlockIdentifier {
819 #[serde(default, skip_serializing_if = "Option::is_none")]
820 pub index: Option<u64>,
821 #[serde(default, skip_serializing_if = "Option::is_none")]
822 pub hash: Option<BlockHash>,
823}
824#[derive(Deserialize)]
825pub struct BlockRequest {
826 pub network_identifier: NetworkIdentifier,
827 #[serde(default)]
828 pub block_identifier: PartialBlockIdentifier,
829}
830
831#[derive(Deserialize)]
832pub struct BlockTransactionRequest {
833 pub network_identifier: NetworkIdentifier,
834 pub block_identifier: BlockIdentifier,
835 pub transaction_identifier: TransactionIdentifier,
836}
837
838#[derive(Serialize)]
839pub struct BlockTransactionResponse {
840 pub transaction: Transaction,
841}
842
843impl IntoResponse for BlockTransactionResponse {
844 fn into_response(self) -> Response {
845 Json(self).into_response()
846 }
847}
848
849#[derive(Serialize, Clone)]
850pub struct PrefundedAccount {
851 pub privkey: String,
852 pub account_identifier: AccountIdentifier,
853 pub curve_type: CurveType,
854 pub currency: Currency,
855}
856
857#[derive(Serialize, Deserialize, Debug)]
858pub enum InternalOperation {
859 PayIota {
860 sender: IotaAddress,
861 recipients: Vec<IotaAddress>,
862 amounts: Vec<u64>,
863 },
864 Stake {
865 sender: IotaAddress,
866 validator: IotaAddress,
867 amount: Option<u64>,
868 },
869 WithdrawStake {
870 sender: IotaAddress,
871 #[serde(default, skip_serializing_if = "Vec::is_empty")]
872 stake_ids: Vec<ObjectID>,
873 },
874}
875
876impl InternalOperation {
877 pub fn sender(&self) -> IotaAddress {
878 match self {
879 InternalOperation::PayIota { sender, .. }
880 | InternalOperation::Stake { sender, .. }
881 | InternalOperation::WithdrawStake { sender, .. } => *sender,
882 }
883 }
884 pub fn try_into_data(self, metadata: ConstructionMetadata) -> Result<TransactionData, Error> {
886 let pt = match self {
887 Self::PayIota {
888 recipients,
889 amounts,
890 ..
891 } => {
892 let mut builder = ProgrammableTransactionBuilder::new();
893 builder.pay_iota(recipients, amounts)?;
894 builder.finish()
895 }
896 InternalOperation::Stake {
897 validator, amount, ..
898 } => {
899 let mut builder = ProgrammableTransactionBuilder::new();
900
901 let (validator, system_state, amount) = if let Some(amount) = amount {
905 let amount = builder.pure(amount)?;
906 let validator = builder.input(CallArg::Pure(bcs::to_bytes(&validator)?))?;
907 let state = builder.input(CallArg::IOTA_SYSTEM_MUT)?;
908 (validator, state, amount)
909 } else {
910 let amount = builder.pure(metadata.total_coin_value - metadata.budget)?;
911 let state = builder.input(CallArg::IOTA_SYSTEM_MUT)?;
912 let validator = builder.input(CallArg::Pure(bcs::to_bytes(&validator)?))?;
913 (validator, state, amount)
914 };
915 let coin = builder.command(Command::SplitCoins(Argument::GasCoin, vec![amount]));
916
917 let arguments = vec![system_state, coin, validator];
918
919 builder.command(Command::move_call(
920 IOTA_SYSTEM_PACKAGE_ID,
921 IOTA_SYSTEM_MODULE_NAME.to_owned(),
922 ADD_STAKE_FUN_NAME.to_owned(),
923 vec![],
924 arguments,
925 ));
926 builder.finish()
927 }
928 InternalOperation::WithdrawStake { stake_ids, .. } => {
929 let mut builder = ProgrammableTransactionBuilder::new();
930
931 for stake_id in metadata.objects {
932 let (system_state, id) = if !stake_ids.is_empty() {
937 let system_state = builder.input(CallArg::IOTA_SYSTEM_MUT)?;
938 let id = builder.obj(ObjectArg::ImmOrOwnedObject(stake_id))?;
939 (system_state, id)
940 } else {
941 let id = builder.obj(ObjectArg::ImmOrOwnedObject(stake_id))?;
942 let system_state = builder.input(CallArg::IOTA_SYSTEM_MUT)?;
943 (system_state, id)
944 };
945
946 let arguments = vec![system_state, id];
947 builder.command(Command::move_call(
948 IOTA_SYSTEM_PACKAGE_ID,
949 IOTA_SYSTEM_MODULE_NAME.to_owned(),
950 WITHDRAW_STAKE_FUN_NAME.to_owned(),
951 vec![],
952 arguments,
953 ));
954 }
955 builder.finish()
956 }
957 };
958
959 Ok(TransactionData::new_programmable(
960 metadata.sender,
961 metadata.coins,
962 pt,
963 metadata.budget,
964 metadata.gas_price,
965 ))
966 }
967}