Skip to main content

stellar_baselib/
operation.rs

1//! Operations are individual commands that modify the ledger.
2use crate::liquidity_pool_asset::LiquidityPoolAssetBehavior;
3use crate::utils::decode_encode_muxed_account::decode_address_to_muxed_account_fix_for_g_address;
4use crate::xdr;
5use crate::xdr::WriteXdr;
6use num_traits::identities::One;
7use num_traits::ToPrimitive;
8use num_traits::{FromPrimitive, Num, Signed, Zero};
9use std::collections::HashMap;
10use std::hash::Hash;
11use std::str::FromStr;
12use stellar_strkey::ed25519::{MuxedAccount, PublicKey};
13
14use crate::asset::Asset;
15use crate::asset::AssetBehavior;
16use crate::claimant::Claimant;
17use crate::claimant::ClaimantBehavior;
18use crate::liquidity_pool_asset::LiquidityPoolAsset;
19use crate::utils::decode_encode_muxed_account::{
20    decode_address_to_muxed_account, encode_muxed_account_to_address,
21};
22
23pub use super::op_list::set_options::AccountFlags;
24pub use super::op_list::set_trustline_flags::TrustlineFlags;
25
26pub const ONE: i64 = 10_000_000;
27const MAX_INT64: &str = "9223372036854775807";
28pub enum SignerKeyAttrs {
29    Ed25519PublicKey(String),
30    PreAuthTx(String),
31    Sha256Hash(String),
32}
33
34pub struct Operation {
35    pub source: Option<xdr::MuxedAccount>,
36}
37
38#[derive(Debug, PartialEq, Eq)]
39pub enum Error {
40    InvalidField(String),
41    InvalidAmount(i64),
42    InvalidPrice(i32, i32),
43}
44
45impl Operation {
46    pub fn new() -> Self {
47        Self { source: None }
48    }
49
50    pub fn with_source(source: &str) -> Result<Self, Error> {
51        Ok(Self {
52            source: Some(
53                xdr::MuxedAccount::from_str(source)
54                    .map_err(|_| Error::InvalidField("source".into()))?,
55            ),
56        })
57    }
58}
59
60impl Default for Operation {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66/// Validates that a given amount is possible for a Stellar asset.
67pub fn is_valid_amount(value: &str, allow_zero: bool) -> bool {
68    if !value.is_empty() {
69        if let Ok(amount) = value.parse::<i64>() {
70            if !allow_zero && amount.is_zero() {
71                return false;
72            }
73
74            let max_int64 = i64::MAX;
75            let one = 1i64;
76
77            if amount.is_negative()
78                || amount > max_int64
79                || amount.to_string().chars().filter(|&c| c == '.').count() > 1
80                || amount
81                    .to_string()
82                    .chars()
83                    .skip_while(|&c| c != '.')
84                    .skip(1)
85                    .count()
86                    > 7
87            //TODO: Add case for checking infinite number and NaN
88            {
89                return false;
90            }
91
92            return true;
93        }
94    }
95
96    false
97}
98
99/// xdr representation of the amount value
100pub fn to_xdr_amount(value: &str) -> Result<xdr::Int64, Box<dyn std::error::Error>> {
101    let amount = value.parse::<i64>()?;
102    let one = 1i64;
103    let xdr_amount = amount * one;
104    let xdr_string = xdr_amount.to_string();
105    let xdr_int64 = xdr::Int64::from_str(&xdr_string)?;
106    Ok(xdr_int64)
107}
108
109pub fn from_xdr_amount(value: u64) -> f64 {
110    // Convert the value to f64, divide by ONE, and keep up to 7 decimal places
111    round_to((value.to_f64().unwrap() / ONE as f64), 7)
112}
113
114// Utility function to round an f64 to a specific number of decimal places
115pub fn round_to(value: f64, decimal_places: u32) -> f64 {
116    let multiplier = 10f64.powi(decimal_places as i32);
117    (value * multiplier).round() / multiplier
118}
119
120fn account_id_to_address(account_id: &xdr::AccountId) -> String {
121    let xdr::PublicKey::PublicKeyTypeEd25519(val) = account_id.0.clone();
122    let key: Result<PublicKey, stellar_strkey::DecodeError> =
123        PublicKey::from_string(val.to_string().as_str());
124
125    if key.is_ok() {
126        val.to_string()
127    } else {
128        panic!("Invalid account");
129    }
130}
131
132fn convert_xdr_signer_key_to_object(
133    signer_key: &xdr::SignerKeyType,
134) -> Result<SignerKeyAttrs, String> {
135    match signer_key {
136        xdr::SignerKeyType::Ed25519 => {
137            let ed25519_public_key = PublicKey::from_string(signer_key.to_string().as_str())
138                .unwrap()
139                .to_string();
140            Ok(SignerKeyAttrs::Ed25519PublicKey(ed25519_public_key))
141        }
142        xdr::SignerKeyType::PreAuthTx => Ok(SignerKeyAttrs::PreAuthTx(
143            signer_key.to_xdr_base64(xdr::Limits::none()).unwrap(),
144        )),
145        xdr::SignerKeyType::HashX => Ok(SignerKeyAttrs::Sha256Hash(
146            signer_key.to_xdr_base64(xdr::Limits::none()).unwrap(),
147        )),
148        _ => panic!("Invalid Type"),
149    }
150}