From 82b76449ba7627a69d86cb39e448cd21447d25b8 Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Thu, 11 Jul 2024 15:07:51 +0200 Subject: [PATCH] Update documents --- src/contract_client.rs | 215 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 201 insertions(+), 14 deletions(-) diff --git a/src/contract_client.rs b/src/contract_client.rs index 9d99acf9c..da88e623b 100644 --- a/src/contract_client.rs +++ b/src/contract_client.rs @@ -701,6 +701,11 @@ pub struct RejectedTransaction { /// This is only available if the reject reason is a smart contract logical /// revert and a valid error schema is available for decoding, or the /// reject reason originates from the `concordium-std` crate. + /// The JSON value format of such errors is for example: + /// Object {\"Unauthorized\": Array []} or + /// Object {\"Custom\": Array [Object {\"Unauthorized\": Array []}]} (e.g. + /// the cis2_library error) + /// ... pub decoded_reason: Option, /// The energy used by the transaction. pub used_energy: Energy, @@ -712,11 +717,11 @@ pub struct RejectedTransaction { #[repr(i32)] pub enum ConcordiumStdRejectReason { #[error("[Unspecified (Default reject)]")] - Unspecified = -2147483648, + Unspecified = -2147483648, // i32::MIN #[error("[Error ()]")] - Unit = -2147483647, + Unit = -2147483647, // i32::MIN + 1 #[error("[ParseError]")] - Parse = -2147483646, + Parse = -2147483646, // ... #[error("[LogError::Full]")] LogFull = -2147483645, #[error("[LogError::Malformed]")] @@ -779,7 +784,7 @@ pub fn decode_concordium_std_error(reject_reason: i32) -> Option Option` +/// +/// 1. Example: Deriving `Reject` in the smart contract. +/// The simplest way to implement `Reject` in the smart contract is by deriving +/// it. +/// +/// ```ignore +/// /// The custom errors the contract can produce. +/// #[derive(Serialize, Debug, Reject, SchemaType)] +/// pub enum CustomContractError { +/// /// CustomError1 defined in smart contract logic. +/// CustomError1, // return_value: 00; error_code: -1 +/// /// CustomError2 defined in smart contract logic. +/// CustomError2, // return_value: 01; error_code: -2 +/// } +/// ``` +/// +/// The `Reject` macro assigns the `error_codes` starting from `-1` to the enum +/// variants and assigns the `return_values` by serializing the enum variants. +/// The `return_values` are equivalent to the enum tags in the above example. +/// +/// The JSON value returned by this function for the above `CustomError1` is: +/// ```json +/// {\"CustomError1\/": Array []} +/// ``` +/// +/// 2. Example: Deriving `Reject` in the smart contract with nested errors. +/// Nested errors are often used to inherit the errors from a smart +/// contract library such as the cis2-library. +/// `` +/// +/// Library: +/// ```ignore +/// /// The custom errors the library can produce. +/// #[derive(Serialize, Debug, Reject, SchemaType)] +/// pub enum Cis2Error { +/// /// CustomErrorLibrary1 defined in smart contract logic. +/// CustomErrorLibrary1, // return_value: 00; error_code: -1 +/// /// Nested error variant. +/// Custom(R), +/// // return_value: 0100; error_code: -1 +/// // return_value: 0101; error_code: -2 +/// // return_value: 0102; error_code: -3 +/// // ... +/// /// CustomErrorLibrary2 defined in smart contract logic. +/// CustomErrorLibrary2, // return_value: 02; error_code: -3 +/// } +/// ``` +/// +/// Smart contract: +/// ```ignore +/// /// The different errors the contract can produce. +/// #[derive(Serialize, Debug, PartialEq, Eq, Reject, SchemaType)] +/// pub enum CustomContractError { +/// /// CustomError1 defined in smart contract logic. +/// CustomError1, // return_value: 0100; error_code: -1 +/// /// CustomError2 defined in smart contract logic. +/// CustomError2, // return_value: 0101; error_code: -2 +/// /// CustomError3 defined in smart contract logic. +/// CustomError3, // return_value: 0102; error_code: -2 +/// } +/// +/// /// Mapping CustomContractError to ContractError +/// impl From for ContractError { +/// fn from(c: CustomContractError) -> Self { Cis2Error::Custom(c) } +/// } +/// +/// pub type ContractError = Cis2Error; +/// +/// pub type ContractResult = Result; +/// ``` +/// +/// The `Reject` macro assigns the `error_codes` starting from `-1` to the enum +/// variants and assigns the `return_values` by serializing the enum variants +/// starting with the topmost enum. The `return_values` are equivalent to the +/// enum tags of all enums in the nested chain. +/// +/// The JSON value returned by this function for the above `CustomError1` is: +/// ```json +/// {\"Custom\/": Array [Object {\"CustomError1\/": Array []}]} +/// ``` +/// +/// 3. Example: `Reject::default()`. +/// The historical `Reject::default()` can be used by implementing the +/// conversion to `Reject` manually. +/// +/// ```ignore +/// /// The custom errors the contract can produce. +/// #[derive(Serialize, Debug, SchemaType)] +/// pub enum CustomContractError { +/// /// CustomError1 defined in smart contract logic. +/// CustomError1, // return_value: None; error_code: -2147483648 (i32::MIN) +/// } +/// +/// impl From for Reject { +/// fn from(error: CustomContractError) -> Self { +/// match error { +/// _ => Reject::default(), +/// } +/// } +/// } +/// ``` +/// +/// `Reject::default()` assigns `-2147483648` as `error_code` and `None` to the +/// `return_value`. +/// +/// The JSON value returned by this function for the above `CustomError1` is: +/// ```json +/// {\"[Unspecified (Default reject)]\/": Array []} +/// ``` +/// +/// 4. Example: Implementing the conversion to `Reject` manually. +/// A smart contract can implement the conversion to `Reject` manually and +/// define custom error codes. The convention for the `return_value` is to set +/// the value to the serialization of the enum variants so that decoding of the +/// error is possible. +/// +/// ```ignore +/// /// The custom errors the contract can produce. +/// #[derive(Serialize, Debug, SchemaType)] +/// pub enum CustomContractError { +/// /// CustomError1 defined in smart contract logic. +/// CustomError1, // return_value: 00; error_code: -123 +/// /// CustomError2 defined in smart contract logic. +/// CustomError2, // return_value: 01; error_code: -124 +/// } +/// +/// impl From for Reject { +/// fn from(error: CustomContractError) -> Self { +/// match error { +/// CustomContractError::CustomError1 => Reject { +/// error_code: NonZeroI32::new(-123).unwrap(), +/// return_value: Some(vec![00]), +/// }, +/// CustomContractError::CustomError2 => Reject { +/// error_code: NonZeroI32::new(-124).unwrap(), +/// return_value: Some(vec![01]), +/// }, +/// } +/// } +/// } +/// ``` +/// +/// The JSON value returned by this function for the above `CustomError1` is: +/// ```json +/// {\"CustomError1\/": Array []} +/// ``` +/// +/// Disclaimer: A smart contract can have logic to overwrite/change/reuse the +/// meaning of the error codes as defined in the `concordium-std` crate (see +/// Example 4 above). While it is not advised to reuse these error codes and is +/// rather unusual to do so, this function decodes the error codes based on the /// definitions in the `concordium-std` crate (assuming they have not been -/// overwritten with other meanings in the smart contract logic). No guarantee -/// are given as such that the meaning of the decoded reject reason haven't been -/// altered by the smart contract logic. +/// reused with other meanings in the smart contract logic). No guarantee +/// are given as such that the meaning of the decoded reject reason hasn't been +/// altered by the smart contract logic. The main reason for setting the +/// `concordium-std` crate errors to `i32::MIN`,`i32::MIN+1`, etc., is to avoid +/// conflicts with the error codes used in the smart contract logic. pub fn decode_smart_contract_revert( return_value: Option<&ReturnValue>, reject_reason: &RejectReason, @@ -812,7 +999,7 @@ pub fn decode_smart_contract_revert( ) -> Option { match reject_reason { RejectReason::RejectedReceive { - reject_reason: reject_reason_code, + reject_reason: error_code, contract_address: _, receive_name, parameter: _, @@ -821,7 +1008,7 @@ pub fn decode_smart_contract_revert( // Step 1: Try to decode the `reject_reason` using the `concordium-std` // error codes. - if let Some(decoded_error) = decode_concordium_std_error(*reject_reason_code) { + if let Some(decoded_error) = decode_concordium_std_error(*error_code) { // Convert the `decoded_error` into the same format as errors decoded // with the error schema from smart contracts. let mut error_schema_format: HashMap = HashMap::new();