1use core::{cmp::Ordering, convert::Infallible, fmt::Debug};
2
3use super::{
4 contracttype, env::internal::AddressObject, env::internal::Env as _, unwrap::UnwrapInfallible,
5 Bytes, BytesN, ConversionError, Env, IntoVal, String, TryFromVal, TryIntoVal, Val, Vec,
6};
7
8#[cfg(any(test, feature = "hazmat-address"))]
9use crate::address_payload::AddressPayload;
10
11#[cfg(not(target_family = "wasm"))]
12use crate::env::internal::xdr::{AccountId, ScVal};
13#[cfg(any(test, feature = "testutils", not(target_family = "wasm")))]
14use crate::env::xdr::ScAddress;
15
16#[derive(Clone)]
33pub struct Address {
34 env: Env,
35 obj: AddressObject,
36}
37
38impl Debug for Address {
39 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40 #[cfg(target_family = "wasm")]
41 write!(f, "Address(..)")?;
42 #[cfg(not(target_family = "wasm"))]
43 {
44 use crate::env::internal::xdr;
45 use stellar_strkey::{ed25519, Contract, Strkey};
46 let sc_val = ScVal::try_from(self).map_err(|_| core::fmt::Error)?;
47 if let ScVal::Address(addr) = sc_val {
48 match addr {
49 xdr::ScAddress::Account(account_id) => {
50 let xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(xdr::Uint256(
51 ed25519,
52 ))) = account_id;
53 let strkey = Strkey::PublicKeyEd25519(ed25519::PublicKey(ed25519));
54 write!(f, "AccountId({})", strkey.to_string())?;
55 }
56 xdr::ScAddress::Contract(xdr::ContractId(contract_id)) => {
57 let strkey = Strkey::Contract(Contract(contract_id.0));
58 write!(f, "Contract({})", strkey.to_string())?;
59 }
60 ScAddress::MuxedAccount(_)
61 | ScAddress::ClaimableBalance(_)
62 | ScAddress::LiquidityPool(_) => {
63 return Err(core::fmt::Error);
64 }
65 }
66 } else {
67 return Err(core::fmt::Error);
68 }
69 }
70 Ok(())
71 }
72}
73
74impl Eq for Address {}
75
76impl PartialEq for Address {
77 fn eq(&self, other: &Self) -> bool {
78 self.partial_cmp(other) == Some(Ordering::Equal)
79 }
80}
81
82impl PartialOrd for Address {
83 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
84 Some(Ord::cmp(self, other))
85 }
86}
87
88impl Ord for Address {
89 fn cmp(&self, other: &Self) -> Ordering {
90 #[cfg(not(target_family = "wasm"))]
91 if !self.env.is_same_env(&other.env) {
92 return ScVal::from(self).cmp(&ScVal::from(other));
93 }
94 let v = self
95 .env
96 .obj_cmp(self.obj.to_val(), other.obj.to_val())
97 .unwrap_infallible();
98 v.cmp(&0)
99 }
100}
101
102impl TryFromVal<Env, AddressObject> for Address {
103 type Error = Infallible;
104
105 fn try_from_val(env: &Env, val: &AddressObject) -> Result<Self, Self::Error> {
106 Ok(unsafe { Address::unchecked_new(env.clone(), *val) })
107 }
108}
109
110impl TryFromVal<Env, Val> for Address {
111 type Error = ConversionError;
112
113 fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
114 Ok(AddressObject::try_from_val(env, val)?
115 .try_into_val(env)
116 .unwrap_infallible())
117 }
118}
119
120impl TryFromVal<Env, Address> for Val {
121 type Error = ConversionError;
122
123 fn try_from_val(_env: &Env, v: &Address) -> Result<Self, Self::Error> {
124 Ok(v.to_val())
125 }
126}
127
128impl TryFromVal<Env, &Address> for Val {
129 type Error = ConversionError;
130
131 fn try_from_val(_env: &Env, v: &&Address) -> Result<Self, Self::Error> {
132 Ok(v.to_val())
133 }
134}
135
136#[cfg(not(target_family = "wasm"))]
137impl From<&Address> for ScVal {
138 fn from(v: &Address) -> Self {
139 ScVal::try_from_val(&v.env, &v.obj.to_val()).unwrap()
145 }
146}
147
148#[cfg(not(target_family = "wasm"))]
149impl From<Address> for ScVal {
150 fn from(v: Address) -> Self {
151 (&v).into()
152 }
153}
154
155#[cfg(not(target_family = "wasm"))]
156impl TryFromVal<Env, ScVal> for Address {
157 type Error = ConversionError;
158 fn try_from_val(env: &Env, val: &ScVal) -> Result<Self, Self::Error> {
159 Ok(
160 AddressObject::try_from_val(env, &Val::try_from_val(env, val)?)?
161 .try_into_val(env)
162 .unwrap_infallible(),
163 )
164 }
165}
166
167#[cfg(not(target_family = "wasm"))]
168impl From<&Address> for ScAddress {
169 fn from(v: &Address) -> Self {
170 match ScVal::try_from_val(&v.env, &v.obj.to_val()).unwrap() {
171 ScVal::Address(a) => a,
172 _ => panic!("expected ScVal::Address"),
173 }
174 }
175}
176
177#[cfg(not(target_family = "wasm"))]
178impl From<Address> for ScAddress {
179 fn from(v: Address) -> Self {
180 (&v).into()
181 }
182}
183
184#[cfg(not(target_family = "wasm"))]
185impl TryFromVal<Env, ScAddress> for Address {
186 type Error = ConversionError;
187 fn try_from_val(env: &Env, val: &ScAddress) -> Result<Self, Self::Error> {
188 Ok(AddressObject::try_from_val(
189 env,
190 &Val::try_from_val(env, &ScVal::Address(val.clone()))?,
191 )?
192 .try_into_val(env)
193 .unwrap_infallible())
194 }
195}
196
197#[cfg(not(target_family = "wasm"))]
198impl TryFrom<&Address> for AccountId {
199 type Error = ConversionError;
200 fn try_from(v: &Address) -> Result<Self, Self::Error> {
201 let sc: ScAddress = v.into();
202 match sc {
203 ScAddress::Account(aid) => Ok(aid),
204 _ => Err(ConversionError),
205 }
206 }
207}
208
209#[cfg(not(target_family = "wasm"))]
210impl TryFrom<Address> for AccountId {
211 type Error = ConversionError;
212 fn try_from(v: Address) -> Result<Self, Self::Error> {
213 (&v).try_into()
214 }
215}
216
217#[contracttype(crate_path = "crate", export = false)]
218#[derive(Clone, Debug, PartialEq, Eq)]
219pub enum Executable {
220 Wasm(BytesN<32>),
221 StellarAsset,
222 Account,
223}
224
225impl Address {
226 pub fn require_auth_for_args(&self, args: Vec<Val>) {
244 self.env.require_auth_for_args(self, args);
245 }
246
247 pub fn require_auth(&self) {
262 self.env.require_auth(self);
263 }
264
265 pub fn from_str(env: &Env, strkey: &str) -> Address {
274 Address::from_string(&String::from_str(env, strkey))
275 }
276
277 pub fn from_string(strkey: &String) -> Self {
286 let env = strkey.env();
287 unsafe {
288 Self::unchecked_new(
289 env.clone(),
290 env.strkey_to_address(strkey.to_object().to_val())
291 .unwrap_infallible(),
292 )
293 }
294 }
295
296 pub fn from_string_bytes(strkey: &Bytes) -> Self {
308 let env = strkey.env();
309 unsafe {
310 Self::unchecked_new(
311 env.clone(),
312 env.strkey_to_address(strkey.to_object().to_val())
313 .unwrap_infallible(),
314 )
315 }
316 }
317
318 pub fn executable(&self) -> Option<Executable> {
326 let executable_val: Val =
327 Env::get_address_executable(&self.env, self.obj).unwrap_infallible();
328 executable_val.into_val(&self.env)
329 }
330
331 pub fn exists(&self) -> bool {
337 let executable_val: Val =
338 Env::get_address_executable(&self.env, self.obj).unwrap_infallible();
339 !executable_val.is_void()
340 }
341
342 pub fn to_string(&self) -> String {
344 String::try_from_val(
345 &self.env,
346 &self.env.address_to_strkey(self.obj).unwrap_infallible(),
347 )
348 .unwrap_optimized()
349 }
350
351 #[inline(always)]
352 pub(crate) unsafe fn unchecked_new(env: Env, obj: AddressObject) -> Self {
353 Self { env, obj }
354 }
355
356 #[inline(always)]
357 pub fn env(&self) -> &Env {
358 &self.env
359 }
360
361 pub fn as_val(&self) -> &Val {
362 self.obj.as_val()
363 }
364
365 pub fn to_val(&self) -> Val {
366 self.obj.to_val()
367 }
368
369 pub fn as_object(&self) -> &AddressObject {
370 &self.obj
371 }
372
373 pub fn to_object(&self) -> AddressObject {
374 self.obj
375 }
376
377 #[cfg(any(test, feature = "hazmat-address"))]
397 #[cfg_attr(
398 feature = "docs",
399 doc(cfg(any(feature = "hazmat", feature = "hazmat-address")))
400 )]
401 pub fn to_payload(&self) -> Option<AddressPayload> {
402 AddressPayload::from_address(self)
403 }
404
405 #[cfg(any(test, feature = "hazmat-address"))]
417 #[cfg_attr(
418 feature = "docs",
419 doc(cfg(any(feature = "hazmat", feature = "hazmat-address")))
420 )]
421 pub fn from_payload(env: &Env, payload: AddressPayload) -> Address {
422 payload.to_address(env)
423 }
424}
425
426#[cfg(any(not(target_family = "wasm"), test, feature = "testutils"))]
427use crate::env::xdr::{ContractId, Hash};
428use crate::unwrap::UnwrapOptimized;
429
430#[cfg(any(test, feature = "testutils"))]
431#[cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
432impl crate::testutils::Address for Address {
433 fn generate(env: &Env) -> Self {
434 Self::try_from_val(
435 env,
436 &ScAddress::Contract(ContractId(Hash(env.with_generator(|mut g| g.address())))),
437 )
438 .unwrap()
439 }
440}
441
442#[cfg(not(target_family = "wasm"))]
443impl Address {
444 pub(crate) fn contract_id(&self) -> ContractId {
445 let sc_address: ScAddress = self.try_into().unwrap();
446 if let ScAddress::Contract(c) = sc_address {
447 c
448 } else {
449 panic!("address is not a contract {:?}", self);
450 }
451 }
452
453 pub(crate) fn from_contract_id(env: &Env, contract_id: [u8; 32]) -> Self {
454 Self::try_from_val(env, &ScAddress::Contract(ContractId(Hash(contract_id)))).unwrap()
455 }
456}