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(contract_id) => {
55 let strkey = Strkey::Contract(Contract(contract_id.0));
56 write!(f, "Contract({})", strkey.to_string())?;
57 }
58 }
59 } else {
60 return Err(core::fmt::Error);
61 }
62 }
63 Ok(())
64 }
65}
66
67impl Eq for Address {}
68
69impl PartialEq for Address {
70 fn eq(&self, other: &Self) -> bool {
71 self.partial_cmp(other) == Some(Ordering::Equal)
72 }
73}
74
75impl PartialOrd for Address {
76 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
77 Some(Ord::cmp(self, other))
78 }
79}
80
81impl Ord for Address {
82 fn cmp(&self, other: &Self) -> Ordering {
83 #[cfg(not(target_family = "wasm"))]
84 if !self.env.is_same_env(&other.env) {
85 return ScVal::from(self).cmp(&ScVal::from(other));
86 }
87 let v = self
88 .env
89 .obj_cmp(self.obj.to_val(), other.obj.to_val())
90 .unwrap_infallible();
91 v.cmp(&0)
92 }
93}
94
95impl TryFromVal<Env, AddressObject> for Address {
96 type Error = Infallible;
97
98 fn try_from_val(env: &Env, val: &AddressObject) -> Result<Self, Self::Error> {
99 Ok(unsafe { Address::unchecked_new(env.clone(), *val) })
100 }
101}
102
103impl TryFromVal<Env, Val> for Address {
104 type Error = ConversionError;
105
106 fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
107 Ok(AddressObject::try_from_val(env, val)?
108 .try_into_val(env)
109 .unwrap_infallible())
110 }
111}
112
113impl TryFromVal<Env, Address> for Val {
114 type Error = ConversionError;
115
116 fn try_from_val(_env: &Env, v: &Address) -> Result<Self, Self::Error> {
117 Ok(v.to_val())
118 }
119}
120
121impl TryFromVal<Env, &Address> for Val {
122 type Error = ConversionError;
123
124 fn try_from_val(_env: &Env, v: &&Address) -> Result<Self, Self::Error> {
125 Ok(v.to_val())
126 }
127}
128
129#[cfg(not(target_family = "wasm"))]
130impl From<&Address> for ScVal {
131 fn from(v: &Address) -> Self {
132 ScVal::try_from_val(&v.env, &v.obj.to_val()).unwrap()
138 }
139}
140
141#[cfg(not(target_family = "wasm"))]
142impl From<Address> for ScVal {
143 fn from(v: Address) -> Self {
144 (&v).into()
145 }
146}
147
148#[cfg(not(target_family = "wasm"))]
149impl TryFromVal<Env, ScVal> for Address {
150 type Error = ConversionError;
151 fn try_from_val(env: &Env, val: &ScVal) -> Result<Self, Self::Error> {
152 Ok(
153 AddressObject::try_from_val(env, &Val::try_from_val(env, val)?)?
154 .try_into_val(env)
155 .unwrap_infallible(),
156 )
157 }
158}
159
160#[cfg(not(target_family = "wasm"))]
161impl From<&Address> for ScAddress {
162 fn from(v: &Address) -> Self {
163 match ScVal::try_from_val(&v.env, &v.obj.to_val()).unwrap() {
164 ScVal::Address(a) => a,
165 _ => panic!("expected ScVal::Address"),
166 }
167 }
168}
169
170#[cfg(not(target_family = "wasm"))]
171impl From<Address> for ScAddress {
172 fn from(v: Address) -> Self {
173 (&v).into()
174 }
175}
176
177#[cfg(not(target_family = "wasm"))]
178impl TryFromVal<Env, ScAddress> for Address {
179 type Error = ConversionError;
180 fn try_from_val(env: &Env, val: &ScAddress) -> Result<Self, Self::Error> {
181 Ok(AddressObject::try_from_val(
182 env,
183 &Val::try_from_val(env, &ScVal::Address(val.clone()))?,
184 )?
185 .try_into_val(env)
186 .unwrap_infallible())
187 }
188}
189
190impl Address {
191 pub fn require_auth_for_args(&self, args: Vec<Val>) {
209 self.env.require_auth_for_args(self, args);
210 }
211
212 pub fn require_auth(&self) {
227 self.env.require_auth(self);
228 }
229
230 pub fn from_str(env: &Env, strkey: &str) -> Address {
239 Address::from_string(&String::from_str(env, strkey))
240 }
241
242 pub fn from_string(strkey: &String) -> Self {
251 let env = strkey.env();
252 unsafe {
253 Self::unchecked_new(
254 env.clone(),
255 env.strkey_to_address(strkey.to_object().to_val())
256 .unwrap_infallible(),
257 )
258 }
259 }
260
261 pub fn from_string_bytes(strkey: &Bytes) -> Self {
273 let env = strkey.env();
274 unsafe {
275 Self::unchecked_new(
276 env.clone(),
277 env.strkey_to_address(strkey.to_object().to_val())
278 .unwrap_infallible(),
279 )
280 }
281 }
282
283 pub fn to_string(&self) -> String {
284 String::try_from_val(
285 &self.env,
286 &self.env.address_to_strkey(self.obj).unwrap_infallible(),
287 )
288 .unwrap_optimized()
289 }
290
291 #[inline(always)]
292 pub(crate) unsafe fn unchecked_new(env: Env, obj: AddressObject) -> Self {
293 Self { env, obj }
294 }
295
296 #[inline(always)]
297 pub fn env(&self) -> &Env {
298 &self.env
299 }
300
301 pub fn as_val(&self) -> &Val {
302 self.obj.as_val()
303 }
304
305 pub fn to_val(&self) -> Val {
306 self.obj.to_val()
307 }
308
309 pub fn as_object(&self) -> &AddressObject {
310 &self.obj
311 }
312
313 pub fn to_object(&self) -> AddressObject {
314 self.obj
315 }
316}
317
318#[cfg(any(not(target_family = "wasm"), test, feature = "testutils"))]
319use crate::env::xdr::Hash;
320use crate::unwrap::UnwrapOptimized;
321
322#[cfg(any(test, feature = "testutils"))]
323#[cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
324impl crate::testutils::Address for Address {
325 fn generate(env: &Env) -> Self {
326 Self::try_from_val(
327 env,
328 &ScAddress::Contract(Hash(env.with_generator(|mut g| g.address()))),
329 )
330 .unwrap()
331 }
332}
333
334#[cfg(not(target_family = "wasm"))]
335impl Address {
336 pub(crate) fn contract_id(&self) -> Hash {
337 let sc_address: ScAddress = self.try_into().unwrap();
338 if let ScAddress::Contract(c) = sc_address {
339 c
340 } else {
341 panic!("address is not a contract {:?}", self);
342 }
343 }
344
345 pub(crate) fn from_contract_id(env: &Env, contract_id: [u8; 32]) -> Self {
346 Self::try_from_val(env, &ScAddress::Contract(Hash(contract_id))).unwrap()
347 }
348}