1use core::{cmp::Ordering, convert::Infallible, fmt::Debug};
2
3use super::{
4 env::internal::{AddressObject, Env as _},
5 ConversionError, Env, String, TryFromVal, TryIntoVal, Val,
6};
7
8#[cfg(not(target_family = "wasm"))]
9use crate::env::internal::xdr::ScVal;
10#[cfg(any(test, feature = "testutils", not(target_family = "wasm")))]
11use crate::env::xdr::ScAddress;
12use crate::{unwrap::UnwrapInfallible, Bytes, Vec};
13
14#[derive(Clone)]
31pub struct Address {
32 env: Env,
33 obj: AddressObject,
34}
35
36impl Debug for Address {
37 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38 #[cfg(target_family = "wasm")]
39 write!(f, "Address(..)")?;
40 #[cfg(not(target_family = "wasm"))]
41 {
42 use crate::env::internal::xdr;
43 use stellar_strkey::{ed25519, Contract, Strkey};
44 let sc_val = ScVal::try_from(self).map_err(|_| core::fmt::Error)?;
45 if let ScVal::Address(addr) = sc_val {
46 match addr {
47 xdr::ScAddress::Account(account_id) => {
48 let xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(xdr::Uint256(
49 ed25519,
50 ))) = account_id;
51 let strkey = Strkey::PublicKeyEd25519(ed25519::PublicKey(ed25519));
52 write!(f, "AccountId({})", strkey.to_string())?;
53 }
54 xdr::ScAddress::Contract(xdr::ContractId(contract_id)) => {
55 let strkey = Strkey::Contract(Contract(contract_id.0));
56 write!(f, "Contract({})", strkey.to_string())?;
57 }
58 ScAddress::MuxedAccount(_)
59 | ScAddress::ClaimableBalance(_)
60 | ScAddress::LiquidityPool(_) => {
61 return Err(core::fmt::Error);
62 }
63 }
64 } else {
65 return Err(core::fmt::Error);
66 }
67 }
68 Ok(())
69 }
70}
71
72impl Eq for Address {}
73
74impl PartialEq for Address {
75 fn eq(&self, other: &Self) -> bool {
76 self.partial_cmp(other) == Some(Ordering::Equal)
77 }
78}
79
80impl PartialOrd for Address {
81 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
82 Some(Ord::cmp(self, other))
83 }
84}
85
86impl Ord for Address {
87 fn cmp(&self, other: &Self) -> Ordering {
88 #[cfg(not(target_family = "wasm"))]
89 if !self.env.is_same_env(&other.env) {
90 return ScVal::from(self).cmp(&ScVal::from(other));
91 }
92 let v = self
93 .env
94 .obj_cmp(self.obj.to_val(), other.obj.to_val())
95 .unwrap_infallible();
96 v.cmp(&0)
97 }
98}
99
100impl TryFromVal<Env, AddressObject> for Address {
101 type Error = Infallible;
102
103 fn try_from_val(env: &Env, val: &AddressObject) -> Result<Self, Self::Error> {
104 Ok(unsafe { Address::unchecked_new(env.clone(), *val) })
105 }
106}
107
108impl TryFromVal<Env, Val> for Address {
109 type Error = ConversionError;
110
111 fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
112 Ok(AddressObject::try_from_val(env, val)?
113 .try_into_val(env)
114 .unwrap_infallible())
115 }
116}
117
118impl TryFromVal<Env, Address> for Val {
119 type Error = ConversionError;
120
121 fn try_from_val(_env: &Env, v: &Address) -> Result<Self, Self::Error> {
122 Ok(v.to_val())
123 }
124}
125
126impl TryFromVal<Env, &Address> for Val {
127 type Error = ConversionError;
128
129 fn try_from_val(_env: &Env, v: &&Address) -> Result<Self, Self::Error> {
130 Ok(v.to_val())
131 }
132}
133
134#[cfg(not(target_family = "wasm"))]
135impl From<&Address> for ScVal {
136 fn from(v: &Address) -> Self {
137 ScVal::try_from_val(&v.env, &v.obj.to_val()).unwrap()
143 }
144}
145
146#[cfg(not(target_family = "wasm"))]
147impl From<Address> for ScVal {
148 fn from(v: Address) -> Self {
149 (&v).into()
150 }
151}
152
153#[cfg(not(target_family = "wasm"))]
154impl TryFromVal<Env, ScVal> for Address {
155 type Error = ConversionError;
156 fn try_from_val(env: &Env, val: &ScVal) -> Result<Self, Self::Error> {
157 Ok(
158 AddressObject::try_from_val(env, &Val::try_from_val(env, val)?)?
159 .try_into_val(env)
160 .unwrap_infallible(),
161 )
162 }
163}
164
165#[cfg(not(target_family = "wasm"))]
166impl From<&Address> for ScAddress {
167 fn from(v: &Address) -> Self {
168 match ScVal::try_from_val(&v.env, &v.obj.to_val()).unwrap() {
169 ScVal::Address(a) => a,
170 _ => panic!("expected ScVal::Address"),
171 }
172 }
173}
174
175#[cfg(not(target_family = "wasm"))]
176impl From<Address> for ScAddress {
177 fn from(v: Address) -> Self {
178 (&v).into()
179 }
180}
181
182#[cfg(not(target_family = "wasm"))]
183impl TryFromVal<Env, ScAddress> for Address {
184 type Error = ConversionError;
185 fn try_from_val(env: &Env, val: &ScAddress) -> Result<Self, Self::Error> {
186 Ok(AddressObject::try_from_val(
187 env,
188 &Val::try_from_val(env, &ScVal::Address(val.clone()))?,
189 )?
190 .try_into_val(env)
191 .unwrap_infallible())
192 }
193}
194
195impl Address {
196 pub fn require_auth_for_args(&self, args: Vec<Val>) {
214 self.env.require_auth_for_args(self, args);
215 }
216
217 pub fn require_auth(&self) {
232 self.env.require_auth(self);
233 }
234
235 pub fn from_str(env: &Env, strkey: &str) -> Address {
244 Address::from_string(&String::from_str(env, strkey))
245 }
246
247 pub fn from_string(strkey: &String) -> Self {
256 let env = strkey.env();
257 unsafe {
258 Self::unchecked_new(
259 env.clone(),
260 env.strkey_to_address(strkey.to_object().to_val())
261 .unwrap_infallible(),
262 )
263 }
264 }
265
266 pub fn from_string_bytes(strkey: &Bytes) -> Self {
278 let env = strkey.env();
279 unsafe {
280 Self::unchecked_new(
281 env.clone(),
282 env.strkey_to_address(strkey.to_object().to_val())
283 .unwrap_infallible(),
284 )
285 }
286 }
287
288 pub fn to_string(&self) -> String {
289 String::try_from_val(
290 &self.env,
291 &self.env.address_to_strkey(self.obj).unwrap_infallible(),
292 )
293 .unwrap_optimized()
294 }
295
296 #[inline(always)]
297 pub(crate) unsafe fn unchecked_new(env: Env, obj: AddressObject) -> Self {
298 Self { env, obj }
299 }
300
301 #[inline(always)]
302 pub fn env(&self) -> &Env {
303 &self.env
304 }
305
306 pub fn as_val(&self) -> &Val {
307 self.obj.as_val()
308 }
309
310 pub fn to_val(&self) -> Val {
311 self.obj.to_val()
312 }
313
314 pub fn as_object(&self) -> &AddressObject {
315 &self.obj
316 }
317
318 pub fn to_object(&self) -> AddressObject {
319 self.obj
320 }
321}
322
323#[cfg(any(not(target_family = "wasm"), test, feature = "testutils"))]
324use crate::env::xdr::{ContractId, Hash};
325use crate::unwrap::UnwrapOptimized;
326
327#[cfg(any(test, feature = "testutils"))]
328#[cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
329impl crate::testutils::Address for Address {
330 fn generate(env: &Env) -> Self {
331 Self::try_from_val(
332 env,
333 &ScAddress::Contract(ContractId(Hash(env.with_generator(|mut g| g.address())))),
334 )
335 .unwrap()
336 }
337}
338
339#[cfg(not(target_family = "wasm"))]
340impl Address {
341 pub(crate) fn contract_id(&self) -> ContractId {
342 let sc_address: ScAddress = self.try_into().unwrap();
343 if let ScAddress::Contract(c) = sc_address {
344 c
345 } else {
346 panic!("address is not a contract {:?}", self);
347 }
348 }
349
350 pub(crate) fn from_contract_id(env: &Env, contract_id: [u8; 32]) -> Self {
351 Self::try_from_val(env, &ScAddress::Contract(ContractId(Hash(contract_id)))).unwrap()
352 }
353}