1use std::str::FromStr;
2
3use cosmwasm_schema::{cw_serde, QueryResponses};
4use cosmwasm_std::{ensure, Addr, Api, Uint128};
5use schemars::JsonSchema;
6use semver::Version;
7use serde::{Deserialize, Serialize};
8
9use crate::{ado_base::ownership::OwnershipMessage, error::ContractError};
10
11#[cw_serde]
12pub struct InstantiateMsg {
13 pub kernel_address: String,
14 pub owner: Option<String>,
15}
16
17#[cw_serde]
18pub enum ExecuteMsg {
19 Publish {
20 code_id: u64,
21 ado_type: String,
22 action_fees: Option<Vec<ActionFee>>,
23 version: String,
24 publisher: Option<String>,
25 },
26 Unpublish {
27 ado_type: String,
28 version: String,
29 },
30 UpdateActionFees {
31 ado_type: String,
32 action_fees: Vec<ActionFee>,
33 },
34 RemoveActionFees {
35 ado_type: String,
36 actions: Vec<String>,
37 },
38 UpdatePublisher {
39 ado_type: String,
40 publisher: String,
41 },
42 Ownership(OwnershipMessage),
44}
45
46#[cw_serde]
47pub struct ActionFee {
48 pub action: String,
49 pub asset: String,
50 pub amount: Uint128,
51 pub receiver: Option<Addr>,
52}
53
54impl ActionFee {
55 pub fn new(action: String, asset: String, amount: Uint128) -> Self {
56 Self {
57 action,
58 asset,
59 amount,
60 receiver: None,
61 }
62 }
63
64 pub fn with_receive(&self, receiver: Addr) -> Self {
65 Self {
66 action: self.action.clone(),
67 asset: self.asset.clone(),
68 amount: self.amount,
69 receiver: Some(receiver),
70 }
71 }
72
73 pub fn validate_asset(&self, api: &dyn Api) -> Result<(), ContractError> {
78 let asset_split = self.asset.split(':').collect::<Vec<&str>>();
79 ensure!(
82 asset_split.len() == 2 && !asset_split.is_empty(),
83 ContractError::InvalidAsset {
84 asset: self.asset.clone()
85 }
86 );
87 let asset_type = asset_split[0];
88 ensure!(
89 asset_type == "cw20" || asset_type == "native",
90 ContractError::InvalidAsset {
91 asset: self.asset.clone()
92 }
93 );
94
95 if asset_type == "cw20" {
96 api.addr_validate(asset_split[1])?;
97 }
98
99 Ok(())
100 }
101
102 pub fn get_asset_string(&self) -> Result<&str, ContractError> {
106 ensure!(
107 self.asset.contains(':'),
108 ContractError::InvalidAsset {
109 asset: self.asset.clone()
110 }
111 );
112 match self.asset.split(':').last() {
113 Some(asset) => Ok(asset),
114 None => Err(ContractError::InvalidAsset {
115 asset: self.asset.clone(),
116 }),
117 }
118 }
119}
120
121#[cw_serde]
122pub struct ADOMetadata {
123 pub publisher: String,
124 pub latest_version: String,
125}
126
127#[cw_serde]
128#[derive(QueryResponses)]
129pub enum QueryMsg {
130 #[returns(u64)]
131 CodeId { key: String },
132 #[returns(IsUnpublishedCodeIdResponse)]
135 IsUnpublishedCodeId { code_id: u64 },
136 #[returns(Option<String>)]
137 #[serde(rename = "ado_type")]
138 ADOType { code_id: u64 },
139 #[returns(Vec<String>)]
140 #[serde(rename = "all_ado_types")]
141 AllADOTypes {
142 start_after: Option<String>,
143 limit: Option<u32>,
144 },
145 #[returns(Vec<String>)]
146 #[serde(rename = "ado_versions")]
147 ADOVersions {
148 ado_type: String,
149 start_after: Option<String>,
150 limit: Option<u32>,
151 },
152 #[returns(Option<ADOMetadata>)]
156 #[serde(rename = "ado_metadata")]
157 ADOMetadata { ado_type: String },
158 #[returns(Option<ActionFee>)]
159 ActionFee { ado_type: String, action: String },
160 #[returns(Option<ActionFee>)]
161 ActionFeeByCodeId { code_id: u64, action: String },
162 #[returns(crate::ado_base::version::VersionResponse)]
164 Version {},
165 #[returns(crate::ado_base::ado_type::TypeResponse)]
166 Type {},
167 #[returns(crate::ado_base::ownership::ContractOwnerResponse)]
168 Owner {},
169 #[returns(crate::ado_base::kernel_address::KernelAddressResponse)]
170 KernelAddress {},
171}
172
173#[cw_serde]
174pub struct IsUnpublishedCodeIdResponse {
175 pub is_unpublished_code_id: bool,
176}
177
178#[derive(
179 Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, JsonSchema,
180)]
181pub struct ADOVersion(String);
182
183impl ADOVersion {
184 #[inline]
185 pub fn as_str(&self) -> &str {
186 self.0.as_str()
187 }
188
189 #[inline]
190 pub fn as_bytes(&self) -> &[u8] {
191 self.0.as_bytes()
192 }
193
194 #[inline]
195 pub fn into_string(self) -> String {
196 self.0
197 }
198
199 #[inline]
200 pub fn from_string(string: impl Into<String>) -> ADOVersion {
201 ADOVersion(string.into())
202 }
203
204 #[inline]
205 pub fn from_type(ado_type: impl Into<String>) -> ADOVersion {
206 ADOVersion(ado_type.into())
207 }
208
209 #[inline]
210 pub fn with_version(&self, version: impl Into<String>) -> ADOVersion {
211 let mut ado_version = self.clone();
212 ado_version.0 = ado_version.get_type();
214 ado_version.0.push('@');
215 ado_version.0.push_str(&version.into());
216 ado_version
217 }
218
219 pub fn validate(&self) -> bool {
230 !self.clone().into_string().is_empty()
231 && self.clone().into_string().split('@').count() <= 2
232 && (self.get_version() == "latest"
233 || Version::from_str(self.get_version().as_str()).is_ok())
234 }
235
236 pub fn get_version(&self) -> String {
240 match self
241 .clone()
242 .into_string()
243 .split('@')
244 .collect::<Vec<&str>>()
245 .len()
246 {
247 1 => "latest".to_string(),
248 _ => self.clone().into_string().split('@').collect::<Vec<&str>>()[1].to_string(),
249 }
250 }
251
252 pub fn get_type(&self) -> String {
254 self.clone().into_string().split('@').collect::<Vec<&str>>()[0].to_string()
255 }
256
257 pub fn get_tuple(&self) -> (String, String) {
259 (self.get_type(), self.get_version())
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use cosmwasm_std::testing::mock_dependencies;
266
267 use super::*;
268
269 #[test]
270 fn test_validate() {
271 let ado_version = ADOVersion::from_string("valid_version");
272 assert!(ado_version.validate());
273
274 let ado_version = ADOVersion::from_string("valid_version@0.1.0");
275 assert!(ado_version.validate());
276
277 let ado_version = ADOVersion::from_string("");
278 assert!(!ado_version.validate());
279
280 let ado_version = ADOVersion::from_string("not@valid@version");
281 assert!(!ado_version.validate());
282 }
283
284 #[test]
285 fn test_get_version() {
286 let ado_version = ADOVersion::from_string("ado_type");
287 assert_eq!(ado_version.get_version(), "latest");
288
289 let ado_version = ADOVersion::from_string("ado_type@0.1.0");
290 assert_eq!(ado_version.get_version(), "0.1.0");
291
292 let ado_version = ADOVersion::from_string("ado_type@latest");
293 assert_eq!(ado_version.get_version(), "latest");
294 }
295
296 #[test]
297 fn test_get_type() {
298 let ado_version = ADOVersion::from_string("ado_type");
299 assert_eq!(ado_version.get_type(), "ado_type");
300
301 let ado_version = ADOVersion::from_string("ado_type@0.1.0");
302 assert_eq!(ado_version.get_type(), "ado_type");
303
304 let ado_version = ADOVersion::from_string("ado_type@latest");
305 assert_eq!(ado_version.get_type(), "ado_type");
306 }
307
308 #[test]
309 fn test_action_fee_asset() {
310 let deps = mock_dependencies();
311 let action_fee = ActionFee::new(
312 "action".to_string(),
313 "cw20:address".to_string(),
314 Uint128::zero(),
315 );
316 assert!(action_fee.validate_asset(deps.as_ref().api).is_ok());
317
318 let action_fee = ActionFee::new(
319 "action".to_string(),
320 "native:denom".to_string(),
321 Uint128::zero(),
322 );
323 assert!(action_fee.validate_asset(deps.as_ref().api).is_ok());
324
325 let action_fee =
326 ActionFee::new("action".to_string(), "cw20:aw".to_string(), Uint128::zero());
327 assert!(action_fee.validate_asset(deps.as_ref().api).is_err());
328
329 let action_fee =
330 ActionFee::new("action".to_string(), "invalid".to_string(), Uint128::zero());
331 assert!(action_fee.validate_asset(deps.as_ref().api).is_err());
332 }
333}