1use crate::{
4 types::{self as sdk_types, smart_contracts::ContractContext},
5 v2::{BlockIdentifier, QueryResponse},
6};
7use concordium_base::contracts_common::{
8 Amount, ContractName, EntrypointName, OwnedReceiveName, ParseError,
9};
10use sdk_types::{smart_contracts, ContractAddress};
11use smart_contracts::concordium_contracts_common as contracts_common;
12use thiserror::*;
13
14#[derive(Debug, Clone)]
16pub enum SupportResult {
17 NoSupport,
19 Support,
21 SupportBy(Vec<ContractAddress>),
23}
24
25impl SupportResult {
26 pub fn is_support(&self) -> bool {
28 matches!(self, &Self::Support)
29 }
30}
31
32impl contracts_common::Serial for SupportResult {
33 fn serial<W: contracts_common::Write>(&self, out: &mut W) -> Result<(), W::Err> {
34 match self {
35 SupportResult::NoSupport => out.write_u8(0u8),
36 SupportResult::Support => out.write_u8(1u8),
37 SupportResult::SupportBy(addrs) => {
38 out.write_u8(2)?;
39 out.write_u8(addrs.len() as u8)?;
40 for addr in addrs {
41 addr.serial(out)?;
42 }
43 Ok(())
44 }
45 }
46 }
47}
48
49impl contracts_common::Deserial for SupportResult {
50 fn deserial<R: contracts_common::Read>(source: &mut R) -> contracts_common::ParseResult<Self> {
51 match source.read_u8()? {
52 0u8 => Ok(Self::NoSupport),
53 1u8 => Ok(Self::Support),
54 2u8 => {
55 let len = source.read_u8()?;
56 let mut out = Vec::new();
57 for _ in 0..len {
58 out.push(ContractAddress::deserial(source)?);
59 }
60 Ok(Self::SupportBy(out))
61 }
62 _ => Err(contracts_common::ParseError {}),
63 }
64 }
65}
66
67#[derive(Debug, Clone)]
71pub struct SupportsQueryResponse {
72 pub results: Vec<SupportResult>,
74}
75
76impl contracts_common::Deserial for SupportsQueryResponse {
77 fn deserial<R: contracts_common::Read>(source: &mut R) -> contracts_common::ParseResult<Self> {
78 let len = u16::deserial(source)?;
79 let mut results = Vec::new();
80 for _ in 0..len {
81 results.push(SupportResult::deserial(source)?)
82 }
83 Ok(Self { results })
84 }
85}
86
87impl contracts_common::Serial for SupportsQueryResponse {
88 fn serial<W: contracts_common::Write>(&self, out: &mut W) -> Result<(), W::Err> {
89 (self.results.len() as u16).serial(out)?;
90 for result in &self.results {
91 result.serial(out)?;
92 }
93 Ok(())
94 }
95}
96
97#[non_exhaustive]
99#[derive(Clone, Debug)]
100pub enum StandardIdentifier {
101 CIS0,
102 CIS1,
103 CIS2,
104 CIS3,
105 CIS4,
106 Other(String),
107}
108
109impl std::fmt::Display for StandardIdentifier {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 match self {
112 StandardIdentifier::CIS0 => f.write_str("CIS-0"),
113 StandardIdentifier::CIS1 => f.write_str("CIS-1"),
114 StandardIdentifier::CIS2 => f.write_str("CIS-2"),
115 StandardIdentifier::CIS3 => f.write_str("CIS-3"),
116 StandardIdentifier::CIS4 => f.write_str("CIS-4"),
117 StandardIdentifier::Other(s) => f.write_str(s),
118 }
119 }
120}
121
122#[derive(Error, Debug, Clone)]
123#[error("Invalid CIS standard name.")]
125pub struct InvalidStandardName;
126
127impl std::str::FromStr for StandardIdentifier {
128 type Err = InvalidStandardName;
129
130 fn from_str(s: &str) -> Result<Self, Self::Err> {
131 match s {
132 "CIS-0" => Ok(Self::CIS0),
133 "CIS-1" => Ok(Self::CIS1),
134 "CIS-2" => Ok(Self::CIS2),
135 "CIS-3" => Ok(Self::CIS3),
136 "CIS-4" => Ok(Self::CIS4),
137 other if other.is_ascii() && other.len() <= 255 => Ok(Self::Other(other.into())),
138 _ => Err(InvalidStandardName),
139 }
140 }
141}
142
143impl contracts_common::Serial for StandardIdentifier {
144 fn serial<W: contracts_common::Write>(&self, out: &mut W) -> Result<(), W::Err> {
145 match self {
146 StandardIdentifier::CIS0 => {
147 out.write_u8(5)?; out.write_all(b"CIS-0")
149 }
150 StandardIdentifier::CIS1 => {
151 out.write_u8(5)?; out.write_all(b"CIS-1")
153 }
154 StandardIdentifier::CIS2 => {
155 out.write_u8(5)?; out.write_all(b"CIS-2")
157 }
158 StandardIdentifier::CIS3 => {
159 out.write_u8(5)?; out.write_all(b"CIS-3")
161 }
162 StandardIdentifier::CIS4 => {
163 out.write_u8(5)?; out.write_all(b"CIS-4")
165 }
166 StandardIdentifier::Other(s) => {
167 out.write_u8(s.len() as u8)?;
168 out.write_all(s.as_bytes())
169 }
170 }
171 }
172}
173
174#[derive(Error, Debug)]
175pub enum SupportsError {
177 #[error("The name of the contract is not valid and thus the contract does not support CIS-0.")]
178 ContractNameInvalid,
179 #[error("Parameter size exceeds maximum allowed. Too many ids.")]
180 InvalidParameter,
181 #[error("Query error: {0}")]
182 QueryError(#[from] super::v2::QueryError),
183 #[error("Contract reject.")]
184 ContractReject,
185 #[error("No return. This is a V0 contract, and V0 contracts do not support CIS-0.")]
186 NoReturn,
187 #[error("Parsing result failed.")]
188 ParseError(#[from] ParseError),
189 #[error("The contract return an inconsistent result.")]
190 InvalidResponse,
191}
192
193pub async fn supports_multi(
199 client: &mut super::v2::Client,
200 bi: &BlockIdentifier,
201 addr: ContractAddress,
202 name: ContractName<'_>,
203 ids: &[StandardIdentifier],
204) -> Result<super::v2::QueryResponse<SupportsQueryResponse>, SupportsError> {
205 use contracts_common::{Deserial, Serial};
206 let method = OwnedReceiveName::construct(name, EntrypointName::new_unchecked("supports"))
207 .map_err(|_| SupportsError::ContractNameInvalid)?;
208 let mut parameters = Vec::new();
209 (ids.len() as u16)
210 .serial(&mut parameters)
211 .map_err(|_| SupportsError::InvalidParameter)?;
212 for id in ids {
213 id.serial(&mut parameters)
214 .map_err(|_| SupportsError::InvalidParameter)?;
215 }
216 let parameter = smart_contracts::OwnedParameter::try_from(parameters)
217 .map_err(|_| SupportsError::InvalidParameter)?;
218 let ctx = ContractContext {
219 invoker: None,
220 contract: addr,
221 amount: Amount::from_micro_ccd(0),
222 method,
223 parameter,
224 energy: None,
225 };
226 let res = client.invoke_instance(bi, &ctx).await?;
227 match res.response {
228 smart_contracts::InvokeContractResult::Success { return_value, .. } => match return_value {
229 Some(rv) => {
230 let response =
231 SupportsQueryResponse::deserial(&mut contracts_common::Cursor::new(&rv.value))?;
232 if response.results.len() != ids.len() {
233 return Err(SupportsError::InvalidResponse);
234 }
235 Ok(QueryResponse {
236 block_hash: res.block_hash,
237 response,
238 })
239 }
240 None => Err(SupportsError::NoReturn),
241 },
242 smart_contracts::InvokeContractResult::Failure { .. } => Err(SupportsError::ContractReject),
243 }
244}
245
246pub async fn supports(
249 client: &mut super::v2::Client,
250 bi: &BlockIdentifier,
251 addr: ContractAddress,
252 name: ContractName<'_>,
253 ids: StandardIdentifier,
254) -> Result<super::v2::QueryResponse<SupportResult>, SupportsError> {
255 let mut response = supports_multi(client, bi, addr, name, &[ids]).await?;
256 if let Some(r) = response.response.results.pop() {
257 Ok(QueryResponse {
258 block_hash: response.block_hash,
259 response: r,
260 })
261 } else {
262 Err(SupportsError::InvalidResponse)
263 }
264}