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
use std::{
    fmt::{Debug, Display},
    path::PathBuf,
    str::FromStr,
};

use crate::error::DeployError;
use serde::Serialize;
use strum::{IntoEnumIterator, ParseError};

pub trait Msg: Debug + Send + Sync + erased_serde::Serialize {}

impl<T> Msg for T where T: Debug + Serialize + Send + Sync {}

impl Serialize for dyn Msg {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        erased_serde::serialize(self, serializer)
    }
}

/// This trait represents a contract that can be deployed.
pub trait Deploy: ContractInteractive {
    /// This method gets the preprogrammed instantiate msg for the contract.
    fn instantiate_msg(&self) -> Option<Box<dyn Msg>> {
        println!("No instantiate msg for {}", self.name());
        println!("Defaulting to interactive instantiate");
        self.instantiate().ok()
    }

    /// This method gets the preprogrammed migrate msg for the contract.
    fn migrate_msg(&self) -> Option<Box<dyn Msg>> {
        None
    }

    /// This method gets the preprogrammed set config msg for the contract.
    fn set_config_msg(&self) -> Option<Box<dyn Msg>> {
        None
    }

    /// This method gets the preprogrammed set up for the contract.
    fn set_up_msgs(&self) -> Vec<Box<dyn Msg>> {
        vec![]
    }

    /// This method will instantiate an external contract via code_id alongside a local contract.
    fn external_instantiate_msgs(&self) -> Vec<ExternalInstantiate<Box<dyn Msg>>> {
        vec![]
    }
}

pub trait ContractInteractive:
    Send + Sync + Debug + Display + FromStr<Err = ParseError> + IntoEnumIterator + 'static
{
    /// This is the name of the contract and represents
    /// how it will appear in the cli.
    fn name(&self) -> String {
        self.to_string()
    }

    /// This is the name of the generated binary.
    /// It defaults to the contract name.
    /// If you have multiple contracts that share the same code
    /// then you can use this, in conjunction with the path method.
    fn bin_name(&self) -> String {
        self.name()
    }

    /// This method allows for customizing the path to the contract.
    /// This should be the path relative to the project root.
    fn path(&self) -> PathBuf {
        PathBuf::from(format!("contracts/{}", self.name()))
    }

    /// This is the address of the contract admin.
    /// It is required when instantiating.
    fn admin(&self) -> String;

    /// This method allows instantiating a contract interactively.
    /// interactive-parse should be used to generate the msg.
    fn instantiate(&self) -> anyhow::Result<Box<dyn Msg>> {
        Err(DeployError::TraitNotImplemented.into())
    }

    /// This method allows executing a contract interactively.
    /// interactive-parse should be used to generate the msg.
    fn execute(&self) -> anyhow::Result<Box<dyn Msg>> {
        Err(DeployError::TraitNotImplemented.into())
    }

    /// This method allows querying a contract interactively.
    /// interactive-parse should be used to generate the msg.
    fn query(&self) -> anyhow::Result<Box<dyn Msg>> {
        Err(DeployError::TraitNotImplemented.into())
    }

    /// This method allows migrating a contract interactively.
    /// interactive-parse should be used to generate the msg.
    fn migrate(&self) -> anyhow::Result<Box<dyn Msg>> {
        Err(DeployError::TraitNotImplemented.into())
    }

    /// This method allows sending a cw20 token with an attached message to a contract interactively.
    /// interactive-parse should be used to generate the msg.
    fn cw20_send(&self) -> anyhow::Result<Box<dyn Msg>> {
        Err(DeployError::TraitNotImplemented.into())
    }
}

#[derive(Debug, Clone)]
pub struct ExternalInstantiate<T> {
    pub msg: T,
    pub code_id: u64,
    pub name: String,
}

impl<T> From<ExternalInstantiate<T>> for ExternalInstantiate<Box<dyn Msg>>
where
    T: Msg + Clone + 'static,
{
    fn from(msg: ExternalInstantiate<T>) -> Self {
        ExternalInstantiate {
            msg: Box::new(msg.msg),
            code_id: msg.code_id,
            name: msg.name,
        }
    }
}