croncat_integration_utils/
task_creation.rs1use crate::error::CronCatContractError;
2use crate::types::{CronCatTaskSubmessageParams, SubMessageReplyType};
3use crate::{REPLY_CRONCAT_TASK_CREATION, TASKS_NAME};
4use cosmwasm_std::CosmosMsg::Wasm;
5use cosmwasm_std::WasmMsg::{self, Execute};
6use cosmwasm_std::{to_binary, Addr, BankMsg, CosmosMsg, MessageInfo, QuerierWrapper, SubMsg};
7use croncat_sdk_factory::msg::ContractMetadataResponse;
8use croncat_sdk_factory::msg::FactoryQueryMsg::LatestContract;
9use croncat_sdk_manager::types::Config as ManagerConfig;
10use croncat_sdk_tasks::msg::TasksExecuteMsg::CreateTask;
11use croncat_sdk_tasks::types::{
12 AmountForOneTask, Boundary, BoundaryHeight, BoundaryTime, Config as TasksConfig, Interval,
13 Task, TaskRequest,
14};
15use cw20::{Cw20CoinVerified, Cw20ExecuteMsg};
16
17pub fn get_latest_croncat_contract(
20 querier: &QuerierWrapper,
21 croncat_factory_address: Addr,
22 croncat_contract_name: String,
23) -> Result<Addr, CronCatContractError> {
24 let query_factory_msg = LatestContract {
25 contract_name: croncat_contract_name.clone(),
26 };
27 let latest_contract_res: ContractMetadataResponse =
28 querier.query_wasm_smart(&croncat_factory_address, &query_factory_msg)?;
29
30 let contract_metadata =
32 latest_contract_res
33 .metadata
34 .ok_or(CronCatContractError::NoSuchContractOnFactory {
35 contract_name: croncat_contract_name,
36 factory_addr: croncat_factory_address,
37 version: "latest".to_owned(),
38 })?;
39
40 Ok(contract_metadata.contract_addr)
41}
42
43pub fn get_croncat_contract(
47 querier: &QuerierWrapper,
48 croncat_factory_address: Addr,
49 croncat_contract_name: String,
50 croncat_version: String,
51) -> Result<Addr, CronCatContractError> {
52 let croncat_version_parsed = croncat_version
54 .split('.')
55 .map(|ver| {
56 ver.parse()
57 .map_err(|_| CronCatContractError::InvalidVersionString {})
58 })
59 .collect::<Result<Vec<u8>, _>>()?;
60
61 let contract_addr = croncat_factory::state::CONTRACT_ADDRS
63 .query(
64 querier,
65 croncat_factory_address.clone(),
66 (&croncat_contract_name, &croncat_version_parsed),
67 )?
68 .ok_or(CronCatContractError::NoSuchContractOnFactory {
69 contract_name: croncat_contract_name,
70 factory_addr: croncat_factory_address,
71 version: croncat_version,
72 })?;
73
74 Ok(contract_addr)
75}
76
77pub fn create_croncat_task_submessage(
82 querier: &QuerierWrapper,
83 info: MessageInfo,
84 croncat_factory_address: Addr,
85 task: TaskRequest,
86 reply_type: Option<CronCatTaskSubmessageParams>,
87) -> Result<SubMsg, CronCatContractError> {
88 croncat_basic_validation(&info)?;
89 let wasm_exec_msg =
90 create_croncat_task_cosmos_msg(querier, info, croncat_factory_address, task)?;
91
92 let (reply_id, sub_reply_type) = match reply_type {
94 None => (REPLY_CRONCAT_TASK_CREATION, SubMessageReplyType::Always),
95 Some(params) => (
96 params.reply_id.unwrap_or(REPLY_CRONCAT_TASK_CREATION),
97 params.reply_type.unwrap_or(SubMessageReplyType::Always),
98 ),
99 };
100
101 let sub_message = match sub_reply_type {
102 SubMessageReplyType::Always => SubMsg::reply_always(wasm_exec_msg, reply_id),
103 SubMessageReplyType::OnError => SubMsg::reply_on_error(wasm_exec_msg, reply_id),
104 SubMessageReplyType::OnSuccess => SubMsg::reply_on_success(wasm_exec_msg, reply_id),
105 };
106
107 Ok(sub_message)
108}
109
110pub fn create_croncat_task_message(
114 querier: &QuerierWrapper,
115 info: MessageInfo,
116 croncat_factory_address: Addr,
117 task: TaskRequest,
118) -> Result<CosmosMsg, CronCatContractError> {
119 croncat_basic_validation(&info)?;
120 let wasm_exec_msg =
121 create_croncat_task_cosmos_msg(querier, info, croncat_factory_address, task)?;
122
123 Ok(wasm_exec_msg)
124}
125
126pub fn create_croncat_task_cosmos_msg(
130 querier: &QuerierWrapper,
131 info: MessageInfo,
132 croncat_factory_address: Addr,
133 task: TaskRequest,
134) -> Result<CosmosMsg, CronCatContractError> {
135 let tasks_addr =
136 get_latest_croncat_contract(querier, croncat_factory_address, TASKS_NAME.to_string())?;
137
138 Ok(Wasm(Execute {
139 contract_addr: String::from(tasks_addr),
140 msg: to_binary(&CreateTask {
141 task: Box::new(task),
142 })?,
143 funds: info.funds,
144 }))
145}
146
147pub fn croncat_basic_validation(info: &MessageInfo) -> Result<(), CronCatContractError> {
148 if info.funds.is_empty() {
154 return Err(CronCatContractError::TaskCreationNoFunds {});
155 }
156
157 Ok(())
158}
159
160pub fn simulate_task(
163 task_request: TaskRequest,
164 tasks_config: TasksConfig,
165 manager_config: ManagerConfig,
166 owner_addr: Addr,
167) -> Task {
168 let amount_for_one_task =
169 simulate_amount_for_one_task(&tasks_config, &manager_config, &task_request);
170 let boundary = boundary_mock_validate(task_request.boundary, &task_request.interval);
171
172 Task {
173 owner_addr,
174 interval: task_request.interval,
175 boundary,
176 stop_on_fail: task_request.stop_on_fail,
177 amount_for_one_task,
178 actions: task_request.actions,
179 queries: task_request.queries.unwrap_or_default(),
181 transforms: task_request.transforms.unwrap_or_default(),
182 version: tasks_config.version,
183 }
184}
185
186pub fn simulate_amount_for_one_task(
187 tasks_config: &TasksConfig,
188 manager_config: &ManagerConfig,
189 task_request: &TaskRequest,
190) -> AmountForOneTask {
191 let mut amount_for_one_task = AmountForOneTask {
192 cw20: None,
193 coin: [None, None],
194 gas: tasks_config.gas_base_fee,
195 agent_fee: manager_config.agent_fee,
196 treasury_fee: manager_config.treasury_fee,
197 gas_price: manager_config.gas_price.clone(),
198 };
199 for action in task_request.actions.iter() {
200 amount_for_one_task.add_gas(action.gas_limit.unwrap_or(tasks_config.gas_action_fee));
201
202 match &action.msg {
203 CosmosMsg::Wasm(WasmMsg::Execute {
204 contract_addr,
205 funds,
206 msg,
207 }) => {
208 for coin in funds {
209 if coin.amount.is_zero() || !amount_for_one_task.add_coin(coin.clone()).unwrap()
210 {
211 }
213 }
214 if let Ok(cw20_msg) = cosmwasm_std::from_binary(msg) {
215 match cw20_msg {
216 Cw20ExecuteMsg::Send { amount, .. } if !amount.is_zero() => {
217 amount_for_one_task
218 .add_cw20(Cw20CoinVerified {
219 address: Addr::unchecked(contract_addr),
220 amount,
221 })
222 .unwrap();
223 }
224 Cw20ExecuteMsg::Transfer { amount, .. } if !amount.is_zero() => {
225 amount_for_one_task
226 .add_cw20(Cw20CoinVerified {
227 address: Addr::unchecked(contract_addr),
228 amount,
229 })
230 .unwrap();
231 }
232 _ => {}
233 }
234 }
235 }
236 CosmosMsg::Bank(BankMsg::Send {
237 to_address: _,
238 amount,
239 }) => {
240 for coin in amount {
241 amount_for_one_task.add_coin(coin.clone()).unwrap();
242 }
243 }
244 _ => {}
245 }
246 }
247
248 if let Some(queries) = &task_request.queries {
249 amount_for_one_task.add_gas(queries.len() as u64 * tasks_config.gas_query_fee)
250 }
251 amount_for_one_task
252}
253
254fn boundary_mock_validate(boundary: Option<Boundary>, interval: &Interval) -> Boundary {
255 match (interval, boundary) {
256 (
257 Interval::Cron(_) | Interval::Block(_) | Interval::Once,
258 Some(Boundary::Time(boundary_time)),
259 ) => Boundary::Time(boundary_time),
260 (_, Some(Boundary::Height(boundary_height))) => Boundary::Height(boundary_height),
261 (Interval::Cron(_), None) => Boundary::Time(BoundaryTime {
262 start: None,
263 end: None,
264 }),
265 (_, None) => Boundary::Height(BoundaryHeight {
266 start: None,
267 end: None,
268 }),
269 _ => unimplemented!(),
270 }
271}