1mod types;
8
9use crate::{contract_client::*, types as sdk_types, v2::IntoBlockIdentifier};
10use concordium_base::{
11 base::Energy,
12 contracts_common::{Address, Amount},
13 transactions::{AccountTransaction, EncodedPayload},
14};
15use sdk_types::{smart_contracts, transactions};
16use smart_contracts::concordium_contracts_common;
17use std::convert::From;
18use thiserror::*;
19pub use types::*;
20
21#[derive(Debug, Clone, Copy)]
22pub enum Cis2Type {}
25
26pub type Cis2Contract = ContractClient<Cis2Type>;
34
35#[derive(Debug, Error)]
38pub enum Cis2TransactionError {
39 #[error("Invalid receive name: {0}")]
41 InvalidReceiveName(#[from] concordium_contracts_common::NewReceiveNameError),
42
43 #[error("Invalid transfer parameter: {0}")]
45 InvalidTransferParams(#[from] NewTransferParamsError),
46
47 #[error("Invalid updateOperator parameter: {0}")]
49 InvalidUpdateOperatorParams(#[from] NewUpdateOperatorParamsError),
50
51 #[error("RPC error: {0}")]
53 RPCError(#[from] crate::endpoints::RPCError),
54}
55
56#[derive(Debug, Error)]
59pub enum Cis2DryRunError {
60 #[error("Invalid receive name: {0}")]
62 InvalidReceiveName(#[from] concordium_contracts_common::NewReceiveNameError),
63
64 #[error("Invalid transfer parameter: {0}")]
66 InvalidTransferParams(#[from] NewTransferParamsError),
67
68 #[error("Invalid updateOperator parameter: {0}")]
70 InvalidUpdateOperatorParams(#[from] NewUpdateOperatorParamsError),
71
72 #[error("RPC error: {0}")]
74 QueryError(#[from] crate::endpoints::QueryError),
75
76 #[error("Rejected by the node: {0:?}.")]
78 NodeRejected(sdk_types::RejectReason),
79}
80
81#[derive(Debug, Error)]
84pub enum Cis2QueryError {
85 #[error("Invalid receive name: {0}")]
87 InvalidReceiveName(#[from] concordium_contracts_common::NewReceiveNameError),
88
89 #[error("Invalid balanceOf parameter: {0}")]
91 InvalidBalanceOfParams(#[from] NewBalanceOfQueryParamsError),
92
93 #[error("Invalid operatorOf parameter: {0}")]
95 InvalidOperatorOfParams(#[from] NewOperatorOfQueryParamsError),
96
97 #[error("Invalid tokenMetadata parameter: {0}")]
99 InvalidTokenMetadataParams(#[from] NewTokenMetadataQueryParamsError),
100
101 #[error("RPC error: {0}")]
103 RPCError(#[from] super::v2::QueryError),
104
105 #[error("Failed parsing the response.")]
107 ResponseParseError(#[from] concordium_contracts_common::ParseError),
108
109 #[error("Rejected by the node: {0:?}.")]
111 NodeRejected(sdk_types::RejectReason),
112}
113
114impl From<sdk_types::RejectReason> for Cis2QueryError {
117 fn from(err: sdk_types::RejectReason) -> Self { Self::NodeRejected(err) }
118}
119
120impl From<sdk_types::RejectReason> for Cis2DryRunError {
123 fn from(err: sdk_types::RejectReason) -> Self { Self::NodeRejected(err) }
124}
125
126pub type Cis2TransactionMetadata = ContractTransactionMetadata;
128
129impl Cis2Contract {
130 pub async fn transfer_dry_run(
141 &mut self,
142 bi: impl IntoBlockIdentifier,
143 sender: Address,
144 transfers: Vec<Transfer>,
145 ) -> Result<Energy, Cis2DryRunError> {
146 let parameter = TransferParams::new(transfers)?;
147 let parameter = smart_contracts::OwnedParameter::from_serial(¶meter)
148 .map_err(|_| Cis2DryRunError::InvalidTransferParams(NewTransferParamsError))?;
149 let ir = self
150 .invoke_raw::<Cis2DryRunError>("transfer", Amount::zero(), Some(sender), parameter, bi)
151 .await?;
152 match ir {
153 smart_contracts::InvokeContractResult::Success { used_energy, .. } => Ok(used_energy),
154 smart_contracts::InvokeContractResult::Failure { reason, .. } => Err(reason.into()),
155 }
156 }
157
158 pub async fn transfer_single_dry_run(
161 &mut self,
162 bi: impl IntoBlockIdentifier,
163 sender: Address,
164 transfer: Transfer,
165 ) -> Result<Energy, Cis2DryRunError> {
166 self.transfer_dry_run(bi, sender, vec![transfer]).await
167 }
168
169 pub async fn transfer(
180 &mut self,
181 signer: &impl transactions::ExactSizeTransactionSigner,
182 transaction_metadata: Cis2TransactionMetadata,
183 transfers: Vec<Transfer>,
184 ) -> Result<sdk_types::hashes::TransactionHash, Cis2TransactionError> {
185 let transfer = self.make_transfer(signer, transaction_metadata, transfers)?;
186 let hash = self.client.send_account_transaction(transfer).await?;
187 Ok(hash)
188 }
189
190 pub fn make_transfer(
201 &self,
202 signer: &impl transactions::ExactSizeTransactionSigner,
203 transaction_metadata: Cis2TransactionMetadata,
204 transfers: Vec<Transfer>,
205 ) -> Result<AccountTransaction<EncodedPayload>, Cis2TransactionError> {
206 let parameter = TransferParams::new(transfers)?;
207 let message = smart_contracts::OwnedParameter::from_serial(¶meter)
208 .map_err(|_| Cis2TransactionError::InvalidTransferParams(NewTransferParamsError))?;
209 self.make_update_raw(signer, &transaction_metadata, "transfer", message)
210 }
211
212 pub async fn transfer_single(
215 &mut self,
216 signer: &impl transactions::ExactSizeTransactionSigner,
217 transaction_metadata: Cis2TransactionMetadata,
218 transfer: Transfer,
219 ) -> Result<sdk_types::hashes::TransactionHash, Cis2TransactionError> {
220 self.transfer(signer, transaction_metadata, vec![transfer])
221 .await
222 }
223
224 pub fn make_transfer_single(
227 &self,
228 signer: &impl transactions::ExactSizeTransactionSigner,
229 transaction_metadata: Cis2TransactionMetadata,
230 transfer: Transfer,
231 ) -> Result<AccountTransaction<EncodedPayload>, Cis2TransactionError> {
232 self.make_transfer(signer, transaction_metadata, vec![transfer])
233 }
234
235 pub async fn update_operator_dry_run(
246 &mut self,
247 bi: impl IntoBlockIdentifier,
248 owner: Address,
249 updates: Vec<UpdateOperator>,
250 ) -> anyhow::Result<Energy, Cis2DryRunError> {
251 let parameter = UpdateOperatorParams::new(updates)?;
252 let parameter = smart_contracts::OwnedParameter::from_serial(¶meter)
253 .map_err(|_| Cis2DryRunError::InvalidTransferParams(NewTransferParamsError))?;
254 let ir = self
255 .invoke_raw::<Cis2DryRunError>(
256 "updateOperator",
257 Amount::zero(),
258 Some(owner),
259 parameter,
260 bi,
261 )
262 .await?;
263 match ir {
264 smart_contracts::InvokeContractResult::Success { used_energy, .. } => Ok(used_energy),
265 smart_contracts::InvokeContractResult::Failure { reason, .. } => Err(reason.into()),
266 }
267 }
268
269 pub async fn update_operator_single_dry_run(
272 &mut self,
273 bi: impl IntoBlockIdentifier,
274 owner: Address,
275 operator: Address,
276 update: OperatorUpdate,
277 ) -> anyhow::Result<Energy, Cis2DryRunError> {
278 self.update_operator_dry_run(bi, owner, vec![UpdateOperator { update, operator }])
279 .await
280 }
281
282 pub fn make_update_operator(
293 &self,
294 signer: &impl transactions::ExactSizeTransactionSigner,
295 transaction_metadata: Cis2TransactionMetadata,
296 updates: Vec<UpdateOperator>,
297 ) -> anyhow::Result<AccountTransaction<EncodedPayload>, Cis2TransactionError> {
298 let parameter = UpdateOperatorParams::new(updates)?;
299 let message = smart_contracts::OwnedParameter::from_serial(¶meter).map_err(|_| {
300 Cis2TransactionError::InvalidUpdateOperatorParams(NewUpdateOperatorParamsError)
301 })?;
302 self.make_update_raw(signer, &transaction_metadata, "updateOperator", message)
303 }
304
305 pub async fn update_operator(
316 &mut self,
317 signer: &impl transactions::ExactSizeTransactionSigner,
318 transaction_metadata: Cis2TransactionMetadata,
319 updates: Vec<UpdateOperator>,
320 ) -> anyhow::Result<sdk_types::hashes::TransactionHash, Cis2TransactionError> {
321 let update = self.make_update_operator(signer, transaction_metadata, updates)?;
322 let hash = self.client.send_account_transaction(update).await?;
323 Ok(hash)
324 }
325
326 pub async fn update_operator_single(
329 &mut self,
330 signer: &impl transactions::ExactSizeTransactionSigner,
331 transaction_metadata: Cis2TransactionMetadata,
332 operator: Address,
333 update: OperatorUpdate,
334 ) -> anyhow::Result<sdk_types::hashes::TransactionHash, Cis2TransactionError> {
335 self.update_operator(signer, transaction_metadata, vec![UpdateOperator {
336 update,
337 operator,
338 }])
339 .await
340 }
341
342 pub fn make_update_operator_single(
345 &self,
346 signer: &impl transactions::ExactSizeTransactionSigner,
347 transaction_metadata: Cis2TransactionMetadata,
348 operator: Address,
349 update: OperatorUpdate,
350 ) -> anyhow::Result<AccountTransaction<EncodedPayload>, Cis2TransactionError> {
351 self.make_update_operator(signer, transaction_metadata, vec![UpdateOperator {
352 update,
353 operator,
354 }])
355 }
356
357 pub async fn balance_of(
368 &mut self,
369 bi: impl IntoBlockIdentifier,
370 queries: Vec<BalanceOfQuery>,
371 ) -> Result<BalanceOfQueryResponse, Cis2QueryError> {
372 let parameter = BalanceOfQueryParams::new(queries)?;
373 let parameter = smart_contracts::OwnedParameter::from_serial(¶meter)
374 .map_err(|_| Cis2QueryError::InvalidBalanceOfParams(NewBalanceOfQueryParamsError))?;
375 self.view_raw("balanceOf", parameter, bi).await
376 }
377
378 pub async fn balance_of_single(
382 &mut self,
383 bi: impl IntoBlockIdentifier,
384 token_id: TokenId,
385 address: Address,
386 ) -> Result<TokenAmount, Cis2QueryError> {
387 let res = self
388 .balance_of(bi, vec![BalanceOfQuery { token_id, address }])
389 .await?;
390 only_one(res)
391 }
392
393 pub async fn operator_of(
404 &mut self,
405 bi: impl IntoBlockIdentifier,
406 queries: Vec<OperatorOfQuery>,
407 ) -> Result<OperatorOfQueryResponse, Cis2QueryError> {
408 let parameter = OperatorOfQueryParams::new(queries)?;
409 let parameter = smart_contracts::OwnedParameter::from_serial(¶meter)
410 .map_err(|_| Cis2QueryError::InvalidOperatorOfParams(NewOperatorOfQueryParamsError))?;
411 self.view_raw("operatorOf", parameter, bi).await
412 }
413
414 pub async fn operator_of_single(
418 &mut self,
419 bi: impl IntoBlockIdentifier,
420 owner: Address,
421 operator: Address,
422 ) -> Result<bool, Cis2QueryError> {
423 let res = self
424 .operator_of(bi, vec![OperatorOfQuery {
425 owner,
426 address: operator,
427 }])
428 .await?;
429 only_one(res)
430 }
431
432 pub async fn token_metadata(
443 &mut self,
444 bi: impl IntoBlockIdentifier,
445 queries: Vec<TokenId>,
446 ) -> Result<TokenMetadataQueryResponse, Cis2QueryError> {
447 let parameter = TokenMetadataQueryParams::new(queries)?;
448 let parameter = smart_contracts::OwnedParameter::from_serial(¶meter).map_err(|_| {
449 Cis2QueryError::InvalidTokenMetadataParams(NewTokenMetadataQueryParamsError)
450 })?;
451 self.view_raw("tokenMetadata", parameter, bi).await
452 }
453
454 pub async fn token_metadata_single(
458 &mut self,
459 bi: impl IntoBlockIdentifier,
460 token_id: TokenId,
461 ) -> Result<MetadataUrl, Cis2QueryError> {
462 let res = self.token_metadata(bi, vec![token_id]).await?;
463 only_one(res)
464 }
465}
466
467fn only_one<A, V: AsRef<Vec<A>>>(res: V) -> Result<A, Cis2QueryError>
470where
471 Vec<A>: From<V>, {
472 let err = Cis2QueryError::ResponseParseError(concordium_contracts_common::ParseError {});
473 if res.as_ref().len() > 1 {
474 Err(err)
475 } else {
476 Vec::from(res).pop().ok_or(err)
477 }
478}