1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! Encapsulates a set of structures that abstract out a smart contract layout.

use alloc::{string::String, vec::Vec};

use crate::Type;

/// Contract's entrypoint.
#[derive(Debug, Clone)]
pub struct Entrypoint {
    pub ident: String,
    pub args: Vec<Argument>,
    pub is_mut: bool,
    pub ret: Type,
    pub ty: EntrypointType
}

/// Defines an argument passed to an entrypoint.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Argument {
    pub ident: String,
    pub ty: Type,
    pub is_ref: bool
}

/// Defines an entrypoint type.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum EntrypointType {
    /// A special entrypoint that can be called just once on the contract initialization.
    Constructor { non_reentrant: bool },
    /// A regular entrypoint.
    Public { non_reentrant: bool },
    /// A payable entrypoint.
    PublicPayable { non_reentrant: bool }
}

impl EntrypointType {
    pub fn is_non_reentrant(&self) -> bool {
        match self {
            EntrypointType::Constructor { non_reentrant } => *non_reentrant,
            EntrypointType::Public { non_reentrant } => *non_reentrant,
            EntrypointType::PublicPayable { non_reentrant } => *non_reentrant
        }
    }
}

/// Defines an event.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Event {
    pub ident: String,
    pub args: Vec<Argument>
}

impl Event {
    pub fn has_any(&self) -> bool {
        self.args.iter().any(|arg| Type::has_any(&arg.ty))
    }
}

/// A trait that should be implemented by each smart contract to allow the backend
/// to generate blockchain-specific code.
pub trait HasEntrypoints {
    /// Returns an abstract contract definition.
    fn entrypoints() -> Vec<Entrypoint>;
}

/// A trait that should be implemented by each smart contract to allow the backend.
pub trait HasIdent {
    fn ident() -> String;
}
/// A trait that should be implemented by each smart contract to allow the backend.
pub trait HasEvents {
    fn events() -> Vec<Event>;
}

pub trait Node {
    const IS_LEAF: bool = true;
    const COUNT: u32;

    fn __keys() -> Vec<String> {
        Vec::new()
    }
}

#[derive(Debug, Clone)]
pub struct ContractBlueprint {
    pub keys: Vec<String>,
    pub keys_count: u32,
    pub events: Vec<Event>,
    pub entrypoints: Vec<Entrypoint>,
    pub fqn: &'static str
}

#[cfg(test)]
#[allow(dead_code)]
mod test {
    use super::Node;
    use alloc::{string::String, vec, vec::Vec};
    use core::marker::PhantomData;

    #[test]
    fn key_collection_works() {
        struct Variable<T> {
            ty: PhantomData<T>
        }

        impl<T> Node for Variable<T> {
            const COUNT: u32 = 1;
        }

        struct Contract {
            pub value: Variable<u32>,
            pub submodule: Submodule
        }

        impl Node for Contract {
            const COUNT: u32 = <Variable<u32> as Node>::COUNT + <Submodule as Node>::COUNT;
            const IS_LEAF: bool = false;

            fn __keys() -> Vec<String> {
                let mut result = vec![];
                // The same would be generated by the [odra::module] macro.
                if <Variable<u32> as Node>::IS_LEAF {
                    result.push(String::from("value"));
                } else {
                    result.extend(
                        <Variable<u32> as Node>::__keys()
                            .iter()
                            .map(|k| odra_utils::create_key("value", k))
                    )
                }
                if <Submodule as Node>::IS_LEAF {
                    result.push(String::from("submodule"));
                } else {
                    result.extend(
                        <Submodule as Node>::__keys()
                            .iter()
                            .map(|k| odra_utils::create_key("submodule", k))
                    )
                }
                result
            }
        }

        struct Submodule {
            abc: Variable<u32>
        }

        impl Node for Submodule {
            const COUNT: u32 = <Variable<u32> as Node>::COUNT;
            const IS_LEAF: bool = false;

            fn __keys() -> Vec<String> {
                let mut result = vec![];
                if <Variable<u32> as Node>::IS_LEAF {
                    result.push(String::from("abc"));
                } else {
                    result.extend(
                        <Variable<u32> as Node>::__keys()
                            .iter()
                            .map(|k| odra_utils::create_key("abc", k))
                    )
                }
                result
            }
        }

        assert_eq!(Contract::__keys(), ["value", "submodule#abc"])
    }
}