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#[cfg_attr(
218 feature = "experimental_spec_shaking_v2",
219 contracttype(crate_path = "crate")
220)]
221#[cfg_attr(
222 not(feature = "experimental_spec_shaking_v2"),
223 contracttype(crate_path = "crate", export = false)
224)]
225#[derive(Clone, Debug, PartialEq, Eq)]
226pub enum Executable {
227 Wasm(BytesN<32>),
228 StellarAsset,
229 Account,
230}
231
232impl Address {
233 pub fn require_auth_for_args(&self, args: Vec<Val>) {
251 self.env.require_auth_for_args(self, args);
252 }
253
254 pub fn require_auth(&self) {
269 self.env.require_auth(self);
270 }
271
272 pub fn from_str(env: &Env, strkey: &str) -> Address {
281 Address::from_string(&String::from_str(env, strkey))
282 }
283
284 pub fn from_string(strkey: &String) -> Self {
293 let env = strkey.env();
294 unsafe {
295 Self::unchecked_new(
296 env.clone(),
297 env.strkey_to_address(strkey.to_object().to_val())
298 .unwrap_infallible(),
299 )
300 }
301 }
302
303 pub fn from_string_bytes(strkey: &Bytes) -> Self {
315 let env = strkey.env();
316 unsafe {
317 Self::unchecked_new(
318 env.clone(),
319 env.strkey_to_address(strkey.to_object().to_val())
320 .unwrap_infallible(),
321 )
322 }
323 }
324
325 pub fn executable(&self) -> Option<Executable> {
333 let executable_val: Val =
334 Env::get_address_executable(&self.env, self.obj).unwrap_infallible();
335 executable_val.into_val(&self.env)
336 }
337
338 pub fn exists(&self) -> bool {
344 let executable_val: Val =
345 Env::get_address_executable(&self.env, self.obj).unwrap_infallible();
346 !executable_val.is_void()
347 }
348
349 pub fn to_string(&self) -> String {
351 String::try_from_val(
352 &self.env,
353 &self.env.address_to_strkey(self.obj).unwrap_infallible(),
354 )
355 .unwrap_optimized()
356 }
357
358 #[inline(always)]
359 pub(crate) unsafe fn unchecked_new(env: Env, obj: AddressObject) -> Self {
360 Self { env, obj }
361 }
362
363 #[inline(always)]
364 pub fn env(&self) -> &Env {
365 &self.env
366 }
367
368 pub fn as_val(&self) -> &Val {
369 self.obj.as_val()
370 }
371
372 pub fn to_val(&self) -> Val {
373 self.obj.to_val()
374 }
375
376 pub fn as_object(&self) -> &AddressObject {
377 &self.obj
378 }
379
380 pub fn to_object(&self) -> AddressObject {
381 self.obj
382 }
383
384 #[cfg(any(test, feature = "hazmat-address"))]
404 #[cfg_attr(feature = "docs", doc(cfg(feature = "hazmat-address")))]
405 pub fn to_payload(&self) -> Option<AddressPayload> {
406 AddressPayload::from_address(self)
407 }
408
409 #[cfg(any(test, feature = "hazmat-address"))]
421 #[cfg_attr(feature = "docs", doc(cfg(feature = "hazmat-address")))]
422 pub fn from_payload(env: &Env, payload: AddressPayload) -> Address {
423 payload.to_address(env)
424 }
425}
426
427#[cfg(any(not(target_family = "wasm"), test, feature = "testutils"))]
428use crate::env::xdr::{ContractId, Hash};
429use crate::unwrap::UnwrapOptimized;
430
431#[cfg(any(test, feature = "testutils"))]
432#[cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
433impl crate::testutils::Address for Address {
434 fn generate(env: &Env) -> Self {
435 Self::try_from_val(
436 env,
437 &ScAddress::Contract(ContractId(Hash(env.with_generator(|mut g| g.address())))),
438 )
439 .unwrap()
440 }
441}
442
443#[cfg(not(target_family = "wasm"))]
444impl Address {
445 pub(crate) fn contract_id(&self) -> ContractId {
446 let sc_address: ScAddress = self.try_into().unwrap();
447 if let ScAddress::Contract(c) = sc_address {
448 c
449 } else {
450 panic!("address is not a contract {:?}", self);
451 }
452 }
453
454 pub(crate) fn from_contract_id(env: &Env, contract_id: [u8; 32]) -> Self {
455 Self::try_from_val(env, &ScAddress::Contract(ContractId(Hash(contract_id)))).unwrap()
456 }
457}