1use core::{cmp::Ordering, convert::Infallible, fmt::Debug};
2
3use super::{
4 env::internal::{AddressObject, Env as _, MuxedAddressObject, Tag},
5 ConversionError, Env, TryFromVal, TryIntoVal, Val,
6};
7use crate::{env::internal, unwrap::UnwrapInfallible, Address};
8
9#[cfg(not(target_family = "wasm"))]
10use crate::env::internal::xdr::{ScAddress, ScVal};
11
12#[derive(Clone)]
13enum AddressObjectWrapper {
14 Address(AddressObject),
15 MuxedAddress(MuxedAddressObject),
16}
17
18#[derive(Clone)]
43pub struct MuxedAddress {
44 env: Env,
45 obj: AddressObjectWrapper,
46}
47
48impl Debug for MuxedAddress {
49 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50 #[cfg(target_family = "wasm")]
51 match &self.obj {
52 AddressObjectWrapper::Address(_) => write!(f, "Address(..)"),
53 AddressObjectWrapper::MuxedAddress(_) => write!(f, "MuxedAddress(..)"),
54 }
55 #[cfg(not(target_family = "wasm"))]
56 {
57 use crate::env::internal::xdr;
58 use stellar_strkey::Strkey;
59 match &self.obj {
60 AddressObjectWrapper::Address(address_object) => {
61 Address::try_from_val(self.env(), address_object)
62 .map_err(|_| core::fmt::Error)?
63 .fmt(f)
64 }
65 AddressObjectWrapper::MuxedAddress(muxed_address_object) => {
66 let sc_val = ScVal::try_from_val(self.env(), &muxed_address_object.to_val())
67 .map_err(|_| core::fmt::Error)?;
68 if let ScVal::Address(addr) = sc_val {
69 match addr {
70 xdr::ScAddress::MuxedAccount(muxed_account) => {
71 let strkey = Strkey::MuxedAccountEd25519(
72 stellar_strkey::ed25519::MuxedAccount {
73 ed25519: muxed_account.ed25519.0,
74 id: muxed_account.id,
75 },
76 );
77 write!(f, "MuxedAccount({})", strkey.to_string())
78 }
79 _ => Err(core::fmt::Error),
80 }
81 } else {
82 Err(core::fmt::Error)
83 }
84 }
85 }
86 }
87 }
88}
89
90impl Eq for MuxedAddress {}
91
92impl PartialEq for MuxedAddress {
93 fn eq(&self, other: &Self) -> bool {
94 self.partial_cmp(other) == Some(Ordering::Equal)
95 }
96}
97
98impl PartialOrd for MuxedAddress {
99 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
100 Some(Ord::cmp(self, other))
101 }
102}
103
104impl Ord for MuxedAddress {
105 fn cmp(&self, other: &Self) -> Ordering {
106 let v = self
107 .env
108 .obj_cmp(self.to_val(), other.to_val())
109 .unwrap_infallible();
110 v.cmp(&0)
111 }
112}
113
114impl TryFromVal<Env, MuxedAddressObject> for MuxedAddress {
115 type Error = Infallible;
116
117 fn try_from_val(env: &Env, val: &MuxedAddressObject) -> Result<Self, Self::Error> {
118 Ok(unsafe { MuxedAddress::unchecked_new(env.clone(), *val) })
119 }
120}
121
122impl TryFromVal<Env, AddressObject> for MuxedAddress {
123 type Error = Infallible;
124
125 fn try_from_val(env: &Env, val: &AddressObject) -> Result<Self, Self::Error> {
126 Ok(unsafe { MuxedAddress::unchecked_new_from_address(env.clone(), *val) })
127 }
128}
129
130impl TryFromVal<Env, Val> for MuxedAddress {
131 type Error = ConversionError;
132
133 fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
134 if val.get_tag() == Tag::AddressObject {
135 Ok(AddressObject::try_from_val(env, val)?
136 .try_into_val(env)
137 .unwrap_infallible())
138 } else {
139 Ok(MuxedAddressObject::try_from_val(env, val)?
140 .try_into_val(env)
141 .unwrap_infallible())
142 }
143 }
144}
145
146impl TryFromVal<Env, MuxedAddress> for Val {
147 type Error = ConversionError;
148
149 fn try_from_val(_env: &Env, v: &MuxedAddress) -> Result<Self, Self::Error> {
150 Ok(v.to_val())
151 }
152}
153
154impl TryFromVal<Env, &MuxedAddress> for Val {
155 type Error = ConversionError;
156
157 fn try_from_val(_env: &Env, v: &&MuxedAddress) -> Result<Self, Self::Error> {
158 Ok(v.to_val())
159 }
160}
161
162impl From<Address> for MuxedAddress {
163 fn from(address: Address) -> Self {
164 (&address).into()
165 }
166}
167
168impl From<&Address> for MuxedAddress {
169 fn from(address: &Address) -> Self {
170 address
171 .as_object()
172 .try_into_val(address.env())
173 .unwrap_infallible()
174 }
175}
176
177impl MuxedAddress {
178 pub fn address(&self) -> Address {
183 match &self.obj {
184 AddressObjectWrapper::Address(address_object) => {
185 Address::try_from_val(&self.env, address_object).unwrap_infallible()
186 }
187 AddressObjectWrapper::MuxedAddress(muxed_address_object) => Address::try_from_val(
188 &self.env,
189 &internal::Env::get_address_from_muxed_address(&self.env, *muxed_address_object)
190 .unwrap_infallible(),
191 )
192 .unwrap_infallible(),
193 }
194 }
195
196 pub fn id(&self) -> Option<u64> {
204 match &self.obj {
205 AddressObjectWrapper::Address(_) => None,
206 AddressObjectWrapper::MuxedAddress(muxed_address_object) => Some(
207 u64::try_from_val(
208 &self.env,
209 &internal::Env::get_id_from_muxed_address(&self.env, *muxed_address_object)
210 .unwrap_infallible(),
211 )
212 .unwrap(),
213 ),
214 }
215 }
216
217 #[inline(always)]
218 pub(crate) unsafe fn unchecked_new_from_address(env: Env, obj: AddressObject) -> Self {
219 Self {
220 env,
221 obj: AddressObjectWrapper::Address(obj),
222 }
223 }
224
225 #[inline(always)]
226 pub(crate) unsafe fn unchecked_new(env: Env, obj: MuxedAddressObject) -> Self {
227 Self {
228 env,
229 obj: AddressObjectWrapper::MuxedAddress(obj),
230 }
231 }
232
233 #[inline(always)]
234 pub fn env(&self) -> &Env {
235 &self.env
236 }
237
238 pub fn as_val(&self) -> &Val {
239 match &self.obj {
240 AddressObjectWrapper::Address(o) => o.as_val(),
241 AddressObjectWrapper::MuxedAddress(o) => o.as_val(),
242 }
243 }
244
245 pub fn to_val(&self) -> Val {
246 match self.obj {
247 AddressObjectWrapper::Address(o) => o.to_val(),
248 AddressObjectWrapper::MuxedAddress(o) => o.to_val(),
249 }
250 }
251}
252
253#[cfg(not(target_family = "wasm"))]
254impl TryFromVal<Env, ScVal> for MuxedAddress {
255 type Error = ConversionError;
256 fn try_from_val(env: &Env, val: &ScVal) -> Result<Self, Self::Error> {
257 let v = Val::try_from_val(env, val)?;
258 match val {
259 ScVal::Address(sc_address) => match sc_address {
260 ScAddress::Account(_) | ScAddress::Contract(_) => {
261 Ok(AddressObject::try_from_val(env, &v)?
262 .try_into_val(env)
263 .unwrap_infallible())
264 }
265 ScAddress::MuxedAccount(_) => Ok(MuxedAddressObject::try_from_val(env, &v)?
266 .try_into_val(env)
267 .unwrap_infallible()),
268 ScAddress::ClaimableBalance(_) | ScAddress::LiquidityPool(_) => {
269 panic!("unsupported ScAddress type")
270 }
271 },
272 _ => panic!("incorrect scval type"),
273 }
274 }
275}
276
277#[cfg(not(target_family = "wasm"))]
278impl TryFromVal<Env, ScAddress> for MuxedAddress {
279 type Error = ConversionError;
280 fn try_from_val(env: &Env, val: &ScAddress) -> Result<Self, Self::Error> {
281 ScVal::Address(val.clone()).try_into_val(env)
282 }
283}
284
285#[cfg(any(test, feature = "testutils"))]
286#[cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
287impl crate::testutils::MuxedAddress for MuxedAddress {
288 fn generate(env: &Env) -> crate::MuxedAddress {
289 let sc_val = ScVal::Address(crate::env::internal::xdr::ScAddress::MuxedAccount(
290 crate::env::internal::xdr::MuxedEd25519Account {
291 ed25519: crate::env::internal::xdr::Uint256(
292 env.with_generator(|mut g| g.address()),
293 ),
294 id: env.with_generator(|mut g| g.mux_id()),
295 },
296 ));
297 sc_val.try_into_val(env).unwrap()
298 }
299
300 fn new<T: Into<MuxedAddress>>(address: T, id: u64) -> crate::MuxedAddress {
301 let address: MuxedAddress = address.into();
302 let sc_val = ScVal::try_from_val(&address.env, address.as_val()).unwrap();
303 let account_id = match sc_val {
304 ScVal::Address(address) => match address {
305 ScAddress::MuxedAccount(muxed_account) => muxed_account.ed25519,
306 ScAddress::Account(crate::env::internal::xdr::AccountId(
307 crate::env::internal::xdr::PublicKey::PublicKeyTypeEd25519(account_id),
308 )) => account_id,
309 ScAddress::Contract(_) => panic!("contract addresses can not be multiplexed"),
310 ScAddress::ClaimableBalance(_) | ScAddress::LiquidityPool(_) => unreachable!(),
311 },
312 _ => unreachable!(),
313 };
314 let result_sc_val = ScVal::Address(ScAddress::MuxedAccount(
315 crate::env::internal::xdr::MuxedEd25519Account {
316 id,
317 ed25519: account_id,
318 },
319 ));
320 result_sc_val.try_into_val(&address.env).unwrap()
321 }
322}