1use std::fmt;
4use std::str::FromStr;
5
6use hedera_proto::services;
7
8use crate::entity_id::{
9 Checksum,
10 PartialEntityId,
11};
12use crate::ethereum::SolidityAddress;
13use crate::protobuf::{
14 FromProtobuf,
15 ToProtobuf,
16};
17use crate::{
18 EntityId,
19 Error,
20};
21
22#[derive(Hash, PartialEq, Eq, Clone, Copy)]
24pub struct DelegateContractId {
25 pub shard: u64,
27
28 pub realm: u64,
30
31 pub num: u64,
35
36 pub checksum: Option<Checksum>,
38
39 pub evm_address: Option<[u8; 20]>,
43}
44
45impl DelegateContractId {
46 #[must_use]
48 pub fn new(shard: u64, realm: u64, num: u64) -> Self {
49 Self { shard, realm, num, evm_address: None, checksum: None }
50 }
51
52 pub fn from_solidity_address(address: &str) -> crate::Result<Self> {
57 let EntityId { shard, realm, num, checksum } = EntityId::from_solidity_address(address)?;
58
59 Ok(Self { shard, realm, num, evm_address: None, checksum })
60 }
61}
62
63impl fmt::Debug for DelegateContractId {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(f, "\"{self}\"")
66 }
67}
68
69impl fmt::Display for DelegateContractId {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 if let Some(address) = &self.evm_address {
72 write!(f, "{}.{}.{}", self.shard, self.realm, SolidityAddress::from_ref(address))
73 } else {
74 write!(f, "{}.{}.{}", self.shard, self.realm, self.num)
75 }
76 }
77}
78
79impl FromStr for DelegateContractId {
80 type Err = Error;
81
82 fn from_str(s: &str) -> Result<Self, Self::Err> {
83 let partial = PartialEntityId::from_str(s).map_err(|_| {
85 Error::basic_parse(format!(
86 "expecting <shard>.<realm>.<num> or <shard>.<realm>.<evm_address>, got `{s}`"
87 ))
88 })?;
89
90 match partial {
91 PartialEntityId::ShortNum(it) => Ok(Self::from(it)),
92 PartialEntityId::LongNum(it) => Ok(Self::from(it)),
93 PartialEntityId::ShortOther(_) => Err(Error::basic_parse(format!(
94 "expecting <shard>.<realm>.<num> or <shard>.<realm>.<evm_address>, got `{s}`"
95 ))),
96 PartialEntityId::LongOther { shard, realm, last } => {
97 let evm_address = Some(SolidityAddress::from_str(last)?.to_bytes());
98
99 Ok(Self { shard, realm, num: 0, evm_address, checksum: None })
100 }
101 }
102 }
103}
104
105impl From<[u8; 20]> for DelegateContractId {
106 fn from(address: [u8; 20]) -> Self {
107 Self { shard: 0, realm: 0, num: 0, evm_address: Some(address), checksum: None }
108 }
109}
110
111impl From<u64> for DelegateContractId {
112 fn from(num: u64) -> Self {
113 Self::new(0, 0, num)
114 }
115}
116
117impl From<EntityId> for DelegateContractId {
118 fn from(value: EntityId) -> Self {
119 let EntityId { shard, realm, num, checksum } = value;
120
121 Self { shard, realm, num, evm_address: None, checksum }
122 }
123}
124
125impl FromProtobuf<services::ContractId> for DelegateContractId {
126 fn from_protobuf(pb: services::ContractId) -> crate::Result<Self> {
127 let contract = pb_getf!(pb, contract)?;
128
129 let (num, evm_address) = match contract {
130 services::contract_id::Contract::ContractNum(it) => (it as u64, None),
131 services::contract_id::Contract::EvmAddress(it) => {
132 (0, Some(SolidityAddress::try_from(it)?.to_bytes()))
133 }
134 };
135
136 Ok(Self {
137 evm_address,
138 num,
139 shard: pb.shard_num as u64,
140 realm: pb.realm_num as u64,
141 checksum: None,
142 })
143 }
144}
145
146impl ToProtobuf for DelegateContractId {
147 type Protobuf = services::ContractId;
148
149 fn to_protobuf(&self) -> Self::Protobuf {
150 services::ContractId {
151 contract: Some(match &self.evm_address {
152 Some(address) => services::contract_id::Contract::EvmAddress(address.to_vec()),
153 None => services::contract_id::Contract::ContractNum(self.num as i64),
154 }),
155 realm_num: self.realm as i64,
156 shard_num: self.shard as i64,
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use std::str::FromStr;
164
165 use expect_test::expect;
166
167 use crate::protobuf::{
168 FromProtobuf,
169 ToProtobuf,
170 };
171 use crate::DelegateContractId;
172
173 #[test]
174 fn from_string() {
175 expect!["0.0.5005"]
176 .assert_eq(&DelegateContractId::from_str("0.0.5005").unwrap().to_string());
177 }
178
179 #[test]
180 fn from_solidity_address() {
181 expect!["0.0.5005"].assert_eq(
182 &DelegateContractId::from_solidity_address("000000000000000000000000000000000000138D")
183 .unwrap()
184 .to_string(),
185 );
186 }
187
188 #[test]
189 fn from_solidity_address_with_0x() {
190 expect!["0.0.5005"].assert_eq(
191 &DelegateContractId::from_solidity_address(
192 "0x000000000000000000000000000000000000138D",
193 )
194 .unwrap()
195 .to_string(),
196 );
197 }
198
199 #[test]
200 fn from_bytes() {
201 expect!["0.0.5005"].assert_eq(
202 &DelegateContractId::from_bytes(&DelegateContractId::new(0, 0, 5005).to_bytes())
203 .unwrap()
204 .to_string(),
205 );
206 }
207}