Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[scTool] Error decoding of nested errors #182

Open
DOBEN opened this issue Jul 11, 2024 · 0 comments
Open

[scTool] Error decoding of nested errors #182

DOBEN opened this issue Jul 11, 2024 · 0 comments
Labels
[Type] Bug Something isn't working

Comments

@DOBEN
Copy link
Member

DOBEN commented Jul 11, 2024

Task description

https://github.com/Concordium/concordium-smart-contract-tools/blob/main/front-end-tools/src/utils.ts#L199
Error decoding of nested errors is currently not captured correctly in the above line. An error can be nested e.g.:

{"Custom":  [ {"CustomError1":  []}]}

The scTool only displays the top-most error enum variant Custom in the cis2-library example.

Explanation of how nested errors are constructed in the smart contract

/// 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.
/// `<https://github.com/Concordium/concordium-rust-smart-contracts/blob/dde42fa62254a55b46a4c9c52c32bbe661127001/concordium-cis2/src/lib.rs#L1093>`
///
/// Library:
/// ```ignore
/// /// The custom errors the library can produce.
/// #[derive(Serialize, Debug, Reject, SchemaType)]
/// pub enum Cis2Error<R> {
///     /// 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<CustomContractError> for ContractError {
///     fn from(c: CustomContractError) -> Self { Cis2Error::Custom(c) }
/// }
///
/// pub type ContractError = Cis2Error<CustomContractError>;
///
/// pub type ContractResult<A> = Result<A, ContractError>;
/// ```
///
/// 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 []}]}
/// ```

A recursive approach is needed similar to below code for scTool as well to display nested errors meaningfully.

/// Write a custom display implementation for the error schema type.
/// This displays nested errors meaningfully.
impl std::fmt::Display for ErrorSchema {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.0 {
            Value::Object(map) => {
                if let Some(key) = map.keys().next() {
                    write!(f, "{}", key)?;
                    if let Some(value) = map.values().next() {
                        if value.is_array() {
                            write!(f, "{}", ErrorSchema(value.clone()))?;
                        }
                    }
                }
            }
            Value::Array(arr) => {
                if let Some(value) = arr.iter().next() {
                    write!(f, "::{}", ErrorSchema(value.clone()))?;
                }
            }
            _ => write!(f, "{}", self.0)?,
        }
        Ok(())
    }
}

Another limitation that once resolved needs to be applied to the scTool front-end is given in the comment of the PR:
Concordium/concordium-dapp-examples#88

@DOBEN DOBEN added the [Type] Task An additional feature or improvement. label Jul 11, 2024
@eb-concordium eb-concordium added the [Type] Bug Something isn't working label Jul 25, 2024 — with Linear
@eb-concordium eb-concordium removed the [Type] Task An additional feature or improvement. label Jul 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Type] Bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants