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(not(target_family = "wasm"))]
9use crate::env::internal::xdr::{AccountId, ScVal};
10#[cfg(any(test, feature = "testutils", not(target_family = "wasm")))]
11use crate::env::xdr::ScAddress;
12
13#[derive(Clone)]
30pub struct Address {
31 env: Env,
32 obj: AddressObject,
33}
34
35impl Debug for Address {
36 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37 #[cfg(target_family = "wasm")]
38 write!(f, "Address(..)")?;
39 #[cfg(not(target_family = "wasm"))]
40 {
41 use crate::env::internal::xdr;
42 use stellar_strkey::{ed25519, Contract, Strkey};
43 let sc_val = ScVal::try_from(self).map_err(|_| core::fmt::Error)?;
44 if let ScVal::Address(addr) = sc_val {
45 match addr {
46 xdr::ScAddress::Account(account_id) => {
47 let xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(xdr::Uint256(
48 ed25519,
49 ))) = account_id;
50 let strkey = Strkey::PublicKeyEd25519(ed25519::PublicKey(ed25519));
51 write!(f, "AccountId({})", strkey.to_string())?;
52 }
53 xdr::ScAddress::Contract(xdr::ContractId(contract_id)) => {
54 let strkey = Strkey::Contract(Contract(contract_id.0));
55 write!(f, "Contract({})", strkey.to_string())?;
56 }
57 ScAddress::MuxedAccount(_)
58 | ScAddress::ClaimableBalance(_)
59 | ScAddress::LiquidityPool(_) => {
60 return Err(core::fmt::Error);
61 }
62 }
63 } else {
64 return Err(core::fmt::Error);
65 }
66 }
67 Ok(())
68 }
69}
70
71impl Eq for Address {}
72
73impl PartialEq for Address {
74 fn eq(&self, other: &Self) -> bool {
75 self.partial_cmp(other) == Some(Ordering::Equal)
76 }
77}
78
79impl PartialOrd for Address {
80 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
81 Some(Ord::cmp(self, other))
82 }
83}
84
85impl Ord for Address {
86 fn cmp(&self, other: &Self) -> Ordering {
87 #[cfg(not(target_family = "wasm"))]
88 if !self.env.is_same_env(&other.env) {
89 return ScVal::from(self).cmp(&ScVal::from(other));
90 }
91 let v = self
92 .env
93 .obj_cmp(self.obj.to_val(), other.obj.to_val())
94 .unwrap_infallible();
95 v.cmp(&0)
96 }
97}
98
99impl TryFromVal<Env, AddressObject> for Address {
100 type Error = Infallible;
101
102 fn try_from_val(env: &Env, val: &AddressObject) -> Result<Self, Self::Error> {
103 Ok(unsafe { Address::unchecked_new(env.clone(), *val) })
104 }
105}
106
107impl TryFromVal<Env, Val> for Address {
108 type Error = ConversionError;
109
110 fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
111 Ok(AddressObject::try_from_val(env, val)?
112 .try_into_val(env)
113 .unwrap_infallible())
114 }
115}
116
117impl TryFromVal<Env, Address> for Val {
118 type Error = ConversionError;
119
120 fn try_from_val(_env: &Env, v: &Address) -> Result<Self, Self::Error> {
121 Ok(v.to_val())
122 }
123}
124
125impl TryFromVal<Env, &Address> for Val {
126 type Error = ConversionError;
127
128 fn try_from_val(_env: &Env, v: &&Address) -> Result<Self, Self::Error> {
129 Ok(v.to_val())
130 }
131}
132
133#[cfg(not(target_family = "wasm"))]
134impl From<&Address> for ScVal {
135 fn from(v: &Address) -> Self {
136 ScVal::try_from_val(&v.env, &v.obj.to_val()).unwrap()
142 }
143}
144
145#[cfg(not(target_family = "wasm"))]
146impl From<Address> for ScVal {
147 fn from(v: Address) -> Self {
148 (&v).into()
149 }
150}
151
152#[cfg(not(target_family = "wasm"))]
153impl TryFromVal<Env, ScVal> for Address {
154 type Error = ConversionError;
155 fn try_from_val(env: &Env, val: &ScVal) -> Result<Self, Self::Error> {
156 Ok(
157 AddressObject::try_from_val(env, &Val::try_from_val(env, val)?)?
158 .try_into_val(env)
159 .unwrap_infallible(),
160 )
161 }
162}
163
164#[cfg(not(target_family = "wasm"))]
165impl From<&Address> for ScAddress {
166 fn from(v: &Address) -> Self {
167 match ScVal::try_from_val(&v.env, &v.obj.to_val()).unwrap() {
168 ScVal::Address(a) => a,
169 _ => panic!("expected ScVal::Address"),
170 }
171 }
172}
173
174#[cfg(not(target_family = "wasm"))]
175impl From<Address> for ScAddress {
176 fn from(v: Address) -> Self {
177 (&v).into()
178 }
179}
180
181#[cfg(not(target_family = "wasm"))]
182impl TryFromVal<Env, ScAddress> for Address {
183 type Error = ConversionError;
184 fn try_from_val(env: &Env, val: &ScAddress) -> Result<Self, Self::Error> {
185 Ok(AddressObject::try_from_val(
186 env,
187 &Val::try_from_val(env, &ScVal::Address(val.clone()))?,
188 )?
189 .try_into_val(env)
190 .unwrap_infallible())
191 }
192}
193
194#[cfg(not(target_family = "wasm"))]
195impl TryFrom<&Address> for AccountId {
196 type Error = ConversionError;
197 fn try_from(v: &Address) -> Result<Self, Self::Error> {
198 let sc: ScAddress = v.into();
199 match sc {
200 ScAddress::Account(aid) => Ok(aid),
201 _ => Err(ConversionError),
202 }
203 }
204}
205
206#[cfg(not(target_family = "wasm"))]
207impl TryFrom<Address> for AccountId {
208 type Error = ConversionError;
209 fn try_from(v: Address) -> Result<Self, Self::Error> {
210 (&v).try_into()
211 }
212}
213
214#[contracttype(crate_path = "crate", export = false)]
215#[derive(Clone, Debug, PartialEq, Eq)]
216pub enum Executable {
217 Wasm(BytesN<32>),
218 StellarAsset,
219 Account,
220}
221
222impl Address {
223 pub fn require_auth_for_args(&self, args: Vec<Val>) {
241 self.env.require_auth_for_args(self, args);
242 }
243
244 pub fn require_auth(&self) {
259 self.env.require_auth(self);
260 }
261
262 pub fn from_str(env: &Env, strkey: &str) -> Address {
271 Address::from_string(&String::from_str(env, strkey))
272 }
273
274 pub fn from_string(strkey: &String) -> Self {
283 let env = strkey.env();
284 unsafe {
285 Self::unchecked_new(
286 env.clone(),
287 env.strkey_to_address(strkey.to_object().to_val())
288 .unwrap_infallible(),
289 )
290 }
291 }
292
293 pub fn from_string_bytes(strkey: &Bytes) -> Self {
305 let env = strkey.env();
306 unsafe {
307 Self::unchecked_new(
308 env.clone(),
309 env.strkey_to_address(strkey.to_object().to_val())
310 .unwrap_infallible(),
311 )
312 }
313 }
314
315 pub fn executable(&self) -> Option<Executable> {
323 let executable_val: Val =
324 Env::get_address_executable(&self.env, self.obj).unwrap_infallible();
325 executable_val.into_val(&self.env)
326 }
327
328 pub fn exists(&self) -> bool {
334 let executable_val: Val =
335 Env::get_address_executable(&self.env, self.obj).unwrap_infallible();
336 !executable_val.is_void()
337 }
338
339 pub fn to_string(&self) -> String {
341 String::try_from_val(
342 &self.env,
343 &self.env.address_to_strkey(self.obj).unwrap_infallible(),
344 )
345 .unwrap_optimized()
346 }
347
348 #[inline(always)]
349 pub(crate) unsafe fn unchecked_new(env: Env, obj: AddressObject) -> Self {
350 Self { env, obj }
351 }
352
353 #[inline(always)]
354 pub fn env(&self) -> &Env {
355 &self.env
356 }
357
358 pub fn as_val(&self) -> &Val {
359 self.obj.as_val()
360 }
361
362 pub fn to_val(&self) -> Val {
363 self.obj.to_val()
364 }
365
366 pub fn as_object(&self) -> &AddressObject {
367 &self.obj
368 }
369
370 pub fn to_object(&self) -> AddressObject {
371 self.obj
372 }
373}
374
375#[cfg(any(not(target_family = "wasm"), test, feature = "testutils"))]
376use crate::env::xdr::{ContractId, Hash};
377use crate::unwrap::UnwrapOptimized;
378
379#[cfg(any(test, feature = "testutils"))]
380#[cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
381impl crate::testutils::Address for Address {
382 fn generate(env: &Env) -> Self {
383 Self::try_from_val(
384 env,
385 &ScAddress::Contract(ContractId(Hash(env.with_generator(|mut g| g.address())))),
386 )
387 .unwrap()
388 }
389}
390
391#[cfg(not(target_family = "wasm"))]
392impl Address {
393 pub(crate) fn contract_id(&self) -> ContractId {
394 let sc_address: ScAddress = self.try_into().unwrap();
395 if let ScAddress::Contract(c) = sc_address {
396 c
397 } else {
398 panic!("address is not a contract {:?}", self);
399 }
400 }
401
402 pub(crate) fn from_contract_id(env: &Env, contract_id: [u8; 32]) -> Self {
403 Self::try_from_val(env, &ScAddress::Contract(ContractId(Hash(contract_id)))).unwrap()
404 }
405}