cosm_script/
traits.rs

1use crate::{contract::ContractInstance, error::CosmScriptError, CosmTxResponse};
2use async_trait::async_trait;
3use cosmrs::Coin;
4use serde::{de::DeserializeOwned, Serialize};
5
6#[derive(Debug, Clone, Serialize)]
7pub struct NotImplemented(String);
8
9impl Default for NotImplemented {
10    // TODO: improve, when rust allows negating trait bounds or overwriting traits
11    fn default() -> Self {
12        Self("Stupid Workaround".into())
13    }
14}
15
16// Fn for custom implementation to return ContractInstance
17pub trait Instance {
18    fn instance(&self) -> &ContractInstance;
19}
20
21/// Implementing Interface ensures type safety
22pub trait Interface {
23    type Init: Serialize;
24    type Exec: Serialize;
25    type Query: Serialize;
26    type Migrate: Serialize;
27}
28
29/// Smart Contract execute endpoint
30#[async_trait(?Send)]
31pub trait WasmExecute {
32    type E: Serialize;
33
34    async fn exec<'a>(
35        &self,
36        execute_msg: &'a Self::E,
37        coins: Option<&[Coin]>,
38    ) -> Result<CosmTxResponse, CosmScriptError>;
39}
40
41#[async_trait(?Send)]
42impl<T: Interface + Instance> WasmExecute for T {
43    type E = <T as Interface>::Exec;
44
45    async fn exec<'a>(
46        &self,
47        execute_msg: &'a Self::E,
48        coins: Option<&[Coin]>,
49    ) -> Result<CosmTxResponse, CosmScriptError> {
50        assert_implemented(&execute_msg)?;
51        self.instance()
52            .execute(&execute_msg, coins.unwrap_or(&[]))
53            .await
54    }
55}
56
57/// Smart Contract instantiate endpoint
58
59#[async_trait(?Send)]
60pub trait WasmInstantiate {
61    type I: Serialize;
62
63    async fn init(
64        &self,
65        instantiate_msg: Self::I,
66        admin: Option<String>,
67        coins: Option<&[Coin]>,
68    ) -> Result<CosmTxResponse, CosmScriptError>;
69}
70
71#[async_trait(?Send)]
72impl<T: Interface + Instance> WasmInstantiate for T {
73    type I = <T as Interface>::Init;
74
75    async fn init(
76        &self,
77        instantiate_msg: Self::I,
78        admin: Option<String>,
79        coins: Option<&[Coin]>,
80    ) -> Result<CosmTxResponse, CosmScriptError> {
81        assert_implemented(&instantiate_msg)?;
82        self.instance()
83            .instantiate(instantiate_msg, admin, coins.unwrap_or_default())
84            .await
85    }
86}
87
88/// Smart Contract query endpoint
89
90#[async_trait(?Send)]
91pub trait WasmQuery {
92    type Q: Serialize;
93
94    async fn query<G: Serialize + DeserializeOwned>(
95        &self,
96        query_msg: Self::Q,
97    ) -> Result<G, CosmScriptError>;
98}
99
100#[async_trait(?Send)]
101impl<T: Interface + Instance> WasmQuery for T {
102    type Q = <T as Interface>::Query;
103
104    async fn query<G: Serialize + DeserializeOwned>(
105        &self,
106        query_msg: Self::Q,
107    ) -> Result<G, CosmScriptError> {
108        assert_implemented(&query_msg)?;
109        self.instance().query(query_msg).await
110    }
111}
112
113/// Smart Contract migrate endpoint
114
115#[async_trait(?Send)]
116pub trait WasmMigrate {
117    type M: Serialize;
118
119    async fn migrate(
120        &self,
121        migrate_msg: Self::M,
122        new_code_id: u64,
123    ) -> Result<CosmTxResponse, CosmScriptError>;
124}
125
126#[async_trait(?Send)]
127impl<T: Interface + Instance> WasmMigrate for T {
128    type M = <T as Interface>::Migrate;
129
130    async fn migrate(
131        &self,
132        migrate_msg: Self::M,
133        new_code_id: u64,
134    ) -> Result<CosmTxResponse, CosmScriptError> {
135        assert_implemented(&migrate_msg)?;
136        self.instance().migrate(migrate_msg, new_code_id).await
137    }
138}
139
140/// Smart Contract migrate endpoint
141
142#[async_trait(?Send)]
143pub trait WasmUpload {
144    async fn upload(&self, path: &str) -> Result<CosmTxResponse, CosmScriptError>;
145}
146
147#[async_trait(?Send)]
148impl<T: Instance> WasmUpload for T {
149    async fn upload(&self, path: &str) -> Result<CosmTxResponse, CosmScriptError> {
150        self.instance().upload(path).await
151    }
152}
153
154// asserts that trait function is implemented for contract
155fn assert_implemented<E: Serialize>(msg: &E) -> Result<(), CosmScriptError> {
156    if serde_json::to_string(msg)? == serde_json::to_string(&NotImplemented::default())? {
157        return Err(CosmScriptError::NotImplemented);
158    }
159    Ok(())
160}
161
162// TODO: find out how to create a wrapper trait that can be imported to expose all the interfaces
163// pub trait WasmContract<'a>: WasmExecute + WasmInstantiate + WasmQuery + WasmMigrate {}
164
165// /// Smart Contract execute endpoint
166// #[async_trait(?Send)]
167// pub trait WasmContract<'a>: WasmExecute + WasmInstantiate + WasmQuery where &'a Self: 'a + Interface + Instance  {
168//     async fn exec<'b>(
169//         &'a self,
170//         execute_msg:&'b <&'a Self as Interface>::E,
171//         coins: Option<&[Coin]>,
172//     ) -> Result<CosmTxResponse, CosmScriptError>;
173// }