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, Bytes, String};
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<&MuxedAddress> for MuxedAddress {
163 fn from(address: &MuxedAddress) -> Self {
164 address.clone()
165 }
166}
167
168impl From<Address> for MuxedAddress {
169 fn from(address: Address) -> Self {
170 (&address).into()
171 }
172}
173
174impl From<&Address> for MuxedAddress {
175 fn from(address: &Address) -> Self {
176 address
177 .as_object()
178 .try_into_val(address.env())
179 .unwrap_infallible()
180 }
181}
182
183impl MuxedAddress {
184 pub fn from_str(env: &Env, strkey: &str) -> MuxedAddress {
197 match strkey.as_bytes().first() {
198 Some(b'M') => MuxedAddress::from_muxed_strkey_bytes(
199 env,
200 &Bytes::from_slice(env, strkey.as_bytes()),
201 ),
202 _ => Address::from_str(env, strkey).into(),
203 }
204 }
205
206 pub fn from_string(strkey: &String) -> Self {
219 let env = strkey.env();
220 let strkey_bytes = strkey.to_bytes();
221 match strkey_bytes.first() {
222 Some(b'M') => MuxedAddress::from_muxed_strkey_bytes(env, &strkey_bytes),
223 _ => Address::from_string(strkey).into(),
224 }
225 }
226
227 pub fn from_string_bytes(strkey: &Bytes) -> Self {
241 let env = strkey.env();
242 match strkey.first() {
243 Some(b'M') => MuxedAddress::from_muxed_strkey_bytes(env, strkey),
244 _ => Address::from_string_bytes(strkey).into(),
245 }
246 }
247
248 fn from_muxed_strkey_bytes(env: &Env, strkey: &Bytes) -> Self {
250 use crate::xdr::{FromXdr, ScAddressType, ScValType};
251 use stellar_strkey::ed25519::MuxedAccount;
252
253 const MAX_STRKEY_LEN: usize = 69;
255 let mut strkey_buf = [0u8; MAX_STRKEY_LEN];
256 let len = strkey.len() as usize;
257 if len > strkey_buf.len() {
258 sdk_panic!("unexpected strkey length");
259 }
260 strkey.copy_into_slice(&mut strkey_buf[..len]);
261
262 let muxed = MuxedAccount::from_slice(&strkey_buf[..len])
263 .unwrap_or_else(|_| sdk_panic!("muxed strkey invalid"));
264
265 const SCVAL_ADDRESS: i32 = ScValType::Address as i32;
270 const SCADDRESS_MUXED_ACCOUNT: i32 = ScAddressType::MuxedAccount as i32;
271 let mut buf = [0u8; 48];
272 buf[0..4].copy_from_slice(&SCVAL_ADDRESS.to_be_bytes());
273 buf[4..8].copy_from_slice(&SCADDRESS_MUXED_ACCOUNT.to_be_bytes());
274 buf[8..16].copy_from_slice(&muxed.id.to_be_bytes()); buf[16..48].copy_from_slice(&muxed.ed25519); let xdr_bytes = Bytes::from_slice(env, &buf);
277
278 MuxedAddress::from_xdr(env, &xdr_bytes).unwrap_or_else(|_| sdk_panic!("invalid xdr"))
279 }
280
281 pub fn address(&self) -> Address {
286 match &self.obj {
287 AddressObjectWrapper::Address(address_object) => {
288 Address::try_from_val(&self.env, address_object).unwrap_infallible()
289 }
290 AddressObjectWrapper::MuxedAddress(muxed_address_object) => Address::try_from_val(
291 &self.env,
292 &internal::Env::get_address_from_muxed_address(&self.env, *muxed_address_object)
293 .unwrap_infallible(),
294 )
295 .unwrap_infallible(),
296 }
297 }
298
299 pub fn id(&self) -> Option<u64> {
307 match &self.obj {
308 AddressObjectWrapper::Address(_) => None,
309 AddressObjectWrapper::MuxedAddress(muxed_address_object) => Some(
310 u64::try_from_val(
311 &self.env,
312 &internal::Env::get_id_from_muxed_address(&self.env, *muxed_address_object)
313 .unwrap_infallible(),
314 )
315 .unwrap(),
316 ),
317 }
318 }
319
320 #[inline(always)]
321 pub(crate) unsafe fn unchecked_new_from_address(env: Env, obj: AddressObject) -> Self {
322 Self {
323 env,
324 obj: AddressObjectWrapper::Address(obj),
325 }
326 }
327
328 #[inline(always)]
329 pub(crate) unsafe fn unchecked_new(env: Env, obj: MuxedAddressObject) -> Self {
330 Self {
331 env,
332 obj: AddressObjectWrapper::MuxedAddress(obj),
333 }
334 }
335
336 #[inline(always)]
337 pub fn env(&self) -> &Env {
338 &self.env
339 }
340
341 pub fn as_val(&self) -> &Val {
342 match &self.obj {
343 AddressObjectWrapper::Address(o) => o.as_val(),
344 AddressObjectWrapper::MuxedAddress(o) => o.as_val(),
345 }
346 }
347
348 pub fn to_val(&self) -> Val {
349 match self.obj {
350 AddressObjectWrapper::Address(o) => o.to_val(),
351 AddressObjectWrapper::MuxedAddress(o) => o.to_val(),
352 }
353 }
354}
355
356#[cfg(not(target_family = "wasm"))]
357impl TryFromVal<Env, ScVal> for MuxedAddress {
358 type Error = ConversionError;
359 fn try_from_val(env: &Env, val: &ScVal) -> Result<Self, Self::Error> {
360 let v = Val::try_from_val(env, val)?;
361 match val {
362 ScVal::Address(sc_address) => match sc_address {
363 ScAddress::Account(_) | ScAddress::Contract(_) => {
364 Ok(AddressObject::try_from_val(env, &v)?
365 .try_into_val(env)
366 .unwrap_infallible())
367 }
368 ScAddress::MuxedAccount(_) => Ok(MuxedAddressObject::try_from_val(env, &v)?
369 .try_into_val(env)
370 .unwrap_infallible()),
371 ScAddress::ClaimableBalance(_) | ScAddress::LiquidityPool(_) => {
372 panic!("unsupported ScAddress type")
373 }
374 },
375 _ => panic!("incorrect scval type"),
376 }
377 }
378}
379
380#[cfg(not(target_family = "wasm"))]
381impl TryFromVal<Env, ScAddress> for MuxedAddress {
382 type Error = ConversionError;
383 fn try_from_val(env: &Env, val: &ScAddress) -> Result<Self, Self::Error> {
384 ScVal::Address(val.clone()).try_into_val(env)
385 }
386}
387
388#[cfg(any(test, feature = "testutils"))]
389#[cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
390impl crate::testutils::MuxedAddress for MuxedAddress {
391 fn generate(env: &Env) -> crate::MuxedAddress {
392 let sc_val = ScVal::Address(crate::env::internal::xdr::ScAddress::MuxedAccount(
393 crate::env::internal::xdr::MuxedEd25519Account {
394 ed25519: crate::env::internal::xdr::Uint256(
395 env.with_generator(|mut g| g.address()),
396 ),
397 id: env.with_generator(|mut g| g.mux_id()),
398 },
399 ));
400 sc_val.try_into_val(env).unwrap()
401 }
402
403 fn new<T: Into<MuxedAddress>>(address: T, id: u64) -> crate::MuxedAddress {
404 let address: MuxedAddress = address.into();
405 let sc_val = ScVal::try_from_val(&address.env, address.as_val()).unwrap();
406 let account_id = match sc_val {
407 ScVal::Address(address) => match address {
408 ScAddress::MuxedAccount(muxed_account) => muxed_account.ed25519,
409 ScAddress::Account(crate::env::internal::xdr::AccountId(
410 crate::env::internal::xdr::PublicKey::PublicKeyTypeEd25519(account_id),
411 )) => account_id,
412 ScAddress::Contract(_) => panic!("contract addresses can not be multiplexed"),
413 ScAddress::ClaimableBalance(_) | ScAddress::LiquidityPool(_) => unreachable!(),
414 },
415 _ => unreachable!(),
416 };
417 let result_sc_val = ScVal::Address(ScAddress::MuxedAccount(
418 crate::env::internal::xdr::MuxedEd25519Account {
419 id,
420 ed25519: account_id,
421 },
422 ));
423 result_sc_val.try_into_val(&address.env).unwrap()
424 }
425}