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