use crate::models::ledger::objects::LedgerEntryType;
use crate::models::{Currency, FlagCollection, Model};
use alloc::borrow::Cow;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use serde_with::skip_serializing_none;
use strum_macros::{AsRefStr, Display, EnumIter};
use super::{CommonFields, LedgerObject};
#[derive(
Debug, Eq, PartialEq, Clone, Serialize_repr, Deserialize_repr, Display, AsRefStr, EnumIter,
)]
#[repr(u32)]
pub enum VaultFlag {
LsfVaultPrivate = 0x00010000,
}
#[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
#[serde(rename_all = "PascalCase")]
pub struct Vault<'a> {
#[serde(flatten)]
pub common_fields: CommonFields<'a, VaultFlag>,
pub owner: Cow<'a, str>,
pub account: Cow<'a, str>,
pub asset: Currency<'a>,
pub assets_total: Option<Cow<'a, str>>,
pub assets_available: Option<Cow<'a, str>>,
pub assets_maximum: Option<Cow<'a, str>>,
pub loss_unrealized: Option<Cow<'a, str>>,
#[serde(rename = "ShareMPTID")]
pub share_mpt_id: Cow<'a, str>,
pub withdrawal_policy: u8,
pub scale: Option<u8>,
pub sequence: u32,
pub data: Option<Cow<'a, str>>,
pub owner_node: Cow<'a, str>,
#[serde(rename = "PreviousTxnID")]
pub previous_txn_id: Cow<'a, str>,
pub previous_txn_lgr_seq: u32,
}
impl<'a> Model for Vault<'a> {}
impl<'a> LedgerObject<VaultFlag> for Vault<'a> {
fn get_ledger_entry_type(&self) -> LedgerEntryType {
self.common_fields.get_ledger_entry_type()
}
}
impl<'a> Vault<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
flags: FlagCollection<VaultFlag>,
index: Option<Cow<'a, str>>,
owner: Cow<'a, str>,
account: Cow<'a, str>,
asset: Currency<'a>,
share_mpt_id: Cow<'a, str>,
withdrawal_policy: u8,
sequence: u32,
owner_node: Cow<'a, str>,
previous_txn_id: Cow<'a, str>,
previous_txn_lgr_seq: u32,
) -> Self {
Self {
common_fields: CommonFields {
flags,
ledger_entry_type: LedgerEntryType::Vault,
index,
ledger_index: None,
},
owner,
account,
asset,
assets_total: None,
assets_available: None,
assets_maximum: None,
loss_unrealized: None,
share_mpt_id,
withdrawal_policy,
scale: None,
sequence,
data: None,
owner_node,
previous_txn_id,
previous_txn_lgr_seq,
}
}
}
#[cfg(test)]
mod test_serde {
use crate::models::currency::{Currency, IssuedCurrency};
use crate::models::ledger::objects::vault::{Vault, VaultFlag};
use crate::models::ledger::objects::CommonFields;
use crate::models::ledger::objects::LedgerEntryType;
use crate::models::FlagCollection;
use alloc::borrow::Cow;
use alloc::vec;
fn make_vault<'a>(
index: Option<Cow<'a, str>>,
owner: Cow<'a, str>,
account: Cow<'a, str>,
asset: Currency<'a>,
share_mpt_id: Cow<'a, str>,
withdrawal_policy: u8,
sequence: u32,
owner_node: Cow<'a, str>,
previous_txn_id: Cow<'a, str>,
previous_txn_lgr_seq: u32,
) -> Vault<'a> {
Vault::new(
FlagCollection::<VaultFlag>::default(),
index,
owner,
account,
asset,
share_mpt_id,
withdrawal_policy,
sequence,
owner_node,
previous_txn_id,
previous_txn_lgr_seq,
)
}
#[test]
fn test_serialize() {
let vault = Vault {
common_fields: CommonFields {
flags: FlagCollection::<VaultFlag>::default(),
ledger_entry_type: LedgerEntryType::Vault,
index: Some(Cow::from("ForTest")),
ledger_index: None,
},
owner: "rVaultOwner123".into(),
account: "rPseudoAccount456".into(),
asset: Currency::IssuedCurrency(IssuedCurrency::new("USD".into(), "rIssuer456".into())),
assets_total: Some("1000000".into()),
assets_available: Some("800000".into()),
assets_maximum: Some("5000000".into()),
loss_unrealized: Some("0".into()),
share_mpt_id: "00000001C752C42A1EBD6BF2403134F7CFD2F1D835AFD26E".into(),
withdrawal_policy: 1,
scale: Some(6),
sequence: 5,
data: Some("48656C6C6F".into()),
owner_node: "0".into(),
previous_txn_id: "ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890"
.into(),
previous_txn_lgr_seq: 12345678,
};
let serialized = serde_json::to_string(&vault).unwrap();
let deserialized: Vault = serde_json::from_str(&serialized).unwrap();
assert_eq!(vault, deserialized);
}
#[test]
fn test_minimal_vault() {
let vault = make_vault(
Some(Cow::from("MinimalTest")),
"rMinimalOwner789".into(),
"rMinimalPseudo789".into(),
Currency::IssuedCurrency(IssuedCurrency::new("EUR".into(), "rEURIssuer012".into())),
"00000001C752C42A1EBD6BF2403134F7CFD2F1D835AFD26E".into(),
1,
1,
"0".into(),
"1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF".into(),
1,
);
let serialized = serde_json::to_string(&vault).unwrap();
let deserialized: Vault = serde_json::from_str(&serialized).unwrap();
assert_eq!(vault, deserialized);
}
#[test]
fn test_vault_with_all_fields() {
let vault = Vault {
common_fields: CommonFields {
flags: FlagCollection::<VaultFlag>::default(),
ledger_entry_type: LedgerEntryType::Vault,
index: Some(Cow::from("FullVaultTest")),
ledger_index: Some(Cow::from("ledger_idx_123")),
},
owner: "rFullVaultOwner456".into(),
account: "rFullPseudoAccount".into(),
asset: Currency::IssuedCurrency(IssuedCurrency::new(
"BTC".into(),
"rBTCIssuer789".into(),
)),
assets_total: Some("50000000".into()),
assets_available: Some("45000000".into()),
assets_maximum: Some("100000000".into()),
loss_unrealized: Some("200000".into()),
share_mpt_id: "00000001C752C42A1EBD6BF2403134F7CFD2F1D835AFD26E".into(),
withdrawal_policy: 1,
scale: Some(6),
sequence: 1,
data: Some("44617461".into()),
owner_node: "42".into(),
previous_txn_id: "FEDCBA0987654321FEDCBA0987654321FEDCBA0987654321FEDCBA0987654321"
.into(),
previous_txn_lgr_seq: 99999999,
};
let serialized = serde_json::to_string(&vault).unwrap();
let deserialized: Vault = serde_json::from_str(&serialized).unwrap();
assert_eq!(vault, deserialized);
}
#[test]
fn test_new_constructor() {
let vault = Vault::new(
FlagCollection::<VaultFlag>::default(),
Some(Cow::from("NewConstructorTest")),
"rNewOwner".into(),
"rNewAccount".into(),
Currency::IssuedCurrency(IssuedCurrency::new("USD".into(), "rIssuer".into())),
"00000001C752C42A1EBD6BF2403134F7CFD2F1D835AFD26E".into(),
1,
42,
"0".into(),
"ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890".into(),
100,
);
let serialized = serde_json::to_string(&vault).unwrap();
let deserialized: Vault = serde_json::from_str(&serialized).unwrap();
assert_eq!(vault, deserialized);
assert!(vault.assets_total.is_none());
assert!(vault.data.is_none());
}
#[test]
fn test_vault_private_flag_serde() {
let vault = Vault {
common_fields: CommonFields {
flags: vec![VaultFlag::LsfVaultPrivate].into(),
ledger_entry_type: LedgerEntryType::Vault,
index: Some(Cow::from("PrivateFlagTest")),
ledger_index: None,
},
owner: "rFlagOwner".into(),
account: "rFlagAccount".into(),
asset: Currency::IssuedCurrency(IssuedCurrency::new("USD".into(), "rIssuer".into())),
assets_total: None,
assets_available: None,
assets_maximum: None,
loss_unrealized: None,
share_mpt_id: "00000001C752C42A1EBD6BF2403134F7CFD2F1D835AFD26E".into(),
withdrawal_policy: 1,
scale: None,
sequence: 1,
data: None,
owner_node: "0".into(),
previous_txn_id: "ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890"
.into(),
previous_txn_lgr_seq: 1,
};
let serialized = serde_json::to_string(&vault).unwrap();
let deserialized: Vault = serde_json::from_str(&serialized).unwrap();
assert_eq!(vault, deserialized);
assert!(
serialized.contains("65536"),
"expected LsfVaultPrivate flag value 65536 in JSON: {serialized}"
);
}
#[test]
fn test_serialized_keys_are_pascal_case() {
let vault = Vault {
common_fields: CommonFields {
flags: FlagCollection::<VaultFlag>::default(),
ledger_entry_type: LedgerEntryType::Vault,
index: Some(Cow::from("KeysTest")),
ledger_index: None,
},
owner: "rKeysOwner".into(),
account: "rKeysAccount".into(),
asset: Currency::IssuedCurrency(IssuedCurrency::new("USD".into(), "rIssuerX".into())),
assets_total: Some("100".into()),
assets_available: Some("90".into()),
assets_maximum: Some("200".into()),
loss_unrealized: Some("5".into()),
share_mpt_id: "00000001C752C42A1EBD6BF2403134F7CFD2F1D835AFD26E".into(),
withdrawal_policy: 1,
scale: Some(6),
sequence: 1,
data: Some("48656C6C6F".into()),
owner_node: "0".into(),
previous_txn_id: "ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890"
.into(),
previous_txn_lgr_seq: 100,
};
let json = serde_json::to_string(&vault).unwrap();
assert!(json.contains("\"Account\""), "missing Account key: {json}");
assert!(json.contains("\"Owner\""), "missing Owner key: {json}");
assert!(json.contains("\"Asset\""), "missing Asset key: {json}");
assert!(
json.contains("\"AssetsTotal\""),
"missing AssetsTotal key: {json}"
);
assert!(
json.contains("\"AssetsAvailable\""),
"missing AssetsAvailable key: {json}"
);
assert!(
json.contains("\"AssetsMaximum\""),
"missing AssetsMaximum key: {json}"
);
assert!(
json.contains("\"LossUnrealized\""),
"missing LossUnrealized key: {json}"
);
assert!(
json.contains("\"ShareMPTID\""),
"missing ShareMPTID key: {json}"
);
assert!(
json.contains("\"WithdrawalPolicy\""),
"missing WithdrawalPolicy key: {json}"
);
assert!(json.contains("\"Scale\""), "missing Scale key: {json}");
assert!(
json.contains("\"Sequence\""),
"missing Sequence key: {json}"
);
assert!(json.contains("\"Data\""), "missing Data key: {json}");
assert!(
json.contains("\"OwnerNode\""),
"missing OwnerNode key: {json}"
);
assert!(
json.contains("\"PreviousTxnID\""),
"missing PreviousTxnID key: {json}"
);
assert!(
json.contains("\"PreviousTxnLgrSeq\""),
"missing PreviousTxnLgrSeq key: {json}"
);
assert!(
json.contains("\"LedgerEntryType\":\"Vault\""),
"missing LedgerEntryType=Vault: {json}"
);
}
}